Compare commits

..

581 Commits

Author SHA1 Message Date
Matthias
157cb7d982 Merge pull request #10725 from freqtrade/new_release
New release 2024.9
2024-09-30 11:25:43 +02:00
Matthias
27af9455f5 chore: bump version to 2024.9 2024-09-29 19:53:20 +02:00
Matthias
98ff572afe Merge branch 'stable' into new_release 2024-09-29 19:53:03 +02:00
Matthias
415b8354f4 fix: if coingecko when no pair returned
fails to return valid pairs, the pairlist should be empty
2024-09-28 19:45:01 +02:00
Matthias
51c596a21f chore: add test for "no pair from coingecko" case
this should return an empty list
2024-09-28 19:44:38 +02:00
Matthias
5816a594fd Merge pull request #10671 from jakubikan/category-for-market-cap-pairlist
Category for market cap pairlist
2024-09-28 11:51:42 +02:00
Matthias
f4d76aa360 chore: improved wording 2024-09-28 10:18:59 +02:00
Matthias
56835f5f09 chore: manually check for symlink 2024-09-28 10:17:52 +02:00
Matthias
255ad7cac5 tests: test invalid category in list 2024-09-28 10:14:31 +02:00
Matthias
8c097a81ea tests: enhance test for marketcappairlist 2024-09-28 10:10:07 +02:00
Matthias
3dc92b42fe fix: Check if sub-directories are actually directories and fail otherwise.
This will explicitly fail if a file (or an invalid symlink) is present.
Freqtrade requires these files to be valid files - so failing here is correct behavior.

closes #10720
2024-09-28 09:54:49 +02:00
Jakub W.
1ed5a37280 Update freqtrade/plugins/pairlist/IPairList.py
Co-authored-by: Matthias <xmatthias@outlook.com>
2024-09-26 23:38:17 +02:00
Matthias
cb36f2844e chore: Improve "wrong category" error. 2024-09-26 20:21:27 +02:00
Matthias
7b93b55b78 docs: rephrase categories docs and add performance warning 2024-09-26 20:07:41 +02:00
Matthias
6837196e44 fix: treat marketcap as optional parameter 2024-09-26 19:59:23 +02:00
Matthias
31680f3b59 chore: Improve UI wording 2024-09-26 19:31:43 +02:00
Matthias
91d9c9b4d5 Merge pull request #10711 from freqtrade/fix/pytorch-scaling
fix: Update BasePyTorchRegressor.py
2024-09-26 18:53:14 +02:00
Robert Caulk
d18d8cf0ea freqai_info -> ft_params 2024-09-26 17:54:14 +02:00
Robert Caulk
123909cdac fix: Update BasePyTorchRegressor.py 2024-09-26 16:31:43 +02:00
Matthias
f0eaccc6ac Merge pull request #10709 from freqtrade/update/binance-leverage-tiers
Update Binance Leverage Tiers
2024-09-26 06:20:14 +02:00
xmatthias
1d66ef2f2d chore: update pre-commit hooks 2024-09-26 03:17:36 +00:00
Jakub Werner (jakubikan)
8aefae3aff format 2024-09-25 21:22:40 +02:00
Jakub Werner (jakubikan)
514558796b double quotes 2024-09-25 21:21:56 +02:00
Jakub Werner (jakubikan)
b00ca54707 adding docu 2024-09-25 21:20:35 +02:00
Jakub Werner (jakubikan)
0dbe507b26 making list of categories available 2024-09-25 21:11:52 +02:00
Matthias
4b70bea21f chore: reset params for emulated call 2024-09-25 19:11:12 +02:00
Matthias
096a051b99 test: update test 2024-09-25 19:03:03 +02:00
Matthias
a3ca1ff1e9 fix: send acknoledged to bybit fetch_order calls 2024-09-25 19:02:32 +02:00
Matthias
28eabfe477 tests: update test for retryable okx behavior 2024-09-25 06:20:49 +02:00
Matthias
0a68b0515c chore: reduce retry count for stop orders 2024-09-24 20:20:33 +02:00
Matthias
566c0c8f72 refactor: split okx fetch stop fallback 2024-09-24 20:20:33 +02:00
Matthias
333f2cb472 fix: Improve error handling for OKX stop orders
closes #10704
2024-09-24 19:38:36 +02:00
Matthias
3d1acc65af tests: add test for #10704 2024-09-24 19:38:11 +02:00
Matthias
5907de90c1 Merge pull request #10687 from freqtrade/refactor/trade_init_0
Initialize Trade objects with amount=0
2024-09-24 06:30:24 +02:00
Matthias
25d8a9d1f8 Merge pull request #10701 from freqtrade/update/pre-commit-hooks
Update pre-commit hooks
2024-09-24 06:30:09 +02:00
xmatthias
b44e8199b5 chore: update pre-commit hooks 2024-09-24 03:06:51 +00:00
Matthias
91449d0c8b Merge pull request #10699 from freqtrade/dependabot/pip/develop/ccxt-4.4.6
chore(deps): bump ccxt from 4.4.5 to 4.4.6
2024-09-23 13:05:49 +02:00
dependabot[bot]
b228f177f3 chore(deps): bump ccxt from 4.4.5 to 4.4.6
Bumps [ccxt](https://github.com/ccxt/ccxt) from 4.4.5 to 4.4.6.
- [Release notes](https://github.com/ccxt/ccxt/releases)
- [Changelog](https://github.com/ccxt/ccxt/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ccxt/ccxt/compare/4.4.5...4.4.6)

---
updated-dependencies:
- dependency-name: ccxt
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-23 09:57:20 +00:00
Matthias
9a40a2d4f2 Merge pull request #10695 from freqtrade/dependabot/pip/develop/sqlalchemy-2.0.35
chore(deps): bump sqlalchemy from 2.0.34 to 2.0.35
2024-09-23 11:56:28 +02:00
Matthias
cdc3dabba1 Merge pull request #10693 from freqtrade/dependabot/pip/develop/fastapi-0.115.0
chore(deps): bump fastapi from 0.114.2 to 0.115.0
2024-09-23 11:30:26 +02:00
Matthias
40ba2cbe79 Merge pull request #10690 from freqtrade/dependabot/pip/develop/pymdown-extensions-10.10.1
chore(deps): bump pymdown-extensions from 10.9 to 10.10.1
2024-09-23 11:03:54 +02:00
Matthias
7ede8af193 Merge branch 'develop' into dependabot/pip/develop/sqlalchemy-2.0.35 2024-09-23 10:52:07 +02:00
Matthias
a835b8cc8f Merge pull request #10698 from freqtrade/dependabot/pip/develop/filelock-3.16.1
chore(deps): bump filelock from 3.16.0 to 3.16.1
2024-09-23 09:44:11 +02:00
Matthias
e54b47b857 Merge pull request #10697 from freqtrade/dependabot/pip/develop/python-telegram-bot-21.6
chore(deps): bump python-telegram-bot from 21.5 to 21.6
2024-09-23 09:28:28 +02:00
Matthias
c93c25829b Merge pull request #10696 from freqtrade/dependabot/pip/develop/ruff-0.6.7
chore(deps-dev): bump ruff from 0.6.5 to 0.6.7
2024-09-23 09:24:19 +02:00
dependabot[bot]
1cdf8b29a5 chore(deps): bump fastapi from 0.114.2 to 0.115.0
Bumps [fastapi](https://github.com/fastapi/fastapi) from 0.114.2 to 0.115.0.
- [Release notes](https://github.com/fastapi/fastapi/releases)
- [Commits](https://github.com/fastapi/fastapi/compare/0.114.2...0.115.0)

---
updated-dependencies:
- dependency-name: fastapi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-23 06:38:27 +00:00
Matthias
b6eacf0771 Merge pull request #10694 from freqtrade/dependabot/pip/develop/pandas-2.2.3
chore(deps): bump pandas from 2.2.2 to 2.2.3
2024-09-23 08:37:49 +02:00
Matthias
4ea23c1bd8 Merge pull request #10692 from freqtrade/dependabot/pip/develop/websockets-13.1
chore(deps): bump websockets from 13.0.1 to 13.1
2024-09-23 08:37:01 +02:00
Matthias
70398820c0 Merge pull request #10691 from freqtrade/dependabot/pip/develop/pydantic-2.9.2
chore(deps): bump pydantic from 2.9.1 to 2.9.2
2024-09-23 08:36:21 +02:00
dependabot[bot]
04abc4d12f chore(deps): bump pymdown-extensions from 10.9 to 10.10.1
Bumps [pymdown-extensions](https://github.com/facelessuser/pymdown-extensions) from 10.9 to 10.10.1.
- [Release notes](https://github.com/facelessuser/pymdown-extensions/releases)
- [Commits](https://github.com/facelessuser/pymdown-extensions/compare/10.9...10.10.1)

---
updated-dependencies:
- dependency-name: pymdown-extensions
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-23 06:24:37 +00:00
Matthias
8491f46045 Merge pull request #10689 from freqtrade/dependabot/pip/develop/mkdocs-73612938eb
chore(deps): bump mkdocs-material from 9.5.34 to 9.5.36 in the mkdocs group
2024-09-23 08:23:40 +02:00
Matthias
4aa909e587 Merge pull request #10688 from freqtrade/dependabot/github_actions/develop/pypa/gh-action-pypi-publish-1.10.2
chore(deps): bump pypa/gh-action-pypi-publish from 1.10.1 to 1.10.2
2024-09-23 08:23:22 +02:00
Matthias
9f5e4b5812 chore: update sqlalchemy in pre-commit config 2024-09-23 06:34:12 +02:00
dependabot[bot]
06eb5abf11 chore(deps): bump filelock from 3.16.0 to 3.16.1
Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.16.0 to 3.16.1.
- [Release notes](https://github.com/tox-dev/py-filelock/releases)
- [Changelog](https://github.com/tox-dev/filelock/blob/main/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/py-filelock/compare/3.16.0...3.16.1)

---
updated-dependencies:
- dependency-name: filelock
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-23 03:48:44 +00:00
dependabot[bot]
29e6e3b374 chore(deps): bump python-telegram-bot from 21.5 to 21.6
Bumps [python-telegram-bot](https://github.com/python-telegram-bot/python-telegram-bot) from 21.5 to 21.6.
- [Release notes](https://github.com/python-telegram-bot/python-telegram-bot/releases)
- [Changelog](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/CHANGES.rst)
- [Commits](https://github.com/python-telegram-bot/python-telegram-bot/compare/v21.5...v21.6)

---
updated-dependencies:
- dependency-name: python-telegram-bot
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-23 03:48:38 +00:00
dependabot[bot]
2fc97f83f4 chore(deps-dev): bump ruff from 0.6.5 to 0.6.7
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.6.5 to 0.6.7.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/0.6.5...0.6.7)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-23 03:48:26 +00:00
dependabot[bot]
1e761b4c7d chore(deps): bump sqlalchemy from 2.0.34 to 2.0.35
Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 2.0.34 to 2.0.35.
- [Release notes](https://github.com/sqlalchemy/sqlalchemy/releases)
- [Changelog](https://github.com/sqlalchemy/sqlalchemy/blob/main/CHANGES.rst)
- [Commits](https://github.com/sqlalchemy/sqlalchemy/commits)

---
updated-dependencies:
- dependency-name: sqlalchemy
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-23 03:48:12 +00:00
dependabot[bot]
a2ca136f1f chore(deps): bump pandas from 2.2.2 to 2.2.3
Bumps [pandas](https://github.com/pandas-dev/pandas) from 2.2.2 to 2.2.3.
- [Release notes](https://github.com/pandas-dev/pandas/releases)
- [Commits](https://github.com/pandas-dev/pandas/compare/v2.2.2...v2.2.3)

---
updated-dependencies:
- dependency-name: pandas
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-23 03:47:58 +00:00
dependabot[bot]
94322664f2 chore(deps): bump websockets from 13.0.1 to 13.1
Bumps [websockets](https://github.com/python-websockets/websockets) from 13.0.1 to 13.1.
- [Release notes](https://github.com/python-websockets/websockets/releases)
- [Commits](https://github.com/python-websockets/websockets/compare/13.0.1...13.1)

---
updated-dependencies:
- dependency-name: websockets
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-23 03:47:39 +00:00
dependabot[bot]
cbd5c6d3e9 chore(deps): bump pydantic from 2.9.1 to 2.9.2
Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.9.1 to 2.9.2.
- [Release notes](https://github.com/pydantic/pydantic/releases)
- [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md)
- [Commits](https://github.com/pydantic/pydantic/compare/v2.9.1...v2.9.2)

---
updated-dependencies:
- dependency-name: pydantic
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-23 03:47:32 +00:00
dependabot[bot]
0428dc8381 chore(deps): bump mkdocs-material in the mkdocs group
Bumps the mkdocs group with 1 update: [mkdocs-material](https://github.com/squidfunk/mkdocs-material).


Updates `mkdocs-material` from 9.5.34 to 9.5.36
- [Release notes](https://github.com/squidfunk/mkdocs-material/releases)
- [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG)
- [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.34...9.5.36)

---
updated-dependencies:
- dependency-name: mkdocs-material
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: mkdocs
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-23 03:47:22 +00:00
dependabot[bot]
01e7b0da46 chore(deps): bump pypa/gh-action-pypi-publish from 1.10.1 to 1.10.2
Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.10.1 to 1.10.2.
- [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases)
- [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/v1.10.1...v1.10.2)

---
updated-dependencies:
- dependency-name: pypa/gh-action-pypi-publish
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-23 03:22:42 +00:00
Matthias
b37dadcc05 tests: dry-wallets test update 2024-09-22 13:22:37 +02:00
Matthias
8e6151fe65 fix: properly consider open order values as "tied up" stake. 2024-09-22 13:17:27 +02:00
Matthias
9b346c0937 docs: add hint about amount being 0 2024-09-22 09:20:49 +02:00
Matthias
b09f80ca30 tests: improve create_trade test 2024-09-22 08:47:52 +02:00
Matthias
0e0af82290 fix: odd calculation in calc_profit_ratio 2024-09-22 08:47:52 +02:00
Matthias
c69b09cbff tests: fix amount=0 test 2024-09-22 08:47:52 +02:00
Matthias
b8ba6cd970 tests: update rpc_status 2024-09-22 08:47:52 +02:00
Matthias
004e30d6be tests: update force_sell test to not use empty amount column 2024-09-22 08:47:52 +02:00
Matthias
b084efdd06 feat: initialize trade objects with 0 amount
This way, it'll represent the owned amount
which will be updated once the order fills
2024-09-22 08:47:52 +02:00
Matthias
d377d8462f fix: improve resiliance of order parsing
closes #10676
2024-09-22 08:28:29 +02:00
Matthias
2bbec9f9b1 tests: fix random test failure by reading time only once 2024-09-21 16:59:02 +02:00
Matthias
b326908487 tests: Improve test resiliance 2024-09-21 16:40:00 +02:00
Matthias
b21156a886 tests: improve stoploss test 2024-09-21 16:34:58 +02:00
Matthias
85138b0bc8 tests: Have exchange test get_historic_ohlcv properly 2024-09-21 09:06:26 +02:00
Matthias
e0df0257d1 tests: Update history tests for new response 2024-09-20 07:26:45 +02:00
Matthias
d23c1e8f92 refactor: Move dataframe parsing into get_historic_ohlcv 2024-09-20 07:23:52 +02:00
Matthias
670a40e67b chore: remove no longer valid todo 2024-09-20 07:06:21 +02:00
Matthias
3bbc6cbab1 chore: bump ccxt to 4.4.5
closes #10677
2024-09-19 20:41:32 +02:00
Matthias
15de53a22d chore: bump ccxt to 4.4.5
closes #10677
2024-09-19 20:36:56 +02:00
Matthias
59a44d6973 Merge pull request #10679 from freqtrade/update/binance-leverage-tiers
Update Binance Leverage Tiers
2024-09-19 06:29:02 +02:00
xmatthias
7561692352 chore: update pre-commit hooks 2024-09-19 03:15:32 +00:00
Matthias
f50a633f87 docs: order table formatting 2024-09-18 07:11:12 +02:00
Jakub Werner (jakubikan)
50f07e7b11 only doing this if the category is set 2024-09-17 23:03:51 +02:00
Jakub Werner (jakubikan)
660623181a adding category list if the category is not from the category 2024-09-17 22:36:21 +02:00
Jakub Werner (jakubikan)
03ee3aaf40 adding category list if the category is not from the category 2024-09-17 22:35:00 +02:00
Matthias
ad295946c0 fix: use precise calculation for decrease adjustment calculations 2024-09-17 20:19:22 +02:00
Matthias
c28446dad0 Merge pull request #10672 from freqtrade/update/pre-commit-hooks
Update pre-commit hooks
2024-09-17 06:28:16 +02:00
xmatthias
ff9d1f2728 chore: update pre-commit hooks 2024-09-17 03:03:39 +00:00
Jakub Werner (jakubikan)
0b7cb2a1a8 cleanup 2024-09-16 22:52:26 +02:00
Jakub Werner (jakubikan)
92af01b0cb adding category for MarketCapPairList.py 2024-09-16 22:51:42 +02:00
Jakub Werner (jakubikan)
dc26d0d7ba adding category for MarketCapPairList.py 2024-09-16 22:50:08 +02:00
Matthias
2fe67edab3 chore: update link to okx liquidation formula 2024-09-16 19:05:00 +02:00
Matthias
167e43cbef Merge pull request #10655 from freqtrade/dependabot/pip/develop/types-dda07fe7e8
chore(deps-dev): bump types-requests from 2.32.0.20240907 to 2.32.0.20240914 in the types group
2024-09-16 11:12:07 +02:00
Matthias
d8cb407c25 Merge pull request #10666 from freqtrade/dependabot/pip/develop/pydantic-2.9.1
chore(deps): bump pydantic from 2.9.0 to 2.9.1
2024-09-16 10:56:51 +02:00
Matthias
9452afe3f7 Merge pull request #10660 from freqtrade/dependabot/pip/develop/ruff-0.6.5
chore(deps-dev): bump ruff from 0.6.4 to 0.6.5
2024-09-16 10:21:12 +02:00
Matthias
8dc6d9ce7d Merge pull request #10665 from freqtrade/dependabot/pip/develop/rich-13.8.1
chore(deps): bump rich from 13.8.0 to 13.8.1
2024-09-16 09:45:01 +02:00
Matthias
bf4b8a318d Merge pull request #10661 from freqtrade/dependabot/pip/develop/scikit-learn-1.5.2
chore(deps): bump scikit-learn from 1.5.1 to 1.5.2
2024-09-16 09:44:31 +02:00
Matthias
20f6022050 Merge pull request #10663 from freqtrade/dependabot/pip/develop/ccxt-4.4.3
chore(deps): bump ccxt from 4.3.98 to 4.4.3
2024-09-16 09:33:16 +02:00
dependabot[bot]
65e6c737cd chore(deps): bump pydantic from 2.9.0 to 2.9.1
Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.9.0 to 2.9.1.
- [Release notes](https://github.com/pydantic/pydantic/releases)
- [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md)
- [Commits](https://github.com/pydantic/pydantic/compare/v2.9.0...v2.9.1)

---
updated-dependencies:
- dependency-name: pydantic
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-16 07:07:06 +00:00
Matthias
6d2572e347 Merge pull request #10662 from freqtrade/dependabot/pip/develop/fastapi-0.114.2
chore(deps): bump fastapi from 0.114.0 to 0.114.2
2024-09-16 09:06:12 +02:00
Matthias
52a35197c7 Merge pull request #10659 from freqtrade/dependabot/pip/develop/pytz-2024.2
chore(deps): bump pytz from 2024.1 to 2024.2
2024-09-16 08:33:08 +02:00
Matthias
2b3a41db3e Merge pull request #10658 from freqtrade/dependabot/pip/develop/urllib3-2.2.3
chore(deps): bump urllib3 from 2.2.2 to 2.2.3
2024-09-16 08:32:52 +02:00
dependabot[bot]
09c1459411 chore(deps-dev): bump ruff from 0.6.4 to 0.6.5
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.6.4 to 0.6.5.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/0.6.4...0.6.5)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-16 06:17:40 +00:00
Matthias
98f18b89da Merge pull request #10657 from freqtrade/dependabot/pip/develop/plotly-5.24.1
chore(deps): bump plotly from 5.24.0 to 5.24.1
2024-09-16 08:16:56 +02:00
Matthias
4249db4330 Merge pull request #10656 from freqtrade/dependabot/pip/develop/pytest-baa91b6655
chore(deps-dev): bump pytest from 8.3.2 to 8.3.3 in the pytest group
2024-09-16 08:16:43 +02:00
Matthias
a7f46500ed chore: bump types-requests in pre-commit 2024-09-16 06:38:45 +02:00
dependabot[bot]
c73fa2b0eb chore(deps): bump rich from 13.8.0 to 13.8.1
Bumps [rich](https://github.com/Textualize/rich) from 13.8.0 to 13.8.1.
- [Release notes](https://github.com/Textualize/rich/releases)
- [Changelog](https://github.com/Textualize/rich/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Textualize/rich/compare/v13.8.0...v13.8.1)

---
updated-dependencies:
- dependency-name: rich
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-16 03:18:09 +00:00
dependabot[bot]
db4c4b971a chore(deps): bump ccxt from 4.3.98 to 4.4.3
Bumps [ccxt](https://github.com/ccxt/ccxt) from 4.3.98 to 4.4.3.
- [Release notes](https://github.com/ccxt/ccxt/releases)
- [Changelog](https://github.com/ccxt/ccxt/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ccxt/ccxt/compare/4.3.98...4.4.3)

---
updated-dependencies:
- dependency-name: ccxt
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-16 03:17:58 +00:00
dependabot[bot]
e9ccc98ada chore(deps): bump fastapi from 0.114.0 to 0.114.2
Bumps [fastapi](https://github.com/fastapi/fastapi) from 0.114.0 to 0.114.2.
- [Release notes](https://github.com/fastapi/fastapi/releases)
- [Commits](https://github.com/fastapi/fastapi/compare/0.114.0...0.114.2)

---
updated-dependencies:
- dependency-name: fastapi
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-16 03:17:46 +00:00
dependabot[bot]
11d6ec33b3 chore(deps): bump scikit-learn from 1.5.1 to 1.5.2
Bumps [scikit-learn](https://github.com/scikit-learn/scikit-learn) from 1.5.1 to 1.5.2.
- [Release notes](https://github.com/scikit-learn/scikit-learn/releases)
- [Commits](https://github.com/scikit-learn/scikit-learn/compare/1.5.1...1.5.2)

---
updated-dependencies:
- dependency-name: scikit-learn
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-16 03:17:39 +00:00
dependabot[bot]
c3b6f4ca85 chore(deps): bump pytz from 2024.1 to 2024.2
Bumps [pytz](https://github.com/stub42/pytz) from 2024.1 to 2024.2.
- [Release notes](https://github.com/stub42/pytz/releases)
- [Commits](https://github.com/stub42/pytz/compare/release_2024.1...release_2024.2)

---
updated-dependencies:
- dependency-name: pytz
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-16 03:17:21 +00:00
dependabot[bot]
d37405a307 chore(deps): bump urllib3 from 2.2.2 to 2.2.3
Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.2.2 to 2.2.3.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/2.2.2...2.2.3)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-16 03:17:05 +00:00
dependabot[bot]
d7a9841328 chore(deps): bump plotly from 5.24.0 to 5.24.1
Bumps [plotly](https://github.com/plotly/plotly.py) from 5.24.0 to 5.24.1.
- [Release notes](https://github.com/plotly/plotly.py/releases)
- [Changelog](https://github.com/plotly/plotly.py/blob/master/CHANGELOG.md)
- [Commits](https://github.com/plotly/plotly.py/compare/v5.24.0...v5.24.1)

---
updated-dependencies:
- dependency-name: plotly
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-16 03:17:01 +00:00
dependabot[bot]
cf3af42477 chore(deps-dev): bump pytest from 8.3.2 to 8.3.3 in the pytest group
Bumps the pytest group with 1 update: [pytest](https://github.com/pytest-dev/pytest).


Updates `pytest` from 8.3.2 to 8.3.3
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/8.3.2...8.3.3)

---
updated-dependencies:
- dependency-name: pytest
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: pytest
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-16 03:16:39 +00:00
dependabot[bot]
ad8e6e7d67 chore(deps-dev): bump types-requests in the types group
Bumps the types group with 1 update: [types-requests](https://github.com/python/typeshed).


Updates `types-requests` from 2.32.0.20240907 to 2.32.0.20240914
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-requests
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: types
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-16 03:15:58 +00:00
Matthias
ae41ab101a docs: remove skip_pair_validation - it's no longer used. 2024-09-15 11:28:57 +02:00
Matthias
f4881e7c6f tests: Adjust tests for removed validate_pairlist functionality 2024-09-15 11:28:57 +02:00
Matthias
94ef4380d4 chore: remove validate_pairs from exchange class
Invalid pairs were filtered out before this was called in most cases.
in cases where it's not - regular pairlist-filtering provides proper warnings.
2024-09-15 11:28:57 +02:00
Matthias
7ebe1b8c14 chore: remove pointless validation
pairs are validated through expand_pairlist.
If they're not in markets, they'll no longer be in the
pairlist once this function function is hit.
2024-09-15 11:02:49 +02:00
Matthias
79020bba28 chore: Remove "prohibitedIn" check
it's only been used for bitrex, which does no longer exist.
apparently this was forgotten when decomissioning bittrex.
2024-09-15 10:49:26 +02:00
Matthias
95c250ebcc chore: add explaining comment 2024-09-15 10:37:28 +02:00
Matthias
bfb14614cc chore: enhance change with comment 2024-09-15 09:48:44 +02:00
Matthias
12299d4810 feat: staticPairlist to warn for invalid pairs
Warnings about invalid pairs were "covered" by the implicit
filtering of `expand_pairlist()`
2024-09-15 09:46:47 +02:00
Matthias
c67a9d4e84 docs: update pairlist creation docs 2024-09-15 09:29:45 +02:00
Matthias
af422c7cd4 Merge pull request #10645 from dxbstyle/develop
added check for Kraken exchange
2024-09-14 10:44:23 +02:00
Matthias
51bdecea53 Improve check to cover more potential api oddities 2024-09-14 10:09:15 +02:00
Matthias
0f505c6d7b Improve check to cover more potential api oddities 2024-09-14 10:04:28 +02:00
Matthias
ae72f10448 Merge pull request #10619 from KingND/pixel/feat/freqai_labels_are_okay_in_lookahead_analysis
feat: include lookahead-analysis table caption when biased_indicator is likely from FreqAI target
2024-09-14 09:51:38 +02:00
Matthias
9f34153c84 chore: update typing for reload function 2024-09-13 19:45:30 +02:00
Matthias
c04cf6c5cb test: Improve test coverage of retry/fail logic 2024-09-13 07:24:08 +02:00
Matthias
5112736385 feat: Simplify reload_markets logic 2024-09-13 07:24:08 +02:00
Matthias
11eaa6d77c test: Add tests for new behavior 2024-09-13 07:24:08 +02:00
Matthias
6024903bde feat: conditionally apply retrier to market-reload
closes #10641
2024-09-13 07:23:58 +02:00
Matthias
e96928588e Merge pull request #10639 from jainanuj94/backtesting
Add entry-only and exit-only filters to --indicator-list in backtesting analysis
2024-09-12 06:43:24 +02:00
Matthias
94e38d4cdd Merge pull request #10646 from freqtrade/update/binance-leverage-tiers
Update Binance Leverage Tiers
2024-09-12 06:36:11 +02:00
xmatthias
d15921b3f2 chore: update pre-commit hooks 2024-09-12 03:12:54 +00:00
Matthias
3c6e2b89a4 Merge pull request #10640 from TheJoeSchr/fix/plot-orderflow-data-missing
fix: orderflow data was missing for plotting
2024-09-11 19:11:55 +02:00
Joe Schr
439658fcf1 fix: remove tests for orderflow data missing of other runmodes 2024-09-11 17:12:35 +02:00
Joe Schr
c9acb1466c fix: orderflow data missing for plotting and other runmodes 2024-09-11 17:12:35 +02:00
Anuj Jain
addd27faf8 Update tests and docs 2024-09-11 15:33:26 +05:30
Matthias
5605bdc7a3 Merge pull request #10644 from iridescentGray/develop
chore: remove redundant method
2024-09-11 06:40:53 +02:00
colorfulgray0
f1df7e9bdc chore: remove redundant method 2024-09-11 11:33:38 +08:00
Anuj Jain
4765656f87 Add filter for entry and exit only parameter 2024-09-10 15:21:56 +05:30
Matthias
c3a00b93c2 Merge pull request #10637 from freqtrade/dependabot/docker/docker/python-3.12.6-slim-bookworm
chore(deps): bump python from 3.11.9-slim-bookworm to 3.11.10-slim-bookworm in /docker
2024-09-10 07:20:36 +02:00
Matthias
e5d2ba7835 Merge pull request #10636 from freqtrade/dependabot/docker/python-3.12.6-slim-bookworm
chore(deps): bump python from 3.12.5-slim-bookworm to 3.12.6-slim-bookworm
2024-09-10 06:53:17 +02:00
Matthias
8c1b119e84 chore: rpi image should remain on 3.11 series 2024-09-10 06:25:17 +02:00
Matthias
593a54e6cb Merge pull request #10635 from freqtrade/update/pre-commit-hooks
Update pre-commit hooks
2024-09-10 06:23:20 +02:00
dependabot[bot]
95fa7083a9 chore(deps): bump python in /docker
Bumps python from 3.11.9-slim-bookworm to 3.12.6-slim-bookworm.

---
updated-dependencies:
- dependency-name: python
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-10 04:07:33 +00:00
dependabot[bot]
98e08df807 chore(deps): bump python
Bumps python from 3.12.5-slim-bookworm to 3.12.6-slim-bookworm.

---
updated-dependencies:
- dependency-name: python
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-10 04:07:26 +00:00
xmatthias
01da36f984 chore: update pre-commit hooks 2024-09-10 03:03:23 +00:00
dxbstyle
ae155c78c2 added check 2024-09-09 21:29:49 +02:00
Matthias
9742216479 chore: run ruff-format on pre-commit 2024-09-09 18:23:07 +02:00
Matthias
f720183281 Merge pull request #10630 from freqtrade/dependabot/pip/develop/sqlalchemy-2.0.34
chore(deps): bump sqlalchemy from 2.0.32 to 2.0.34
2024-09-09 14:54:33 +02:00
Matthias
4bc84acac6 Merge branch 'develop' into dependabot/pip/develop/sqlalchemy-2.0.34 2024-09-09 13:37:43 +02:00
Matthias
14a8086677 Merge pull request #10631 from freqtrade/dependabot/pip/develop/fastapi-0.114.0
chore(deps): bump fastapi from 0.112.2 to 0.114.0
2024-09-09 13:37:09 +02:00
Matthias
686b96222e Merge pull request #10621 from freqtrade/dependabot/pip/develop/types-a674cee9e2
chore(deps-dev): bump the types group with 2 updates
2024-09-09 10:57:28 +02:00
Matthias
0962f37f55 Merge pull request #10632 from freqtrade/dependabot/github_actions/develop/peter-evans/create-pull-request-7
chore(deps): bump peter-evans/create-pull-request from 6 to 7
2024-09-09 10:43:52 +02:00
Matthias
2c17551b27 chore: bump sqlalchemy in mypy additional deps 2024-09-09 10:15:18 +02:00
Matthias
3f5a5e35c2 Merge pull request #10633 from freqtrade/dependabot/github_actions/develop/pypa/gh-action-pypi-publish-1.10.1
chore(deps): bump pypa/gh-action-pypi-publish from 1.10.0 to 1.10.1
2024-09-09 09:56:28 +02:00
Matthias
07c6d37ff0 Merge pull request #10628 from freqtrade/dependabot/pip/develop/ccxt-4.3.98
chore(deps): bump ccxt from 4.3.93 to 4.3.98
2024-09-09 09:12:56 +02:00
Matthias
916ea7acc0 Merge pull request #10627 from freqtrade/dependabot/pip/develop/ruff-0.6.4
chore(deps-dev): bump ruff from 0.6.3 to 0.6.4
2024-09-09 09:12:28 +02:00
Matthias
eae7e865a5 Merge pull request #10626 from freqtrade/dependabot/pip/develop/filelock-3.16.0
chore(deps): bump filelock from 3.15.4 to 3.16.0
2024-09-09 09:10:55 +02:00
Matthias
776e5054aa Merge pull request #10624 from freqtrade/dependabot/pip/develop/torch-2.4.1
chore(deps): bump torch from 2.2.2 to 2.4.1
2024-09-09 09:10:27 +02:00
Matthias
bf2b8b280e Merge pull request #10623 from freqtrade/dependabot/pip/develop/catboost-1.2.7
chore(deps): bump catboost from 1.2.5 to 1.2.7
2024-09-09 09:10:15 +02:00
dependabot[bot]
7aa7027a34 chore(deps): bump fastapi from 0.112.2 to 0.114.0
Bumps [fastapi](https://github.com/fastapi/fastapi) from 0.112.2 to 0.114.0.
- [Release notes](https://github.com/fastapi/fastapi/releases)
- [Commits](https://github.com/fastapi/fastapi/compare/0.112.2...0.114.0)

---
updated-dependencies:
- dependency-name: fastapi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-09 06:55:07 +00:00
Matthias
d4713b2091 Merge pull request #10622 from freqtrade/dependabot/pip/develop/pydantic-2.9.0
chore(deps): bump pydantic from 2.8.2 to 2.9.0
2024-09-09 08:54:14 +02:00
Matthias
9b97be4aa4 Bump pre-commit dependencies 2024-09-09 06:44:35 +02:00
dependabot[bot]
621be11395 chore(deps): bump pypa/gh-action-pypi-publish from 1.10.0 to 1.10.1
Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.10.0 to 1.10.1.
- [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases)
- [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/v1.10.0...v1.10.1)

---
updated-dependencies:
- dependency-name: pypa/gh-action-pypi-publish
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-09 03:59:34 +00:00
dependabot[bot]
05af6df536 chore(deps): bump peter-evans/create-pull-request from 6 to 7
Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 6 to 7.
- [Release notes](https://github.com/peter-evans/create-pull-request/releases)
- [Commits](https://github.com/peter-evans/create-pull-request/compare/v6...v7)

---
updated-dependencies:
- dependency-name: peter-evans/create-pull-request
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-09 03:59:29 +00:00
dependabot[bot]
b7bda2355d chore(deps): bump sqlalchemy from 2.0.32 to 2.0.34
Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 2.0.32 to 2.0.34.
- [Release notes](https://github.com/sqlalchemy/sqlalchemy/releases)
- [Changelog](https://github.com/sqlalchemy/sqlalchemy/blob/main/CHANGES.rst)
- [Commits](https://github.com/sqlalchemy/sqlalchemy/commits)

---
updated-dependencies:
- dependency-name: sqlalchemy
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-09 03:46:24 +00:00
dependabot[bot]
b0976031ae chore(deps): bump ccxt from 4.3.93 to 4.3.98
Bumps [ccxt](https://github.com/ccxt/ccxt) from 4.3.93 to 4.3.98.
- [Release notes](https://github.com/ccxt/ccxt/releases)
- [Changelog](https://github.com/ccxt/ccxt/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ccxt/ccxt/compare/4.3.93...4.3.98)

---
updated-dependencies:
- dependency-name: ccxt
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-09 03:46:05 +00:00
dependabot[bot]
d099f30a34 chore(deps-dev): bump ruff from 0.6.3 to 0.6.4
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.6.3 to 0.6.4.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/0.6.3...0.6.4)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-09 03:45:51 +00:00
dependabot[bot]
699be03bb7 chore(deps): bump filelock from 3.15.4 to 3.16.0
Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.15.4 to 3.16.0.
- [Release notes](https://github.com/tox-dev/py-filelock/releases)
- [Changelog](https://github.com/tox-dev/filelock/blob/main/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/py-filelock/compare/3.15.4...3.16.0)

---
updated-dependencies:
- dependency-name: filelock
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-09 03:45:29 +00:00
dependabot[bot]
ccf93cfdcd chore(deps): bump torch from 2.2.2 to 2.4.1
Bumps [torch](https://github.com/pytorch/pytorch) from 2.2.2 to 2.4.1.
- [Release notes](https://github.com/pytorch/pytorch/releases)
- [Changelog](https://github.com/pytorch/pytorch/blob/main/RELEASE.md)
- [Commits](https://github.com/pytorch/pytorch/compare/v2.2.2...v2.4.1)

---
updated-dependencies:
- dependency-name: torch
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-09 03:45:14 +00:00
dependabot[bot]
47358a8229 chore(deps): bump catboost from 1.2.5 to 1.2.7
Bumps [catboost](https://github.com/catboost/catboost) from 1.2.5 to 1.2.7.
- [Release notes](https://github.com/catboost/catboost/releases)
- [Changelog](https://github.com/catboost/catboost/blob/master/RELEASE.md)
- [Commits](https://github.com/catboost/catboost/compare/v1.2.5...v1.2.7)

---
updated-dependencies:
- dependency-name: catboost
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-09 03:45:03 +00:00
dependabot[bot]
9856c2cfc4 chore(deps): bump pydantic from 2.8.2 to 2.9.0
Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.8.2 to 2.9.0.
- [Release notes](https://github.com/pydantic/pydantic/releases)
- [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md)
- [Commits](https://github.com/pydantic/pydantic/compare/v2.8.2...v2.9.0)

---
updated-dependencies:
- dependency-name: pydantic
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-09 03:44:57 +00:00
dependabot[bot]
df9669ba2c chore(deps-dev): bump the types group with 2 updates
Bumps the types group with 2 updates: [types-requests](https://github.com/python/typeshed) and [types-python-dateutil](https://github.com/python/typeshed).


Updates `types-requests` from 2.32.0.20240712 to 2.32.0.20240907
- [Commits](https://github.com/python/typeshed/commits)

Updates `types-python-dateutil` from 2.9.0.20240821 to 2.9.0.20240906
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-requests
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: types
- dependency-name: types-python-dateutil
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: types
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-09 03:43:50 +00:00
KingND
f970454cb4 chore: ruff format 2024-09-08 13:57:48 -04:00
KingND
69678574d4 fix: support python 3.9 union type hinting 2024-09-08 12:04:58 -04:00
KingND
53cab5074b chore: refactor and cleanup tests 2024-09-08 12:04:58 -04:00
KingND
c6c65b1799 chore: flake8 2024-09-08 12:04:58 -04:00
KingND
bb9f64027a chore: improve language in docs 2024-09-08 12:04:58 -04:00
KingND
5f52fc4338 feat: update lookahead-analysis doc caveats to include info regarding the false positive on FreqAI targets 2024-09-08 12:04:58 -04:00
KingND
82e30c8519 feat: if a biased_indicator starting with & appears in a lookahead-analysis, caption the table with a note that freqai targets appearing here can be ignored 2024-09-08 12:04:58 -04:00
Matthias
6e2aa6b4b8 tests: remove unused imports 2024-09-08 08:28:40 +02:00
Matthias
a1681cdd63 chore: improve typing 2024-09-08 08:28:40 +02:00
Matthias
611a3ce138 Merge pull request #10485 from jainanuj94/feature/8902
Add exit signals to export in backtesting
2024-09-07 20:12:05 +02:00
Matthias
396d933e34 feat(bybit): add support for unified Accounts 2024-09-07 18:28:56 +02:00
Matthias
0858e0a21e Minor update to docs 2024-09-07 15:29:23 +02:00
Matthias
704e32b0dc feat: properly parse marginmode on startup 2024-09-07 09:28:35 +02:00
Matthias
f95cc960e1 test: tests should consider additional ff-update call 2024-09-07 09:25:20 +02:00
Matthias
1b00f512c1 fix: call order_filled callback for left open trades 2024-09-07 09:24:21 +02:00
Matthias
d9ec66695c docs: update backtesting docs with new row 2024-09-07 08:49:30 +02:00
Matthias
1a2578a4b7 feat: Add margin/Trading mode output to bt-output 2024-09-07 08:47:45 +02:00
Matthias
f714e306da test: add margin and trading mode to test config 2024-09-06 21:15:10 +02:00
Matthias
6a4b641250 feat: implement __str__ for marign and tradingmode enums 2024-09-06 20:58:54 +02:00
Anuj Jain
8d96844312 use BT_DATA_COLUMNS for trade wide indicators 2024-09-06 12:28:02 +05:30
Anuj Jain
b7145debfb handle trade wide indicators 2024-09-05 21:52:09 +05:30
Matthias
990dbb6c06 Merge pull request #10616 from freqtrade/update/binance-leverage-tiers
Update Binance Leverage Tiers
2024-09-05 05:57:37 +02:00
xmatthias
c6a66a8fac chore: update pre-commit hooks 2024-09-05 03:12:48 +00:00
Matthias
65ba67dedc chore: re-add analytics. 2024-09-04 21:08:34 +02:00
Matthias
824db78234 chore: update site_url again 2024-09-04 21:07:16 +02:00
Matthias
2fdf108198 chore: update site_url to work correctly 2024-09-04 21:04:39 +02:00
Matthias
63092d7d1a chore: re-add analytics to docs page 2024-09-04 20:44:18 +02:00
Matthias
964d437c7a chore: type _ft_has 2024-09-04 07:15:17 +02:00
Matthias
d49c556291 chore: rename ft_has setting from ws.enabled to ws_enabled 2024-09-04 06:57:13 +02:00
Matthias
d6b2748293 chore: rename types to ft_types 2024-09-04 06:44:48 +02:00
Matthias
e3a5831d64 refactor: rename exchange.types 2024-09-04 06:42:51 +02:00
Anuj Jain
08d5174d02 update documentation and add default values 2024-09-04 09:56:12 +05:30
Matthias
dacb926db5 Merge pull request #10614 from freqtrade/dependabot/pip/cryptography-43.0.1
chore(deps): bump cryptography from 42.0.8 to 43.0.1
2024-09-04 06:11:59 +02:00
dependabot[bot]
c0e9173c9b chore(deps): bump cryptography from 42.0.8 to 43.0.1
Bumps [cryptography](https://github.com/pyca/cryptography) from 42.0.8 to 43.0.1.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/42.0.8...43.0.1)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-04 00:25:37 +00:00
Matthias
f46308bbdb Merge pull request #10612 from freqtrade/update/pre-commit-hooks
Update pre-commit hooks
2024-09-03 06:18:55 +02:00
xmatthias
331db99a4e chore: update pre-commit hooks 2024-09-03 03:02:47 +00:00
Matthias
d84f32f27d Merge pull request #10607 from freqtrade/dependabot/pip/develop/ccxt-4.3.93
chore(deps): bump ccxt from 4.3.88 to 4.3.93
2024-09-02 08:33:44 +02:00
Matthias
ac28a44b92 Merge pull request #10609 from freqtrade/dependabot/github_actions/develop/pypa/gh-action-pypi-publish-1.10.0
chore(deps): bump pypa/gh-action-pypi-publish from 1.9.0 to 1.10.0
2024-09-02 08:13:28 +02:00
Matthias
003a41b920 Merge pull request #10608 from freqtrade/dependabot/pip/develop/python-telegram-bot-21.5
chore(deps): bump python-telegram-bot from 21.4 to 21.5
2024-09-02 07:49:17 +02:00
dependabot[bot]
bc4c693525 chore(deps): bump ccxt from 4.3.88 to 4.3.93
Bumps [ccxt](https://github.com/ccxt/ccxt) from 4.3.88 to 4.3.93.
- [Release notes](https://github.com/ccxt/ccxt/releases)
- [Changelog](https://github.com/ccxt/ccxt/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ccxt/ccxt/compare/4.3.88...4.3.93)

---
updated-dependencies:
- dependency-name: ccxt
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-02 05:08:37 +00:00
Matthias
d4ba837641 chore: mark Bybit as supported exchange 2024-09-02 07:03:04 +02:00
Matthias
4a1592dd92 feat: initialize hyperliquid in spot mode by default 2024-09-02 07:03:04 +02:00
Matthias
ac145a0b65 Merge pull request #10606 from freqtrade/dependabot/pip/develop/websockets-13.0.1
chore(deps): bump websockets from 13.0 to 13.0.1
2024-09-02 06:58:04 +02:00
Matthias
50c00dcae6 Merge pull request #10605 from freqtrade/dependabot/pip/develop/plotly-5.24.0
chore(deps): bump plotly from 5.23.0 to 5.24.0
2024-09-02 06:57:47 +02:00
Matthias
95d964140b Merge pull request #10604 from freqtrade/dependabot/pip/develop/rich-13.8.0
chore(deps): bump rich from 13.7.1 to 13.8.0
2024-09-02 06:57:34 +02:00
Matthias
0a73a7eb52 Merge pull request #10603 from freqtrade/dependabot/pip/develop/ruff-0.6.3
chore(deps-dev): bump ruff from 0.6.2 to 0.6.3
2024-09-02 06:57:25 +02:00
Matthias
0aecb24930 Merge pull request #10602 from freqtrade/dependabot/pip/develop/mkdocs-4a19445461
chore(deps): bump the mkdocs group with 2 updates
2024-09-02 06:57:03 +02:00
dependabot[bot]
904f5303a6 chore(deps): bump pypa/gh-action-pypi-publish from 1.9.0 to 1.10.0
Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.9.0 to 1.10.0.
- [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases)
- [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/v1.9.0...v1.10.0)

---
updated-dependencies:
- dependency-name: pypa/gh-action-pypi-publish
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-02 03:05:58 +00:00
dependabot[bot]
585761e931 chore(deps): bump python-telegram-bot from 21.4 to 21.5
Bumps [python-telegram-bot](https://github.com/python-telegram-bot/python-telegram-bot) from 21.4 to 21.5.
- [Release notes](https://github.com/python-telegram-bot/python-telegram-bot/releases)
- [Changelog](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/CHANGES.rst)
- [Commits](https://github.com/python-telegram-bot/python-telegram-bot/compare/v21.4...v21.5)

---
updated-dependencies:
- dependency-name: python-telegram-bot
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-02 03:05:45 +00:00
dependabot[bot]
4d53797cba chore(deps): bump websockets from 13.0 to 13.0.1
Bumps [websockets](https://github.com/python-websockets/websockets) from 13.0 to 13.0.1.
- [Release notes](https://github.com/python-websockets/websockets/releases)
- [Commits](https://github.com/python-websockets/websockets/compare/13.0...13.0.1)

---
updated-dependencies:
- dependency-name: websockets
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-02 03:05:21 +00:00
dependabot[bot]
803677e884 chore(deps): bump plotly from 5.23.0 to 5.24.0
Bumps [plotly](https://github.com/plotly/plotly.py) from 5.23.0 to 5.24.0.
- [Release notes](https://github.com/plotly/plotly.py/releases)
- [Changelog](https://github.com/plotly/plotly.py/blob/master/CHANGELOG.md)
- [Commits](https://github.com/plotly/plotly.py/compare/v5.23.0...v5.24.0)

---
updated-dependencies:
- dependency-name: plotly
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-02 03:05:14 +00:00
dependabot[bot]
17617c58d7 chore(deps): bump rich from 13.7.1 to 13.8.0
Bumps [rich](https://github.com/Textualize/rich) from 13.7.1 to 13.8.0.
- [Release notes](https://github.com/Textualize/rich/releases)
- [Changelog](https://github.com/Textualize/rich/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Textualize/rich/compare/v13.7.1...v13.8.0)

---
updated-dependencies:
- dependency-name: rich
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-02 03:05:10 +00:00
dependabot[bot]
96d03ec13d chore(deps-dev): bump ruff from 0.6.2 to 0.6.3
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.6.2 to 0.6.3.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/0.6.2...0.6.3)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-02 03:05:06 +00:00
dependabot[bot]
4726afbebf chore(deps): bump the mkdocs group with 2 updates
Bumps the mkdocs group with 2 updates: [mkdocs](https://github.com/mkdocs/mkdocs) and [mkdocs-material](https://github.com/squidfunk/mkdocs-material).


Updates `mkdocs` from 1.6.0 to 1.6.1
- [Release notes](https://github.com/mkdocs/mkdocs/releases)
- [Commits](https://github.com/mkdocs/mkdocs/compare/1.6.0...1.6.1)

Updates `mkdocs-material` from 9.5.33 to 9.5.34
- [Release notes](https://github.com/squidfunk/mkdocs-material/releases)
- [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG)
- [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.33...9.5.34)

---
updated-dependencies:
- dependency-name: mkdocs
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: mkdocs
- dependency-name: mkdocs-material
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: mkdocs
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-02 03:04:55 +00:00
Matthias
97c937e554 chore: add Bugbear Ruff checking 2024-09-01 08:32:42 +02:00
Matthias
b25520cf18 chore: improve rhci_progress typing, remove mutable arguments 2024-09-01 08:27:53 +02:00
Matthias
c6b46d75cb chore: fix B018 violation 2024-09-01 08:24:47 +02:00
Matthias
a554352ae0 test: remove unused mutable argument 2024-09-01 08:24:21 +02:00
Matthias
a7fd03f1b7 chore: improve ccxt test 2024-09-01 08:22:58 +02:00
Matthias
ef96116c3f docs: add note about freqUI support of dp.current_whitelist
closes #10600
2024-08-31 20:34:02 +02:00
Matthias
01d10aebca Merge pull request #10599 from freqtrade/new_release
New release 2024.8
2024-08-31 16:03:49 +02:00
Matthias
7edc50865f docs: improve release documentation 2024-08-31 08:34:48 +02:00
Matthias
a881d3fd81 chore: bump version to 2024.9-dev 2024-08-31 08:31:42 +02:00
Matthias
5e9d2323e3 chore: bump version to 2024.8 2024-08-31 08:25:52 +02:00
Matthias
a98b5dd86e Merge branch 'stable' into new_release 2024-08-31 08:25:20 +02:00
Matthias
a250cf7ebe test: Remove unnecessary asyncio decorators 2024-08-29 20:38:57 +02:00
Matthias
1c5ca0f022 chore: improved fix for terminal error 2024-08-29 20:38:25 +02:00
Matthias
ca3dee7b37 chore: add setting to avoid deprecation warning from pytest-asyncio 2024-08-29 20:24:52 +02:00
Matthias
59d47955a0 chore: fix test failure due to terminal error 2024-08-29 20:05:48 +02:00
Matthias
d05ca3db0b fix: handle small terminal width
closes #10572
2024-08-29 07:14:32 +02:00
Matthias
87678eff98 fix: avoid hyperopt-results not showing past terminal height 2024-08-29 07:14:32 +02:00
Matthias
c1f54b14d0 Merge pull request #10594 from freqtrade/update/binance-leverage-tiers
Update Binance Leverage Tiers
2024-08-29 06:32:13 +02:00
xmatthias
4c487d666f chore: update pre-commit hooks 2024-08-29 03:12:45 +00:00
Matthias
655a300acb docs: re-establish search box on develop documentation 2024-08-28 20:27:36 +02:00
Matthias
8d61d66d79 Merge pull request #10573 from freqtrade/dependabot/pip/develop/types-451e0821cf
chore(deps-dev): bump the types group with 2 updates
2024-08-27 20:27:36 +02:00
Matthias
13f391fe4a Merge pull request #10577 from freqtrade/dependabot/pip/develop/websockets-13.0
chore(deps): bump websockets from 12.0 to 13.0
2024-08-27 19:48:21 +02:00
Matthias
660a5d910a chore: bump pre-commit type deps 2024-08-27 19:44:09 +02:00
Matthias
a58c5b372c Merge pull request #10588 from freqtrade/update/pre-commit-hooks
Update pre-commit hooks
2024-08-27 07:26:19 +02:00
xmatthias
ec55fdb8d8 chore: update pre-commit hooks 2024-08-27 03:02:41 +00:00
Matthias
19d670826d Merge pull request #10582 from freqtrade/dependabot/pip/develop/mypy-1.11.2
chore(deps-dev): bump mypy from 1.11.1 to 1.11.2
2024-08-26 11:58:55 +02:00
Matthias
7033bd19fe Merge pull request #10580 from freqtrade/dependabot/pip/develop/aiohttp-3.10.5
chore(deps): bump aiohttp from 3.10.4 to 3.10.5
2024-08-26 10:45:35 +02:00
Matthias
aea75b9e52 Merge pull request #10581 from freqtrade/dependabot/pip/develop/fastapi-0.112.2
chore(deps): bump fastapi from 0.112.1 to 0.112.2
2024-08-26 09:55:02 +02:00
dependabot[bot]
eaf68fe105 chore(deps): bump aiohttp from 3.10.4 to 3.10.5
Bumps [aiohttp](https://github.com/aio-libs/aiohttp) from 3.10.4 to 3.10.5.
- [Release notes](https://github.com/aio-libs/aiohttp/releases)
- [Changelog](https://github.com/aio-libs/aiohttp/blob/master/CHANGES.rst)
- [Commits](https://github.com/aio-libs/aiohttp/compare/v3.10.4...v3.10.5)

---
updated-dependencies:
- dependency-name: aiohttp
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-26 07:29:04 +00:00
Matthias
a27237286c Merge pull request #10578 from freqtrade/dependabot/pip/develop/ccxt-4.3.88
chore(deps): bump ccxt from 4.3.85 to 4.3.88
2024-08-26 09:27:25 +02:00
Matthias
502ca6b612 Merge pull request #10579 from freqtrade/dependabot/pip/develop/scipy-1.14.1
chore(deps): bump scipy from 1.14.0 to 1.14.1
2024-08-26 09:09:24 +02:00
dependabot[bot]
a9451a5413 chore(deps-dev): bump mypy from 1.11.1 to 1.11.2
Bumps [mypy](https://github.com/python/mypy) from 1.11.1 to 1.11.2.
- [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md)
- [Commits](https://github.com/python/mypy/compare/v1.11.1...v1.11.2)

---
updated-dependencies:
- dependency-name: mypy
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-26 06:40:25 +00:00
Matthias
ee54047b94 Merge pull request #10576 from freqtrade/dependabot/pip/develop/ruff-0.6.2
chore(deps-dev): bump ruff from 0.6.1 to 0.6.2
2024-08-26 08:39:58 +02:00
Matthias
a2e2c0a41a Merge pull request #10575 from freqtrade/dependabot/pip/develop/mkdocs-d35dd8fcd7
chore(deps): bump mkdocs-material from 9.5.32 to 9.5.33 in the mkdocs group
2024-08-26 08:39:39 +02:00
Matthias
8e7bfba0ab Merge pull request #10574 from freqtrade/dependabot/pip/develop/pytest-c16e4178ec
chore(deps-dev): bump pytest-asyncio from 0.23.8 to 0.24.0 in the pytest group
2024-08-26 08:39:00 +02:00
dependabot[bot]
6d280be081 chore(deps): bump fastapi from 0.112.1 to 0.112.2
Bumps [fastapi](https://github.com/fastapi/fastapi) from 0.112.1 to 0.112.2.
- [Release notes](https://github.com/fastapi/fastapi/releases)
- [Commits](https://github.com/fastapi/fastapi/compare/0.112.1...0.112.2)

---
updated-dependencies:
- dependency-name: fastapi
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-26 04:01:39 +00:00
dependabot[bot]
877c6635e4 chore(deps): bump scipy from 1.14.0 to 1.14.1
Bumps [scipy](https://github.com/scipy/scipy) from 1.14.0 to 1.14.1.
- [Release notes](https://github.com/scipy/scipy/releases)
- [Commits](https://github.com/scipy/scipy/compare/v1.14.0...v1.14.1)

---
updated-dependencies:
- dependency-name: scipy
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-26 04:01:13 +00:00
dependabot[bot]
ca0be181bc chore(deps): bump ccxt from 4.3.85 to 4.3.88
Bumps [ccxt](https://github.com/ccxt/ccxt) from 4.3.85 to 4.3.88.
- [Release notes](https://github.com/ccxt/ccxt/releases)
- [Changelog](https://github.com/ccxt/ccxt/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ccxt/ccxt/compare/4.3.85...4.3.88)

---
updated-dependencies:
- dependency-name: ccxt
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-26 04:00:57 +00:00
dependabot[bot]
ba2cf8015b chore(deps): bump websockets from 12.0 to 13.0
Bumps [websockets](https://github.com/python-websockets/websockets) from 12.0 to 13.0.
- [Release notes](https://github.com/python-websockets/websockets/releases)
- [Commits](https://github.com/python-websockets/websockets/compare/12.0...13.0)

---
updated-dependencies:
- dependency-name: websockets
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-26 04:00:51 +00:00
dependabot[bot]
f1f4ed97ca chore(deps-dev): bump ruff from 0.6.1 to 0.6.2
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.6.1 to 0.6.2.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/0.6.1...0.6.2)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-26 04:00:46 +00:00
dependabot[bot]
24785d28e6 chore(deps): bump mkdocs-material in the mkdocs group
Bumps the mkdocs group with 1 update: [mkdocs-material](https://github.com/squidfunk/mkdocs-material).


Updates `mkdocs-material` from 9.5.32 to 9.5.33
- [Release notes](https://github.com/squidfunk/mkdocs-material/releases)
- [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG)
- [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.32...9.5.33)

---
updated-dependencies:
- dependency-name: mkdocs-material
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: mkdocs
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-26 04:00:35 +00:00
dependabot[bot]
0076205da6 chore(deps-dev): bump pytest-asyncio in the pytest group
Bumps the pytest group with 1 update: [pytest-asyncio](https://github.com/pytest-dev/pytest-asyncio).


Updates `pytest-asyncio` from 0.23.8 to 0.24.0
- [Release notes](https://github.com/pytest-dev/pytest-asyncio/releases)
- [Commits](https://github.com/pytest-dev/pytest-asyncio/compare/v0.23.8...v0.24.0)

---
updated-dependencies:
- dependency-name: pytest-asyncio
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: pytest
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-26 04:00:21 +00:00
dependabot[bot]
6235b50c9d chore(deps-dev): bump the types group with 2 updates
Bumps the types group with 2 updates: [types-cachetools](https://github.com/python/typeshed) and [types-python-dateutil](https://github.com/python/typeshed).


Updates `types-cachetools` from 5.4.0.20240717 to 5.5.0.20240820
- [Commits](https://github.com/python/typeshed/commits)

Updates `types-python-dateutil` from 2.9.0.20240316 to 2.9.0.20240821
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-cachetools
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: types
- dependency-name: types-python-dateutil
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: types
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-26 03:59:43 +00:00
Matthias
5cca19bb83 refactor: simplify binance liquidation test setup 2024-08-24 18:07:19 +02:00
Matthias
1b7056853b refactor: move test_liquidation_price_binance to binance test file 2024-08-24 18:06:19 +02:00
Matthias
d1bc519599 docs: Show version alias on versions 2024-08-24 09:33:33 +02:00
Matthias
bcae1dce7b docs: reduce font-weight of version_list 2024-08-24 09:19:07 +02:00
Matthias
e87927564b chore: Improve typing 2024-08-23 18:18:05 +02:00
Matthias
01b7ad4a3f feat: prevent freqAI startup on exchanges without history
closes #10570
2024-08-23 18:16:06 +02:00
Matthias
235d38752c Merge pull request #10566 from freqtrade/update/binance-leverage-tiers
Update Binance Leverage Tiers
2024-08-22 06:33:17 +02:00
xmatthias
fd30edf2bb chore: update pre-commit hooks 2024-08-22 03:13:36 +00:00
Matthias
0a2be142ff Merge pull request #10565 from froggleston/develop
Add clarification for untradeable pairs vs markets
2024-08-21 20:25:12 +02:00
Matthias
33614d8ff0 docs: Improve wording for untradeable pairs 2024-08-21 19:51:24 +02:00
Matthias
3dce1d32f9 Merge pull request #10564 from iridescentGray/develop
chore: fix test param
2024-08-21 18:15:55 +02:00
Robert Davey
4a62199682 Add clarification for untradeable pairs vs markets 2024-08-21 15:26:19 +01:00
colorfulgray0
68be56240d chore: fix test param 2024-08-21 17:46:58 +08:00
Matthias
19ccb27dbd chore: deploy through github internal pipeline 2024-08-20 21:19:26 +02:00
Matthias
a7e2bf071b chore: Move deployment to gh native actions 2024-08-20 20:32:53 +02:00
Matthias
e05a6e976e chore: add Ci for gha deployment 2024-08-20 20:20:25 +02:00
Matthias
c7485e3fd4 chore: add mike to mkdocs config 2024-08-20 20:17:36 +02:00
Matthias
80ad1a68e7 Merge pull request #10554 from freqtrade/dependabot/pip/develop/tables-3.10.1
chore(deps): bump tables from 3.9.1 to 3.10.1
2024-08-20 07:45:55 +02:00
Matthias
f4440d43de chore: increase wait time on ws to avoid flukes 2024-08-20 06:45:09 +02:00
Matthias
201a5c06fe Merge pull request #10553 from freqtrade/dependabot/pip/develop/ruff-0.6.1
chore(deps-dev): bump ruff from 0.5.7 to 0.6.1
2024-08-19 20:37:12 +02:00
Matthias
6bd21b8995 chore: pin tables for python 3.9 2024-08-19 20:01:19 +02:00
Matthias
ce66fbb595 chore: ruff format notebook 2024-08-19 19:59:15 +02:00
Matthias
226ebdd935 Merge pull request #10560 from freqtrade/dependabot/pip/develop/ccxt-4.3.85
chore(deps): bump ccxt from 4.3.84 to 4.3.85
2024-08-19 19:10:41 +02:00
Matthias
fcc400b20d Merge pull request #10559 from freqtrade/dependabot/pip/develop/mkdocs-ffd8a664e0
chore(deps): bump mkdocs-material from 9.5.31 to 9.5.32 in the mkdocs group
2024-08-19 18:54:50 +02:00
Matthias
d2c908b1ab chore: bump ruff pre-commit version 2024-08-19 18:23:53 +02:00
Matthias
976f9b2590 chore: re-format ipynb notebook 2024-08-19 18:23:36 +02:00
dependabot[bot]
4d175a466e chore(deps): bump ccxt from 4.3.84 to 4.3.85
Bumps [ccxt](https://github.com/ccxt/ccxt) from 4.3.84 to 4.3.85.
- [Release notes](https://github.com/ccxt/ccxt/releases)
- [Changelog](https://github.com/ccxt/ccxt/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ccxt/ccxt/compare/4.3.84...4.3.85)

---
updated-dependencies:
- dependency-name: ccxt
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-19 16:20:09 +00:00
Matthias
986ff7d1b1 chore: rename parameter to avoid naming collision 2024-08-19 18:19:42 +02:00
dependabot[bot]
bc719feb5d chore(deps): bump mkdocs-material in the mkdocs group
Bumps the mkdocs group with 1 update: [mkdocs-material](https://github.com/squidfunk/mkdocs-material).


Updates `mkdocs-material` from 9.5.31 to 9.5.32
- [Release notes](https://github.com/squidfunk/mkdocs-material/releases)
- [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG)
- [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.31...9.5.32)

---
updated-dependencies:
- dependency-name: mkdocs-material
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: mkdocs
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-19 16:19:33 +00:00
Matthias
fe41612738 Merge pull request #10557 from freqtrade/dependabot/pip/develop/fastapi-0.112.1
chore(deps): bump fastapi from 0.112.0 to 0.112.1
2024-08-19 10:31:32 +02:00
Matthias
91f36ae42a Merge pull request #10552 from freqtrade/dependabot/pip/develop/matplotlib-3.9.2
chore(deps): bump matplotlib from 3.9.1.post1 to 3.9.2
2024-08-19 10:02:02 +02:00
Matthias
2750981b64 Merge pull request #10551 from freqtrade/dependabot/pip/develop/ccxt-4.3.84
chore(deps): bump ccxt from 4.3.79 to 4.3.84
2024-08-19 09:45:43 +02:00
jainanuj94
268683f8ea update documentation 2024-08-19 12:50:54 +05:30
Matthias
5737165f37 Merge pull request #10547 from freqtrade/dependabot/pip/develop/cachetools-5.5.0
chore(deps): bump cachetools from 5.4.0 to 5.5.0
2024-08-19 09:16:40 +02:00
dependabot[bot]
a70116ed4d chore(deps): bump fastapi from 0.112.0 to 0.112.1
Bumps [fastapi](https://github.com/fastapi/fastapi) from 0.112.0 to 0.112.1.
- [Release notes](https://github.com/fastapi/fastapi/releases)
- [Commits](https://github.com/fastapi/fastapi/compare/0.112.0...0.112.1)

---
updated-dependencies:
- dependency-name: fastapi
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-19 06:50:46 +00:00
Matthias
31d2296777 Merge pull request #10555 from freqtrade/dependabot/pip/develop/uvicorn-0.30.6
chore(deps): bump uvicorn from 0.30.5 to 0.30.6
2024-08-19 08:49:35 +02:00
dependabot[bot]
b859d7f3a5 chore(deps): bump ccxt from 4.3.79 to 4.3.84
Bumps [ccxt](https://github.com/ccxt/ccxt) from 4.3.79 to 4.3.84.
- [Release notes](https://github.com/ccxt/ccxt/releases)
- [Changelog](https://github.com/ccxt/ccxt/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ccxt/ccxt/compare/4.3.79...4.3.84)

---
updated-dependencies:
- dependency-name: ccxt
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-19 06:00:52 +00:00
dependabot[bot]
75714ae84a chore(deps): bump matplotlib from 3.9.1.post1 to 3.9.2
Bumps [matplotlib](https://github.com/matplotlib/matplotlib) from 3.9.1.post1 to 3.9.2.
- [Release notes](https://github.com/matplotlib/matplotlib/releases)
- [Commits](https://github.com/matplotlib/matplotlib/compare/v3.9.1.post1...v3.9.2)

---
updated-dependencies:
- dependency-name: matplotlib
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-19 06:00:50 +00:00
Matthias
be221c5a3e Merge pull request #10550 from freqtrade/dependabot/pip/develop/tensorboard-2.17.1
chore(deps): bump tensorboard from 2.17.0 to 2.17.1
2024-08-19 08:00:15 +02:00
Matthias
064ff34866 Merge pull request #10549 from freqtrade/dependabot/pip/develop/aiohttp-3.10.4
chore(deps): bump aiohttp from 3.10.3 to 3.10.4
2024-08-19 08:00:03 +02:00
Matthias
9807d6bb2c Merge pull request #10548 from freqtrade/dependabot/pip/develop/markdown-3.7
chore(deps): bump markdown from 3.6 to 3.7
2024-08-19 07:59:54 +02:00
dependabot[bot]
8321425e62 chore(deps): bump uvicorn from 0.30.5 to 0.30.6
Bumps [uvicorn](https://github.com/encode/uvicorn) from 0.30.5 to 0.30.6.
- [Release notes](https://github.com/encode/uvicorn/releases)
- [Changelog](https://github.com/encode/uvicorn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/encode/uvicorn/compare/0.30.5...0.30.6)

---
updated-dependencies:
- dependency-name: uvicorn
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-19 03:49:10 +00:00
dependabot[bot]
ba3223a9a3 chore(deps): bump tables from 3.9.1 to 3.10.1
Bumps [tables](https://github.com/PyTables/PyTables) from 3.9.1 to 3.10.1.
- [Release notes](https://github.com/PyTables/PyTables/releases)
- [Changelog](https://github.com/PyTables/PyTables/blob/master/RELEASE_NOTES.rst)
- [Commits](https://github.com/PyTables/PyTables/compare/v3.9.1...v3.10.1)

---
updated-dependencies:
- dependency-name: tables
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-19 03:49:05 +00:00
dependabot[bot]
a266997b69 chore(deps-dev): bump ruff from 0.5.7 to 0.6.1
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.5.7 to 0.6.1.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/0.5.7...0.6.1)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-19 03:49:02 +00:00
dependabot[bot]
314983b139 chore(deps): bump tensorboard from 2.17.0 to 2.17.1
Bumps [tensorboard](https://github.com/tensorflow/tensorboard) from 2.17.0 to 2.17.1.
- [Release notes](https://github.com/tensorflow/tensorboard/releases)
- [Changelog](https://github.com/tensorflow/tensorboard/blob/master/RELEASE.md)
- [Commits](https://github.com/tensorflow/tensorboard/compare/2.17.0...2.17.1)

---
updated-dependencies:
- dependency-name: tensorboard
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-19 03:48:37 +00:00
dependabot[bot]
8896b0ae7c chore(deps): bump aiohttp from 3.10.3 to 3.10.4
Bumps [aiohttp](https://github.com/aio-libs/aiohttp) from 3.10.3 to 3.10.4.
- [Release notes](https://github.com/aio-libs/aiohttp/releases)
- [Changelog](https://github.com/aio-libs/aiohttp/blob/master/CHANGES.rst)
- [Commits](https://github.com/aio-libs/aiohttp/compare/v3.10.3...v3.10.4)

---
updated-dependencies:
- dependency-name: aiohttp
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-19 03:48:31 +00:00
dependabot[bot]
b6aa922c09 chore(deps): bump markdown from 3.6 to 3.7
Bumps [markdown](https://github.com/Python-Markdown/markdown) from 3.6 to 3.7.
- [Release notes](https://github.com/Python-Markdown/markdown/releases)
- [Changelog](https://github.com/Python-Markdown/markdown/blob/master/docs/changelog.md)
- [Commits](https://github.com/Python-Markdown/markdown/compare/3.6...3.7)

---
updated-dependencies:
- dependency-name: markdown
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-19 03:48:17 +00:00
dependabot[bot]
95732f4170 chore(deps): bump cachetools from 5.4.0 to 5.5.0
Bumps [cachetools](https://github.com/tkem/cachetools) from 5.4.0 to 5.5.0.
- [Changelog](https://github.com/tkem/cachetools/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/tkem/cachetools/compare/v5.4.0...v5.5.0)

---
updated-dependencies:
- dependency-name: cachetools
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-19 03:48:14 +00:00
jainanuj94
b6702d1d32 simplify merging logic 2024-08-18 23:22:20 +05:30
jainanuj94
c3679910a4 remove additional argument 2024-08-18 23:14:21 +05:30
Matthias
624dfdf6ac Merge pull request #10546 from stash86/fix-recursive
add startup count from strategy to the analysis
2024-08-18 17:16:56 +02:00
Stefano Ariestasia
83e0cf75c5 add startup count from strategy to the analysis 2024-08-18 23:48:11 +09:00
jainanuj94
19a2e06c0b #000 | Anuj | Merge Dfs for entry and exit in one table 2024-08-18 18:41:04 +05:30
Matthias
7fe23ad8c9 chore: add alias_for to tests 2024-08-18 13:15:10 +02:00
Matthias
fd9ec438dc feat: show name, class name and eventually the replacement alias 2024-08-18 11:36:34 +02:00
Matthias
7cab973cbf feat: get new name for aliased ccxt exchanges 2024-08-18 11:36:06 +02:00
Matthias
9e3e5038f7 Merge pull request #10544 from freqtrade/feat/strategy_star_import
Improved imports for strategy
2024-08-18 09:53:45 +02:00
Matthias
7952712c5e chore: update samples to use doublequotes 2024-08-18 08:44:37 +02:00
Matthias
d754a2e295 feat: improve default imports 2024-08-18 08:38:59 +02:00
Matthias
768b4e5e2b chore: Update formatting of default export sequence 2024-08-18 08:38:11 +02:00
Matthias
b1ae09c003 docs: remove callback examples imports 2024-08-18 08:37:34 +02:00
Matthias
9408e858cd chore: use aligned quoting strategy for templtae 2024-08-17 16:43:46 +02:00
Matthias
0995164110 feat: improve formatting of generated strategy 2024-08-17 16:36:21 +02:00
Matthias
b3a042a63b feat: don't use commented typehints
Imports are correct now
2024-08-17 16:32:38 +02:00
Matthias
c2ac70ff10 feat: update base_strategy to include all imports 2024-08-17 16:30:06 +02:00
Matthias
e7b57d8dee chore: Update import for qtpylib to technical 2024-08-17 16:28:56 +02:00
Matthias
5bc8b02b0f feat: Update imports for sample strategy 2024-08-17 16:28:19 +02:00
Matthias
d6f96b2c53 chore: remove typing imports
These shouldn't be star imported, but should be explicitly imported.
2024-08-17 16:26:21 +02:00
Matthias
6c131b5648 chore: add comment to better explain imports 2024-08-17 16:25:47 +02:00
Matthias
27a4a502d7 docs: Add section explaining strategy imports 2024-08-17 16:25:02 +02:00
Matthias
f0a25ea485 feat: Add __all__ export to strategy's init file 2024-08-17 16:24:59 +02:00
Matthias
4ca6e61726 fix: use dynamic trading_mode for trades loading
closes #10540
2024-08-16 18:27:30 +02:00
Matthias
e26ac6ed00 test: speed up detail test 2024-08-15 20:02:45 +02:00
Matthias
f341edb975 feat: Enable websocket support for okx 2024-08-15 19:54:08 +02:00
Matthias
fdad24aaac feat: add leverage to telegram's /status table 2024-08-15 17:57:00 +02:00
Matthias
3a676f98db test: improve telegram balance test 2024-08-15 17:36:14 +02:00
Matthias
8498cb17e7 test: add explicit test for telegram's short behavior 2024-08-15 17:36:14 +02:00
Matthias
36098f6b78 test: update tests for removal of leverage 2024-08-15 17:36:14 +02:00
Matthias
34667c69d3 chore: remove leverage from /balance endpoint 2024-08-15 17:00:11 +02:00
Matthias
756fef53f9 refactor: improve live positions update 2024-08-15 17:00:08 +02:00
Matthias
2ffe938206 test: update test behavior - wallets has 0, never none 2024-08-15 08:21:25 +02:00
Matthias
d521699305 refactor: type fetch_positions response 2024-08-15 08:08:50 +02:00
Matthias
5ad23405b7 chore: align safevalue_fallback types 2024-08-15 08:06:50 +02:00
Matthias
04cdd807ba chore: improved type ordering 2024-08-15 07:30:21 +02:00
Matthias
646ed50f37 chore: improve typing for balance endpoint 2024-08-15 07:29:19 +02:00
Matthias
1b0ba0fa68 fix: typo in armhf dockerfile causing build to fail 2024-08-15 06:58:43 +02:00
Matthias
21c5c919ea chore: Improve typehinting for hyperopt 2024-08-15 06:50:22 +02:00
Matthias
d9f6f0847d docs: improve readability of hyperopt-loss sample 2024-08-15 06:50:04 +02:00
Matthias
59c897b53e Merge pull request #10534 from freqtrade/update/binance-leverage-tiers
Update Binance Leverage Tiers
2024-08-15 06:21:15 +02:00
xmatthias
77b32e94f1 chore: update pre-commit hooks 2024-08-15 03:12:42 +00:00
Matthias
331159a3d8 fix: ensure handle_onexchange_order works without false warnings
futures were not properly handled in this command.

closes #10533
2024-08-14 21:18:51 +02:00
Matthias
23510c80be fix: don't auto-populate non-existing secret entries 2024-08-14 08:19:58 +02:00
Matthias
ef497beaea Merge pull request #10484 from freqtrade/feat/trades_async
Move trades-refresh to async
2024-08-13 15:18:26 +02:00
Matthias
6ea450a4e1 chore: bitvavo uses DECIMAL_PLACES for amount rounding
closes #9560
2024-08-13 14:30:01 +02:00
Matthias
f64786543d feat: hyperliquid requires different precision modes 2024-08-13 12:21:13 +02:00
Matthias
c60e00c77f Merge pull request #10530 from freqtrade/feat/price_precision_mode
add price_precision_mode
2024-08-13 12:20:53 +02:00
Matthias
7e502beafc Merge pull request #10527 from freqtrade/feat/bt_generator
Backtesting - dynamic pairlist sorting
2024-08-13 09:56:19 +02:00
Matthias
aa6c30ade6 chore: fix line too long issue 2024-08-13 09:50:34 +02:00
Matthias
0b8dfa6878 chore: improved docstring for precision_mode_price 2024-08-13 09:29:36 +02:00
Matthias
d7bee0c9e0 test: update further tests for precision_mode_price 2024-08-13 09:23:43 +02:00
Matthias
350c2241c4 test: adjust test mocks for precision_mode_price 2024-08-13 09:20:40 +02:00
Matthias
cfa591838f feat: use "precision_mode_price" where applicable 2024-08-13 09:13:10 +02:00
Matthias
ac1ac0debe feat: set precision_mode_price when creating trade objects 2024-08-13 09:11:44 +02:00
Matthias
54bc60b08f test: Update test for new to-json field 2024-08-13 09:10:50 +02:00
Matthias
f8de46cea9 feat: Add precision_mode_price column 2024-08-13 09:09:12 +02:00
Matthias
6fc2a604b4 Merge pull request #10529 from freqtrade/feat/list-data-trades
add --trades to list-data command
2024-08-13 08:13:23 +02:00
Matthias
1e410feed1 test: fix missing test arg 2024-08-13 07:33:13 +02:00
Matthias
948e67a2b7 docs: improved wording 2024-08-13 07:14:08 +02:00
Matthias
0f820e4498 chore: Fix 3.9 syntax error 2024-08-13 07:12:57 +02:00
Matthias
c7bc1b10e2 docs: fix messed up formatting 2024-08-13 07:04:51 +02:00
Matthias
ef04324f9d docs: update --help output docs 2024-08-13 07:03:00 +02:00
Matthias
2b86865b9b chore: improve wording in subcommand helptext 2024-08-13 07:02:53 +02:00
Matthias
f009625c1a docs: update list-data documentation 2024-08-13 07:01:23 +02:00
Matthias
a991c76842 feat: add test for test_list_data command 2024-08-13 06:54:35 +02:00
Matthias
d02ea3244a feat: add "trades" switch to list-data command 2024-08-13 06:46:02 +02:00
Matthias
5a9f87ac63 feat: add start_list_trades_data command to output trades data 2024-08-13 06:44:28 +02:00
Matthias
9bfd0cb63c feat: add test for trades_data_minmax 2024-08-13 06:43:26 +02:00
Matthias
3f4c19abbc chore: add test for trades_get_available_data 2024-08-13 06:40:11 +02:00
Matthias
cf26635e3c feat: add trades helper functions
trades_get_available data and trades_data_min_max
2024-08-13 06:36:39 +02:00
Matthias
e540862401 Merge pull request #10528 from freqtrade/update/pre-commit-hooks
Update pre-commit hooks
2024-08-13 06:34:44 +02:00
xmatthias
263be72c11 chore: update pre-commit hooks 2024-08-13 03:02:59 +00:00
Matthias
b63c04df4f chore: update help wording 2024-08-12 20:11:43 +02:00
Matthias
784208dd87 chore: improve variable naming for ohlcv 2024-08-12 19:44:55 +02:00
Matthias
5cb6c234c4 chore: improve naming for refresh_latest_trades 2024-08-12 19:44:45 +02:00
Matthias
7972a023ed fix: oddly wrong fee_cost calculation 2024-08-12 16:45:51 +02:00
Matthias
6cf92c2a90 chore: enhanced aggregation syntax 2024-08-12 16:41:07 +02:00
Matthias
50835c878e chore: add more test coverage 2024-08-12 15:35:31 +02:00
Matthias
b727e5ca1c chore: simplify update code 2024-08-12 14:51:42 +02:00
Matthias
5773d1fd8d docs: Update documentation for new flow 2024-08-12 14:51:42 +02:00
Matthias
530226dbe8 chore: Add "use_detail" to detail test 2024-08-12 14:51:42 +02:00
Matthias
4882a18bf9 chore: add pair_detail test 2024-08-12 14:51:42 +02:00
Matthias
70f3018e67 feat: remove "open_trade_count_start" workaround
Due to the updated pair ordering logic, we can open trades on
different pairs during the same candle without
superating the max_open_trades limit
2024-08-12 14:51:42 +02:00
Matthias
08c10c1f9b chore: exclude right boundary from parallelism test 2024-08-12 14:51:42 +02:00
Matthias
7945eba386 feat: Evaluate pairs with open trades first
This will enable further improved logic for pairs with no open trade.
2024-08-12 14:51:42 +02:00
Matthias
b6f4e124ce chore: improve backtesting test details
ensure all candles used the same pairlist ordering
2024-08-12 14:51:42 +02:00
Matthias
f01e101447 feat: extract backtest iteration into generator 2024-08-12 14:51:42 +02:00
Matthias
980b81f009 chore: Simplify futures backtest 2024-08-12 14:51:37 +02:00
Matthias
10f0522a6b chore: update attribute wording to bt_profit 2024-08-12 10:57:53 +02:00
Matthias
2bc9cdafb2 chore: update attribute wording to bt_trades 2024-08-12 10:57:53 +02:00
Matthias
e643a2ea32 chore: update attribute wording to bt_trades_open 2024-08-12 10:57:53 +02:00
Matthias
b456afa2ac chore: improve backtesting test 2024-08-12 10:20:36 +02:00
Matthias
50b55c3f31 Merge pull request #10522 from freqtrade/dependabot/pip/develop/ccxt-4.3.79
chore(deps): bump ccxt from 4.3.76 to 4.3.79
2024-08-12 09:45:55 +02:00
Matthias
88b754e38c chore: update test to reflect a fix in ccxt 2024-08-12 09:14:36 +02:00
Matthias
b3868a77f1 Merge pull request #10525 from freqtrade/dependabot/pip/develop/orjson-3.10.7
chore(deps): bump orjson from 3.10.6 to 3.10.7
2024-08-12 07:11:24 +02:00
dependabot[bot]
16d5d7b318 chore(deps): bump ccxt from 4.3.76 to 4.3.79
Bumps [ccxt](https://github.com/ccxt/ccxt) from 4.3.76 to 4.3.79.
- [Release notes](https://github.com/ccxt/ccxt/releases)
- [Changelog](https://github.com/ccxt/ccxt/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ccxt/ccxt/compare/4.3.76...4.3.79)

---
updated-dependencies:
- dependency-name: ccxt
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-12 04:43:08 +00:00
Matthias
dbef33fe00 Merge pull request #10524 from freqtrade/dependabot/pip/develop/ruff-0.5.7
chore(deps-dev): bump ruff from 0.5.6 to 0.5.7
2024-08-12 06:42:31 +02:00
Matthias
8c11ea69a0 Merge pull request #10523 from freqtrade/dependabot/pip/develop/aiohttp-3.10.3
chore(deps): bump aiohttp from 3.10.2 to 3.10.3
2024-08-12 06:39:37 +02:00
dependabot[bot]
fa0ee035e9 chore(deps): bump orjson from 3.10.6 to 3.10.7
Bumps [orjson](https://github.com/ijl/orjson) from 3.10.6 to 3.10.7.
- [Release notes](https://github.com/ijl/orjson/releases)
- [Changelog](https://github.com/ijl/orjson/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ijl/orjson/compare/3.10.6...3.10.7)

---
updated-dependencies:
- dependency-name: orjson
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-12 03:26:27 +00:00
dependabot[bot]
010dbf82f3 chore(deps-dev): bump ruff from 0.5.6 to 0.5.7
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.5.6 to 0.5.7.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/0.5.6...0.5.7)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-12 03:26:18 +00:00
dependabot[bot]
9a9d27b862 chore(deps): bump aiohttp from 3.10.2 to 3.10.3
Bumps [aiohttp](https://github.com/aio-libs/aiohttp) from 3.10.2 to 3.10.3.
- [Release notes](https://github.com/aio-libs/aiohttp/releases)
- [Changelog](https://github.com/aio-libs/aiohttp/blob/master/CHANGES.rst)
- [Commits](https://github.com/aio-libs/aiohttp/compare/v3.10.2...v3.10.3)

---
updated-dependencies:
- dependency-name: aiohttp
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-12 03:25:59 +00:00
Matthias
0afd3fc5e1 fix: improved handling for corrupt trades files
part of #10515
2024-08-10 20:13:36 +02:00
Matthias
f5ebfcca5a chore: accept that trades dataframes may be empty for some reason
part of #10515
2024-08-10 20:13:30 +02:00
Matthias
6f33115187 Merge pull request #10400 from simwai/feature/stoploss-start-at
Added unlock_at field for protection config
2024-08-10 16:40:09 +02:00
Matthias
58c65ab48c Merge pull request #10519 from freqtrade/dependabot/pip/aiohttp-3.10.2
chore(deps): bump aiohttp from 3.10.1 to 3.10.2
2024-08-10 08:06:29 +02:00
dependabot[bot]
42294ff695 chore(deps): bump aiohttp from 3.10.1 to 3.10.2
Bumps [aiohttp](https://github.com/aio-libs/aiohttp) from 3.10.1 to 3.10.2.
- [Release notes](https://github.com/aio-libs/aiohttp/releases)
- [Changelog](https://github.com/aio-libs/aiohttp/blob/master/CHANGES.rst)
- [Commits](https://github.com/aio-libs/aiohttp/compare/v3.10.1...v3.10.2)

---
updated-dependencies:
- dependency-name: aiohttp
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-09 17:02:14 +00:00
Matthias
ed59f74cb8 chore: move asyncio import to only import when necessary 2024-08-09 06:45:08 +02:00
Matthias
d91dee141d Merge pull request #10500 from freqtrade/dependabot/pip/develop/aiohttp-3.10.1
chore(deps): bump aiohttp from 3.9.5 to 3.10.1
2024-08-09 06:44:19 +02:00
Matthias
2b4438720c chore: call selectorPolicy 2024-08-09 06:16:44 +02:00
Matthias
758e532a6a chore: add todo to uvicorn workaround 2024-08-08 20:25:17 +02:00
Matthias
d4ca6617de chore: set asyncio-policy for windows 2024-08-08 20:25:09 +02:00
Matthias
67fe7f8d3b Merge pull request #10513 from freqtrade/dependabot/pip/develop/matplotlib-3.9.1.post1
chore(deps): bump matplotlib from 3.9.0 to 3.9.1.post1
2024-08-08 18:18:11 +02:00
dependabot[bot]
f6040c5f06 chore(deps): bump aiohttp from 3.9.5 to 3.10.1
Bumps [aiohttp](https://github.com/aio-libs/aiohttp) from 3.9.5 to 3.10.1.
- [Release notes](https://github.com/aio-libs/aiohttp/releases)
- [Changelog](https://github.com/aio-libs/aiohttp/blob/master/CHANGES.rst)
- [Commits](https://github.com/aio-libs/aiohttp/compare/v3.9.5...v3.10.1)

---
updated-dependencies:
- dependency-name: aiohttp
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-08 07:53:56 +00:00
Matthias
324c384fdf Merge pull request #10514 from freqtrade/dependabot/pip/develop/ccxt-4.3.76
chore(deps): bump ccxt from 4.3.73 to 4.3.76
2024-08-08 09:53:00 +02:00
dependabot[bot]
de70ee117c chore(deps): bump matplotlib from 3.9.0 to 3.9.1.post1
Bumps [matplotlib](https://github.com/matplotlib/matplotlib) from 3.9.0 to 3.9.1.post1.
- [Release notes](https://github.com/matplotlib/matplotlib/releases)
- [Commits](https://github.com/matplotlib/matplotlib/compare/v3.9.0...v3.9.1.post1)

---
updated-dependencies:
- dependency-name: matplotlib
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-08 06:37:43 +00:00
dependabot[bot]
85844c8ed4 chore(deps): bump ccxt from 4.3.73 to 4.3.76
Bumps [ccxt](https://github.com/ccxt/ccxt) from 4.3.73 to 4.3.76.
- [Release notes](https://github.com/ccxt/ccxt/releases)
- [Changelog](https://github.com/ccxt/ccxt/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ccxt/ccxt/compare/4.3.73...4.3.76)

---
updated-dependencies:
- dependency-name: ccxt
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-08 06:37:18 +00:00
Matthias
101dc850a2 Update pre-commit sqlalchemy types 2024-08-08 08:36:14 +02:00
Matthias
d88c7c76c6 Merge pull request #10512 from freqtrade/dependabot/pip/develop/time-machine-2.15.0
chore(deps-dev): bump time-machine from 2.14.2 to 2.15.0
2024-08-08 08:26:51 +02:00
Matthias
1fea5f53bd Merge pull request #10511 from freqtrade/dependabot/pip/develop/python-rapidjson-1.20
chore(deps): bump python-rapidjson from 1.19 to 1.20
2024-08-08 08:08:22 +02:00
Matthias
ca13109a20 Merge pull request #10510 from freqtrade/dependabot/pip/develop/sqlalchemy-2.0.32
chore(deps): bump sqlalchemy from 2.0.31 to 2.0.32
2024-08-08 07:19:16 +02:00
Matthias
5650de0627 chore: dependabot shouldn't update major versions 2024-08-08 06:25:52 +02:00
dependabot[bot]
999ee981f7 chore(deps-dev): bump time-machine from 2.14.2 to 2.15.0
Bumps [time-machine](https://github.com/adamchainz/time-machine) from 2.14.2 to 2.15.0.
- [Changelog](https://github.com/adamchainz/time-machine/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/adamchainz/time-machine/compare/2.14.2...2.15.0)

---
updated-dependencies:
- dependency-name: time-machine
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-08 04:22:36 +00:00
dependabot[bot]
ee6e78927f chore(deps): bump python-rapidjson from 1.19 to 1.20
Bumps [python-rapidjson](https://github.com/python-rapidjson/python-rapidjson) from 1.19 to 1.20.
- [Changelog](https://github.com/python-rapidjson/python-rapidjson/blob/master/CHANGES.rst)
- [Commits](https://github.com/python-rapidjson/python-rapidjson/compare/v1.19...v1.20)

---
updated-dependencies:
- dependency-name: python-rapidjson
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-08 04:22:22 +00:00
dependabot[bot]
9dd9ae7a2f chore(deps): bump sqlalchemy from 2.0.31 to 2.0.32
Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 2.0.31 to 2.0.32.
- [Release notes](https://github.com/sqlalchemy/sqlalchemy/releases)
- [Changelog](https://github.com/sqlalchemy/sqlalchemy/blob/main/CHANGES.rst)
- [Commits](https://github.com/sqlalchemy/sqlalchemy/commits)

---
updated-dependencies:
- dependency-name: sqlalchemy
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-08 04:22:06 +00:00
Matthias
cbd178dab2 chore: bump armhf dockerfile to 3.11 2024-08-08 06:20:58 +02:00
Matthias
e34a28ee53 chore: dependabot should monitor /docker, too 2024-08-08 06:18:31 +02:00
Matthias
ffcc55b42b Merge pull request #10508 from freqtrade/dependabot/docker/python-3.12.5-slim-bookworm
chore(deps): bump python from 3.12.4-slim-bookworm to 3.12.5-slim-bookworm
2024-08-08 06:09:43 +02:00
Matthias
72028a9a2e Merge pull request #10507 from freqtrade/update/binance-leverage-tiers
Update Binance Leverage Tiers
2024-08-08 06:09:28 +02:00
dependabot[bot]
d453aa849a chore(deps): bump python
Bumps python from 3.12.4-slim-bookworm to 3.12.5-slim-bookworm.

---
updated-dependencies:
- dependency-name: python
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-08 03:26:08 +00:00
xmatthias
9d0cd961b4 chore: update pre-commit hooks 2024-08-08 03:12:08 +00:00
Matthias
900922760a feat: add json schema validation docs 2024-08-07 20:20:08 +02:00
jainanuj94
8085e24dcd update tests 2024-08-06 20:00:05 +05:30
jainanuj94
d351ed0173 refactor: change analyse_on variable name to date_col 2024-08-06 15:16:30 +05:30
jainanuj94
3ebc5b136c review comments and update test for exit signals 2024-08-06 12:55:48 +05:30
Matthias
a1d02ca689 Merge pull request #10505 from freqtrade/update/pre-commit-hooks
Update pre-commit hooks
2024-08-06 06:23:30 +02:00
xmatthias
95546e0a7b chore: update pre-commit hooks 2024-08-06 03:02:53 +00:00
jainanuj94
103991746b chore: type safety and refactoring 2024-08-05 23:57:24 +05:30
jainanuj94
7f0e5dd335 Refactor and add documentation 2024-08-05 23:19:38 +05:30
Matthias
fa2fc63b7c Merge pull request #10498 from freqtrade/dependabot/pip/develop/pyjwt-2.9.0
chore(deps): bump pyjwt from 2.8.0 to 2.9.0
2024-08-05 13:35:16 +02:00
Matthias
4ad915761b Merge pull request #10495 from freqtrade/dependabot/pip/develop/ccxt-4.3.73
chore(deps): bump ccxt from 4.3.68 to 4.3.73
2024-08-05 12:02:22 +02:00
Matthias
d6a29c1ad5 Merge pull request #10501 from freqtrade/dependabot/pip/develop/mypy-1.11.1
chore(deps-dev): bump mypy from 1.11.0 to 1.11.1
2024-08-05 11:44:23 +02:00
dependabot[bot]
3d439c8c01 chore(deps): bump pyjwt from 2.8.0 to 2.9.0
Bumps [pyjwt](https://github.com/jpadilla/pyjwt) from 2.8.0 to 2.9.0.
- [Release notes](https://github.com/jpadilla/pyjwt/releases)
- [Changelog](https://github.com/jpadilla/pyjwt/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/jpadilla/pyjwt/compare/2.8.0...2.9.0)

---
updated-dependencies:
- dependency-name: pyjwt
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-05 08:58:46 +00:00
Matthias
af00374cb0 Merge pull request #10494 from freqtrade/dependabot/pip/develop/uvicorn-0.30.5
chore(deps): bump uvicorn from 0.30.3 to 0.30.5
2024-08-05 10:57:56 +02:00
Matthias
0bb46aaef4 Merge pull request #10489 from freqtrade/dependabot/pip/develop/mkdocs-fecff3054d
chore(deps): bump mkdocs-material from 9.5.30 to 9.5.31 in the mkdocs group
2024-08-05 10:29:46 +02:00
Matthias
00377e91b4 Merge pull request #10496 from freqtrade/dependabot/pip/develop/tqdm-4.66.5
chore(deps): bump tqdm from 4.66.4 to 4.66.5
2024-08-05 09:33:01 +02:00
dependabot[bot]
477448114a chore(deps-dev): bump mypy from 1.11.0 to 1.11.1
Bumps [mypy](https://github.com/python/mypy) from 1.11.0 to 1.11.1.
- [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md)
- [Commits](https://github.com/python/mypy/compare/v1.11...v1.11.1)

---
updated-dependencies:
- dependency-name: mypy
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-05 06:34:01 +00:00
dependabot[bot]
91da1c3f8b chore(deps): bump uvicorn from 0.30.3 to 0.30.5
Bumps [uvicorn](https://github.com/encode/uvicorn) from 0.30.3 to 0.30.5.
- [Release notes](https://github.com/encode/uvicorn/releases)
- [Changelog](https://github.com/encode/uvicorn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/encode/uvicorn/compare/0.30.3...0.30.5)

---
updated-dependencies:
- dependency-name: uvicorn
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-05 06:33:26 +00:00
Matthias
eddf99ddd0 Merge pull request #10492 from freqtrade/dependabot/pip/develop/ruff-0.5.6
chore(deps-dev): bump ruff from 0.5.5 to 0.5.6
2024-08-05 08:33:23 +02:00
Matthias
f27580b5ae Merge pull request #10493 from freqtrade/dependabot/pip/develop/technical-1.4.4
chore(deps): bump technical from 1.4.3 to 1.4.4
2024-08-05 08:33:15 +02:00
Matthias
b2d35751b4 Merge pull request #10490 from freqtrade/dependabot/pip/develop/fastapi-0.112.0
chore(deps): bump fastapi from 0.111.1 to 0.112.0
2024-08-05 08:32:31 +02:00
Matthias
18df06a7ce Merge pull request #10487 from froggleston/develop
Add rich table width if jupyter in modules
2024-08-05 06:53:43 +02:00
dependabot[bot]
0bee3c9db0 chore(deps): bump tqdm from 4.66.4 to 4.66.5
Bumps [tqdm](https://github.com/tqdm/tqdm) from 4.66.4 to 4.66.5.
- [Release notes](https://github.com/tqdm/tqdm/releases)
- [Commits](https://github.com/tqdm/tqdm/compare/v4.66.4...v4.66.5)

---
updated-dependencies:
- dependency-name: tqdm
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-05 03:27:30 +00:00
dependabot[bot]
e3ba28d767 chore(deps): bump ccxt from 4.3.68 to 4.3.73
Bumps [ccxt](https://github.com/ccxt/ccxt) from 4.3.68 to 4.3.73.
- [Release notes](https://github.com/ccxt/ccxt/releases)
- [Changelog](https://github.com/ccxt/ccxt/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ccxt/ccxt/compare/4.3.68...4.3.73)

---
updated-dependencies:
- dependency-name: ccxt
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-05 03:27:23 +00:00
dependabot[bot]
1f9c2cd181 chore(deps): bump technical from 1.4.3 to 1.4.4
Bumps [technical](https://github.com/freqtrade/technical) from 1.4.3 to 1.4.4.
- [Release notes](https://github.com/freqtrade/technical/releases)
- [Commits](https://github.com/freqtrade/technical/compare/1.4.3...1.4.4)

---
updated-dependencies:
- dependency-name: technical
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-05 03:27:08 +00:00
dependabot[bot]
ea2b12a548 chore(deps-dev): bump ruff from 0.5.5 to 0.5.6
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.5.5 to 0.5.6.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/0.5.5...0.5.6)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-05 03:27:05 +00:00
dependabot[bot]
c9f4db2a4f chore(deps): bump fastapi from 0.111.1 to 0.112.0
Bumps [fastapi](https://github.com/fastapi/fastapi) from 0.111.1 to 0.112.0.
- [Release notes](https://github.com/fastapi/fastapi/releases)
- [Commits](https://github.com/fastapi/fastapi/compare/0.111.1...0.112.0)

---
updated-dependencies:
- dependency-name: fastapi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-05 03:26:45 +00:00
dependabot[bot]
1e5154c901 chore(deps): bump mkdocs-material in the mkdocs group
Bumps the mkdocs group with 1 update: [mkdocs-material](https://github.com/squidfunk/mkdocs-material).


Updates `mkdocs-material` from 9.5.30 to 9.5.31
- [Release notes](https://github.com/squidfunk/mkdocs-material/releases)
- [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG)
- [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.30...9.5.31)

---
updated-dependencies:
- dependency-name: mkdocs-material
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: mkdocs
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-05 03:26:37 +00:00
froggleston
17dc41279c Ruff formatting 2024-08-04 21:59:07 +01:00
froggleston
cb4747aed2 Add rich table width if jupyter in modules 2024-08-04 21:43:00 +01:00
Matthias
ce8d03ddce chore: improve comment as to why matplotlib is pinned in the first place 2024-08-04 19:55:40 +02:00
Matthias
366c7e2b91 fix: pin matplotlib to 3.9.0 to fix windows wheels
caused by the partial yank of 3.9.1 (only the windows wheels have been deleted).

Ref: https://github.com/matplotlib/matplotlib/issues/28551
2024-08-04 19:54:51 +02:00
Matthias
6c5fb5e22b chore: add_config_files to config_schema 2024-08-04 09:03:32 +02:00
Matthias
4854bdd02f chore: Add log_responses to config schema 2024-08-04 08:29:15 +02:00
Matthias
d7ecdc9b07 chore: Downgrade cryptography for RPI
https://github.com/piwheels/packages/issues/464
2024-08-03 18:31:39 +02:00
Matthias
c8d30ae801 chore: fix oneline exchange-list output 2024-08-03 18:30:21 +02:00
Matthias
9eebe82b34 chore: fix api-server tests 2024-08-03 18:29:16 +02:00
Matthias
b3915ff8fd chore: use classname to show exchanges 2024-08-03 18:25:31 +02:00
Matthias
805c946b33 feat: improve structure of list_exchange endpoints 2024-08-03 18:24:52 +02:00
Matthias
8bc1949466 docs: update link to technical documentation 2024-08-03 16:41:22 +02:00
Matthias
a6689b1035 chore: Remove unnecessary, duplicate mkdocs install 2024-08-03 13:29:54 +02:00
Matthias
f63910d355 chore: improve wording for cooldown_period 2024-08-02 20:15:46 +02:00
Matthias
98c8521057 chore: fix minor gotcha 2024-08-02 20:13:59 +02:00
Matthias
57139295b5 tests: Add unlock_at test 2024-08-02 20:12:44 +02:00
Matthias
79d4dc1646 Merge branch 'develop' into feature/stoploss-start-at 2024-08-02 19:51:28 +02:00
Matthias
1760624954 test: Test "invalid date format" 2024-08-02 19:48:43 +02:00
jainanuj94
ecf9c173c4 Add test for backtesitng-analysis 2024-08-02 20:46:19 +05:30
jainanuj94
b0e863dbbb Introduce --exit-signals flag to backtesting-analysis command 2024-08-02 20:09:56 +05:30
jainanuj94
8f8859a5f5 Initial commit - create a different file for signals 2024-08-02 15:54:03 +05:30
Matthias
9429657a2b chore: make Hyperliquid class actually usable 2024-08-02 07:28:09 +02:00
Matthias
2b0b1e23eb chore: enhance error message on ohlcv error 2024-08-02 07:26:54 +02:00
Matthias
dd55baf148 chore: support snake_case for api keys 2024-08-02 07:26:54 +02:00
Matthias
4542157192 Merge pull request #10470 from freqtrade/dependabot/pip/develop/torch-2.4.0
chore(deps): bump torch from 2.2.2 to 2.4.0
2024-08-02 06:56:13 +02:00
Matthias
9e47172d69 chore: Reduce test flakyness of ws test 2024-08-01 20:37:40 +02:00
jainanuj94
2ad921f99e Merge remote-tracking branch 'upstream/develop' into develop 2024-08-01 23:55:01 +05:30
Matthias
a840969512 feat: move trades-refresh to async 2024-08-01 19:58:17 +02:00
Matthias
67fdfdf584 chore: Update schema file 2024-08-01 19:39:06 +02:00
Matthias
abef8e376c feat: add $schema to config examples 2024-08-01 07:03:34 +02:00
Matthias
8a85077e70 chore: add download_trades config key, reorder some keys 2024-08-01 07:02:47 +02:00
Matthias
b3ac296cac chore: Improve schema wording 2024-08-01 06:58:17 +02:00
Matthias
b2db733c83 Merge pull request #10482 from freqtrade/maint/remove_pip_24_lock
chore: Remove pip pin from ci
2024-08-01 06:43:31 +02:00
Matthias
92dfcf3b6d Merge pull request #10483 from freqtrade/update/binance-leverage-tiers
Update Binance Leverage Tiers
2024-08-01 06:43:19 +02:00
xmatthias
af554fc3f7 chore: update pre-commit hooks 2024-08-01 03:15:58 +00:00
Matthias
02621eee74 chore: remove pip version lock from instal scripts 2024-07-31 20:39:21 +02:00
Matthias
8105f51603 chore: remove pip lock from Dockerfiles 2024-07-31 20:39:12 +02:00
Matthias
c40ac27d71 chore: Remove pip pin from ci 2024-07-31 20:36:48 +02:00
Matthias
d33c930f26 Merge pull request #10454 from jainanuj94/feature/10348
Add  percent change pairlist
2024-07-30 20:58:27 +02:00
Matthias
eb0fc0fc80 docs: Fix minor typo 2024-07-30 20:29:21 +02:00
Matthias
a6563543a3 Merge pull request #10477 from freqtrade/update/pre-commit-hooks
Update pre-commit hooks
2024-07-30 06:10:05 +02:00
xmatthias
40b20c5595 chore: update pre-commit hooks 2024-07-30 03:02:51 +00:00
Matthias
1ebbfffd2a chore: hyperliquid doesn't have historic ohlcv 2024-07-29 19:42:28 +02:00
Matthias
24d3e09618 Merge pull request #10467 from freqtrade/dependabot/pip/develop/pre-commit-3.8.0
chore(deps-dev): bump pre-commit from 3.7.1 to 3.8.0
2024-07-29 14:24:09 +02:00
Matthias
d92ddc4c7a Merge pull request #10471 from freqtrade/dependabot/pip/develop/pymdown-extensions-10.9
chore(deps): bump pymdown-extensions from 10.8.1 to 10.9
2024-07-29 11:38:11 +02:00
dependabot[bot]
c8b7580830 chore(deps-dev): bump pre-commit from 3.7.1 to 3.8.0
Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 3.7.1 to 3.8.0.
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pre-commit/pre-commit/compare/v3.7.1...v3.8.0)

---
updated-dependencies:
- dependency-name: pre-commit
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-29 08:27:01 +00:00
Matthias
a9d0c052bc Merge pull request #10468 from freqtrade/dependabot/pip/develop/ruff-0.5.5
chore(deps-dev): bump ruff from 0.5.4 to 0.5.5
2024-07-29 10:26:15 +02:00
Matthias
ad0b349a3f Merge pull request #10474 from freqtrade/dependabot/pip/develop/lightgbm-4.5.0
chore(deps): bump lightgbm from 4.4.0 to 4.5.0
2024-07-29 08:50:36 +02:00
Matthias
9da51a8d85 Merge pull request #10473 from freqtrade/dependabot/pip/develop/plotly-5.23.0
chore(deps): bump plotly from 5.22.0 to 5.23.0
2024-07-29 08:50:23 +02:00
Matthias
814f21a50e Merge pull request #10472 from freqtrade/dependabot/pip/develop/python-rapidjson-1.19
chore(deps): bump python-rapidjson from 1.18 to 1.19
2024-07-29 08:49:54 +02:00
Matthias
092669fb9d Merge pull request #10469 from freqtrade/dependabot/pip/develop/ccxt-4.3.68
chore(deps): bump ccxt from 4.3.65 to 4.3.68
2024-07-29 07:45:22 +02:00
dependabot[bot]
3789e1339b chore(deps): bump pymdown-extensions from 10.8.1 to 10.9
Bumps [pymdown-extensions](https://github.com/facelessuser/pymdown-extensions) from 10.8.1 to 10.9.
- [Release notes](https://github.com/facelessuser/pymdown-extensions/releases)
- [Commits](https://github.com/facelessuser/pymdown-extensions/compare/10.8.1...10.9)

---
updated-dependencies:
- dependency-name: pymdown-extensions
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-29 05:32:28 +00:00
dependabot[bot]
a1490d07b4 chore(deps-dev): bump ruff from 0.5.4 to 0.5.5
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.5.4 to 0.5.5.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/0.5.4...0.5.5)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-29 05:32:28 +00:00
Matthias
08144382b8 Merge pull request #10466 from freqtrade/dependabot/pip/develop/mkdocs-1c45c0a9b7
chore(deps): bump mkdocs-material from 9.5.29 to 9.5.30 in the mkdocs group
2024-07-29 07:31:51 +02:00
Matthias
cb90e1388f Merge pull request #10465 from freqtrade/dependabot/pip/develop/pytest-d2133643f0
chore(deps-dev): bump pytest from 8.3.1 to 8.3.2 in the pytest group
2024-07-29 07:31:37 +02:00
Matthias
5e1038dc67 chore: Fix torch version bump 2024-07-29 07:00:17 +02:00
dependabot[bot]
5e852ebb5d chore(deps): bump lightgbm from 4.4.0 to 4.5.0
Bumps [lightgbm](https://github.com/microsoft/LightGBM) from 4.4.0 to 4.5.0.
- [Release notes](https://github.com/microsoft/LightGBM/releases)
- [Commits](https://github.com/microsoft/LightGBM/compare/v4.4.0...v4.5.0)

---
updated-dependencies:
- dependency-name: lightgbm
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-29 03:08:24 +00:00
dependabot[bot]
097786c62d chore(deps): bump plotly from 5.22.0 to 5.23.0
Bumps [plotly](https://github.com/plotly/plotly.py) from 5.22.0 to 5.23.0.
- [Release notes](https://github.com/plotly/plotly.py/releases)
- [Changelog](https://github.com/plotly/plotly.py/blob/master/CHANGELOG.md)
- [Commits](https://github.com/plotly/plotly.py/compare/v5.22.0...v5.23.0)

---
updated-dependencies:
- dependency-name: plotly
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-29 03:08:18 +00:00
dependabot[bot]
baeced32c3 chore(deps): bump python-rapidjson from 1.18 to 1.19
Bumps [python-rapidjson](https://github.com/python-rapidjson/python-rapidjson) from 1.18 to 1.19.
- [Changelog](https://github.com/python-rapidjson/python-rapidjson/blob/master/CHANGES.rst)
- [Commits](https://github.com/python-rapidjson/python-rapidjson/compare/v1.18...v1.19)

---
updated-dependencies:
- dependency-name: python-rapidjson
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-29 03:08:10 +00:00
dependabot[bot]
2f4e4343c2 chore(deps): bump torch from 2.2.2 to 2.4.0
Bumps [torch](https://github.com/pytorch/pytorch) from 2.2.2 to 2.4.0.
- [Release notes](https://github.com/pytorch/pytorch/releases)
- [Changelog](https://github.com/pytorch/pytorch/blob/main/RELEASE.md)
- [Commits](https://github.com/pytorch/pytorch/compare/v2.2.2...v2.4.0)

---
updated-dependencies:
- dependency-name: torch
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-29 03:07:44 +00:00
dependabot[bot]
9fd6d7318e chore(deps): bump ccxt from 4.3.65 to 4.3.68
Bumps [ccxt](https://github.com/ccxt/ccxt) from 4.3.65 to 4.3.68.
- [Release notes](https://github.com/ccxt/ccxt/releases)
- [Changelog](https://github.com/ccxt/ccxt/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ccxt/ccxt/compare/4.3.65...4.3.68)

---
updated-dependencies:
- dependency-name: ccxt
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-29 03:07:22 +00:00
dependabot[bot]
fd2be958ba chore(deps): bump mkdocs-material in the mkdocs group
Bumps the mkdocs group with 1 update: [mkdocs-material](https://github.com/squidfunk/mkdocs-material).


Updates `mkdocs-material` from 9.5.29 to 9.5.30
- [Release notes](https://github.com/squidfunk/mkdocs-material/releases)
- [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG)
- [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.5.29...9.5.30)

---
updated-dependencies:
- dependency-name: mkdocs-material
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: mkdocs
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-29 03:06:12 +00:00
dependabot[bot]
719889b27a chore(deps-dev): bump pytest from 8.3.1 to 8.3.2 in the pytest group
Bumps the pytest group with 1 update: [pytest](https://github.com/pytest-dev/pytest).


Updates `pytest` from 8.3.1 to 8.3.2
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/8.3.1...8.3.2)

---
updated-dependencies:
- dependency-name: pytest
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: pytest
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-29 03:05:45 +00:00
Matthias
faaa1050da chore: Bump dev version to 2024.8 2024-07-28 20:10:30 +02:00
jainanuj94
27aed5cd7e Update schema.json 2024-07-28 22:34:34 +05:30
jainanuj94
aa327643f5 Merge remote-tracking branch 'upstream/develop' into feature/10348 2024-07-28 22:26:58 +05:30
jainanuj94
3a481df45d Merge remote-tracking branch 'upstream/develop' into develop 2024-07-28 22:26:00 +05:30
jainanuj94
ac1e405c34 Update documentation and fix doc test 2024-07-28 21:10:20 +05:30
jainanuj94
4932473b3f Add documentation 2024-07-27 23:41:32 +05:30
Matthias
206baf7d80 chore: add a bit of typehinting 2024-07-27 16:13:17 +02:00
Matthias
4ac7a4fdab Allow empty min_Value setting... 2024-07-27 16:07:51 +02:00
Matthias
283e8045d8 PercentChangePairlist should partecipate in regular tests 2024-07-27 16:05:59 +02:00
Matthias
8637f4a70d Remove SortKey dynamics and setting 2024-07-27 16:04:51 +02:00
jainanuj94
4a768682ea Remove unnecessary logs and up description 2024-07-26 13:13:26 +05:30
jainanuj94
dad4f30597 Correct calculation for percent calculation and use tickers 2024-07-25 23:33:28 +05:30
jainanuj94
1b81de01b4 10348 | run ruff formatter 2024-07-25 00:04:06 +05:30
jainanuj94
b09f9e8c12 10348 | Update tests and add pairlist constants 2024-07-24 19:12:11 +05:30
jainanuj94
4b1177e07e 10348 | Create new pair list to dynamically fetch pairs based on percent volume change 2024-07-24 19:09:45 +05:30
simwai
f714d1ab28 Added unlock_at field to protections document 2024-07-18 15:08:12 +02:00
Matthias
dcc9d20cca Remove unnecessary statement 2024-07-16 07:31:11 +02:00
Matthias
d590ab003f Add unlock_at config test, simplify validation 2024-07-16 07:26:41 +02:00
Matthias
a3c52445ee Simplify validation 2024-07-16 07:14:46 +02:00
Matthias
be3fcd90e2 Remove unneeded property 2024-07-16 07:14:33 +02:00
Matthias
26aa336450 Combine "until" logic into calculate_lock_end 2024-07-16 07:05:42 +02:00
Matthias
65972d9c0c Add cooldown with timeperiod test 2024-07-16 06:51:31 +02:00
Matthias
d13f47ec0b align wording to simplify "locking for" element 2024-07-16 06:48:30 +02:00
Matthias
1e36bc98b9 chore: Remove unused method 2024-07-16 06:35:32 +02:00
Matthias
16dd86e732 _unlock_at should be private 2024-07-16 06:31:12 +02:00
simwai
be894664ef Fixed building of wrong reason texts
Removed unnecessary method set_unlock_at_as_stop_duration()
2024-07-14 21:47:43 +02:00
Simon Waiblinger
f126120421 Merge branch 'freqtrade:develop' into feature/stoploss-start-at 2024-07-05 22:23:56 +02:00
simwai
af505b346c Fixed an access on the config by a wrong config key 2024-07-05 22:17:40 +02:00
simwai
77b4689ac8 Fixed implementation of unlock_at and updated unit tests 2024-07-05 22:14:35 +02:00
simwai
57118691d8 Removed entry in gitignore 2024-07-04 10:53:14 +02:00
simwai
2b456cbdeb Added unlock_at field for protection config 2024-07-04 10:29:13 +02:00
Simon Waiblinger
1d3ca5743b Merge branch 'freqtrade:develop' into develop 2024-06-27 16:38:13 +02:00
Simon Waiblinger
5f0a27d355 Merge branch 'freqtrade:develop' into develop 2024-06-02 15:38:56 +02:00
Simon Waiblinger
42d0f342b2 Merge branch 'freqtrade:develop' into develop 2024-05-30 10:10:03 +02:00
Simon Waiblinger
c12adea655 Merge branch 'freqtrade:develop' into develop 2024-05-26 15:59:29 +02:00
Simon Waiblinger
2a82e00857 Merge branch 'freqtrade:develop' into develop 2024-05-24 20:47:41 +02:00
192 changed files with 12462 additions and 4866 deletions

View File

@@ -1,9 +1,14 @@
version: 2 version: 2
updates: updates:
- package-ecosystem: docker - package-ecosystem: docker
directory: "/" directories:
- "/"
- "/docker"
schedule: schedule:
interval: daily interval: daily
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-major"]
open-pull-requests-limit: 10 open-pull-requests-limit: 10
- package-ecosystem: pip - package-ecosystem: pip

View File

@@ -32,7 +32,7 @@ jobs:
run: python build_helpers/binance_update_lev_tiers.py run: python build_helpers/binance_update_lev_tiers.py
- uses: peter-evans/create-pull-request@v6 - uses: peter-evans/create-pull-request@v7
with: with:
token: ${{ secrets.REPO_SCOPED_TOKEN }} token: ${{ secrets.REPO_SCOPED_TOKEN }}
add-paths: freqtrade/exchange/binance_leverage_tiers.json add-paths: freqtrade/exchange/binance_leverage_tiers.json

View File

@@ -55,7 +55,7 @@ jobs:
- name: Installation - *nix - name: Installation - *nix
run: | run: |
python -m pip install --upgrade "pip<=24.0" wheel python -m pip install --upgrade pip wheel
export LD_LIBRARY_PATH=${HOME}/dependencies/lib:$LD_LIBRARY_PATH export LD_LIBRARY_PATH=${HOME}/dependencies/lib:$LD_LIBRARY_PATH
export TA_LIBRARY_PATH=${HOME}/dependencies/lib export TA_LIBRARY_PATH=${HOME}/dependencies/lib
export TA_INCLUDE_PATH=${HOME}/dependencies/include export TA_INCLUDE_PATH=${HOME}/dependencies/include
@@ -197,7 +197,7 @@ jobs:
- name: Installation (python) - name: Installation (python)
run: | run: |
python -m pip install --upgrade "pip<=24.0" wheel python -m pip install --upgrade pip wheel
export LD_LIBRARY_PATH=${HOME}/dependencies/lib:$LD_LIBRARY_PATH export LD_LIBRARY_PATH=${HOME}/dependencies/lib:$LD_LIBRARY_PATH
export TA_LIBRARY_PATH=${HOME}/dependencies/lib export TA_LIBRARY_PATH=${HOME}/dependencies/lib
export TA_INCLUDE_PATH=${HOME}/dependencies/include export TA_INCLUDE_PATH=${HOME}/dependencies/include
@@ -384,7 +384,6 @@ jobs:
- name: Documentation build - name: Documentation build
run: | run: |
pip install -r docs/requirements-docs.txt pip install -r docs/requirements-docs.txt
pip install mkdocs
mkdocs build mkdocs build
- name: Discord notification - name: Discord notification
@@ -427,7 +426,7 @@ jobs:
- name: Installation - *nix - name: Installation - *nix
run: | run: |
python -m pip install --upgrade "pip<=24.0" wheel python -m pip install --upgrade pip wheel
export LD_LIBRARY_PATH=${HOME}/dependencies/lib:$LD_LIBRARY_PATH export LD_LIBRARY_PATH=${HOME}/dependencies/lib:$LD_LIBRARY_PATH
export TA_LIBRARY_PATH=${HOME}/dependencies/lib export TA_LIBRARY_PATH=${HOME}/dependencies/lib
export TA_INCLUDE_PATH=${HOME}/dependencies/include export TA_INCLUDE_PATH=${HOME}/dependencies/include
@@ -538,12 +537,12 @@ jobs:
- name: Publish to PyPI (Test) - name: Publish to PyPI (Test)
uses: pypa/gh-action-pypi-publish@v1.9.0 uses: pypa/gh-action-pypi-publish@v1.10.2
with: with:
repository-url: https://test.pypi.org/legacy/ repository-url: https://test.pypi.org/legacy/
- name: Publish to PyPI - name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@v1.9.0 uses: pypa/gh-action-pypi-publish@v1.10.2
deploy-docker: deploy-docker:

55
.github/workflows/deploy-docs.yml vendored Normal file
View File

@@ -0,0 +1,55 @@
name: Build Documentation
on:
push:
branches:
- develop
release:
types: [published]
# disable permissions for all of the available permissions
permissions: {}
jobs:
build-docs:
permissions:
contents: write # for mike to push
name: Deploy Docs through mike
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r docs/requirements-docs.txt
- name: Fetch gh-pages branch
run: |
git fetch origin gh-pages --depth=1
- name: Configure Git user
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
- name: Build and push Mike
if: ${{ github.event_name == 'push' }}
run: |
mike deploy ${{ github.ref_name }} latest --push --update-aliases
- name: Build and push Mike - Release
if: ${{ github.event_name == 'release' }}
run: |
mike deploy ${{ github.ref_name }} stable --push --update-aliases
- name: Show mike versions
run: |
mike list

View File

@@ -26,7 +26,7 @@ jobs:
- name: Run auto-update - name: Run auto-update
run: pre-commit autoupdate run: pre-commit autoupdate
- uses: peter-evans/create-pull-request@v6 - uses: peter-evans/create-pull-request@v7
with: with:
token: ${{ secrets.REPO_SCOPED_TOKEN }} token: ${{ secrets.REPO_SCOPED_TOKEN }}
add-paths: .pre-commit-config.yaml add-paths: .pre-commit-config.yaml

2
.gitignore vendored
View File

@@ -114,3 +114,5 @@ target/
!config_examples/config_full.example.json !config_examples/config_full.example.json
!config_examples/config_kraken.example.json !config_examples/config_kraken.example.json
!config_examples/config_freqai.example.json !config_examples/config_freqai.example.json
docker-compose-*.yml

View File

@@ -2,24 +2,24 @@
# See https://pre-commit.com/hooks.html for more hooks # See https://pre-commit.com/hooks.html for more hooks
repos: repos:
- repo: https://github.com/pycqa/flake8 - repo: https://github.com/pycqa/flake8
rev: "7.1.0" rev: "7.1.1"
hooks: hooks:
- id: flake8 - id: flake8
additional_dependencies: [Flake8-pyproject] additional_dependencies: [Flake8-pyproject]
# stages: [push] # stages: [push]
- repo: https://github.com/pre-commit/mirrors-mypy - repo: https://github.com/pre-commit/mirrors-mypy
rev: "v1.11.0" rev: "v1.11.2"
hooks: hooks:
- id: mypy - id: mypy
exclude: build_helpers exclude: build_helpers
additional_dependencies: additional_dependencies:
- types-cachetools==5.4.0.20240717 - types-cachetools==5.5.0.20240820
- types-filelock==3.2.7 - types-filelock==3.2.7
- types-requests==2.32.0.20240712 - types-requests==2.32.0.20240914
- types-tabulate==0.9.0.20240106 - types-tabulate==0.9.0.20240106
- types-python-dateutil==2.9.0.20240316 - types-python-dateutil==2.9.0.20240906
- SQLAlchemy==2.0.31 - SQLAlchemy==2.0.35
# stages: [push] # stages: [push]
- repo: https://github.com/pycqa/isort - repo: https://github.com/pycqa/isort
@@ -31,9 +31,10 @@ repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit - repo: https://github.com/charliermarsh/ruff-pre-commit
# Ruff version. # Ruff version.
rev: 'v0.5.4' rev: 'v0.6.7'
hooks: hooks:
- id: ruff - id: ruff
- id: ruff-format
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0 rev: v4.6.0

View File

@@ -1,4 +1,4 @@
FROM python:3.12.4-slim-bookworm as base FROM python:3.12.6-slim-bookworm as base
# Setup env # Setup env
ENV LANG C.UTF-8 ENV LANG C.UTF-8
@@ -25,7 +25,7 @@ FROM base as python-deps
RUN apt-get update \ RUN apt-get update \
&& apt-get -y install build-essential libssl-dev git libffi-dev libgfortran5 pkg-config cmake gcc \ && apt-get -y install build-essential libssl-dev git libffi-dev libgfortran5 pkg-config cmake gcc \
&& apt-get clean \ && apt-get clean \
&& pip install --upgrade "pip<=24.0" wheel && pip install --upgrade pip wheel
# Install TA-lib # Install TA-lib
COPY build_helpers/* /tmp/ COPY build_helpers/* /tmp/

View File

@@ -30,6 +30,7 @@ Please read the [exchange specific notes](docs/exchanges.md) to learn about even
- [X] [Binance](https://www.binance.com/) - [X] [Binance](https://www.binance.com/)
- [X] [Bitmart](https://bitmart.com/) - [X] [Bitmart](https://bitmart.com/)
- [X] [BingX](https://bingx.com/invite/0EM9RX) - [X] [BingX](https://bingx.com/invite/0EM9RX)
- [X] [Bybit](https://bybit.com/)
- [X] [Gate.io](https://www.gate.io/ref/6266643) - [X] [Gate.io](https://www.gate.io/ref/6266643)
- [X] [HTX](https://www.htx.com/) (Former Huobi) - [X] [HTX](https://www.htx.com/) (Former Huobi)
- [X] [Kraken](https://kraken.com/) - [X] [Kraken](https://kraken.com/)
@@ -86,41 +87,50 @@ For further (native) installation methods, please refer to the [Installation doc
``` ```
usage: freqtrade [-h] [-V] usage: freqtrade [-h] [-V]
{trade,create-userdir,new-config,new-strategy,download-data,convert-data,convert-trade-data,list-data,backtesting,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-hyperopts,list-markets,list-pairs,list-strategies,list-timeframes,show-trades,test-pairlist,install-ui,plot-dataframe,plot-profit,webserver} {trade,create-userdir,new-config,show-config,new-strategy,download-data,convert-data,convert-trade-data,trades-to-ohlcv,list-data,backtesting,backtesting-show,backtesting-analysis,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-markets,list-pairs,list-strategies,list-freqaimodels,list-timeframes,show-trades,test-pairlist,convert-db,install-ui,plot-dataframe,plot-profit,webserver,strategy-updater,lookahead-analysis,recursive-analysis}
... ...
Free, open source crypto trading bot Free, open source crypto trading bot
positional arguments: positional arguments:
{trade,create-userdir,new-config,new-strategy,download-data,convert-data,convert-trade-data,list-data,backtesting,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-hyperopts,list-markets,list-pairs,list-strategies,list-timeframes,show-trades,test-pairlist,install-ui,plot-dataframe,plot-profit,webserver} {trade,create-userdir,new-config,show-config,new-strategy,download-data,convert-data,convert-trade-data,trades-to-ohlcv,list-data,backtesting,backtesting-show,backtesting-analysis,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-markets,list-pairs,list-strategies,list-freqaimodels,list-timeframes,show-trades,test-pairlist,convert-db,install-ui,plot-dataframe,plot-profit,webserver,strategy-updater,lookahead-analysis,recursive-analysis}
trade Trade module. trade Trade module.
create-userdir Create user-data directory. create-userdir Create user-data directory.
new-config Create new config new-config Create new config
show-config Show resolved config
new-strategy Create new strategy new-strategy Create new strategy
download-data Download backtesting data. download-data Download backtesting data.
convert-data Convert candle (OHLCV) data from one format to convert-data Convert candle (OHLCV) data from one format to
another. another.
convert-trade-data Convert trade data from one format to another. convert-trade-data Convert trade data from one format to another.
trades-to-ohlcv Convert trade data to OHLCV data.
list-data List downloaded data. list-data List downloaded data.
backtesting Backtesting module. backtesting Backtesting module.
backtesting-show Show past Backtest results
backtesting-analysis
Backtest Analysis module.
edge Edge module. edge Edge module.
hyperopt Hyperopt module. hyperopt Hyperopt module.
hyperopt-list List Hyperopt results hyperopt-list List Hyperopt results
hyperopt-show Show details of Hyperopt results hyperopt-show Show details of Hyperopt results
list-exchanges Print available exchanges. list-exchanges Print available exchanges.
list-hyperopts Print available hyperopt classes.
list-markets Print markets on exchange. list-markets Print markets on exchange.
list-pairs Print pairs on exchange. list-pairs Print pairs on exchange.
list-strategies Print available strategies. list-strategies Print available strategies.
list-freqaimodels Print available freqAI models.
list-timeframes Print available timeframes for the exchange. list-timeframes Print available timeframes for the exchange.
show-trades Show trades. show-trades Show trades.
test-pairlist Test your pairlist configuration. test-pairlist Test your pairlist configuration.
convert-db Migrate database to different system
install-ui Install FreqUI install-ui Install FreqUI
plot-dataframe Plot candles with indicators. plot-dataframe Plot candles with indicators.
plot-profit Generate plot showing profits. plot-profit Generate plot showing profits.
webserver Webserver module. webserver Webserver module.
strategy-updater updates outdated strategy files to the current version
lookahead-analysis Check for potential look ahead bias.
recursive-analysis Check for potential recursive formula issue.
optional arguments: options:
-h, --help show this help message and exit -h, --help show this help message and exit
-V, --version show program's version number and exit -V, --version show program's version number and exit

View File

@@ -1,6 +1,6 @@
# vendored Wheels compiled via https://github.com/xmatthias/ta-lib-python/tree/ta_bundled_040 # vendored Wheels compiled via https://github.com/xmatthias/ta-lib-python/tree/ta_bundled_040
python -m pip install --upgrade "pip<=24.0" wheel python -m pip install --upgrade pip wheel
$pyv = python -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')" $pyv = python -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')"

View File

@@ -9,11 +9,6 @@
], ],
"minimum": -1 "minimum": -1
}, },
"new_pairs_days": {
"description": "Download data of new pairs for given number of days",
"type": "integer",
"default": 30
},
"timeframe": { "timeframe": {
"description": "The timeframe to use (e.g `1m`, `5m`, `15m`, `30m`, `1h` ...). \nUsually specified in the strategy and missing in the configuration.", "description": "The timeframe to use (e.g `1m`, `5m`, `15m`, `30m`, `1h` ...). \nUsually specified in the strategy and missing in the configuration.",
"type": "string" "type": "string"
@@ -562,6 +557,7 @@
"enum": [ "enum": [
"StaticPairList", "StaticPairList",
"VolumePairList", "VolumePairList",
"PercentChangePairList",
"ProducerPairList", "ProducerPairList",
"RemotePairList", "RemotePairList",
"MarketCapPairList", "MarketCapPairList",
@@ -609,6 +605,10 @@
"type": "number", "type": "number",
"minimum": 0 "minimum": 0
}, },
"unlock_at": {
"description": "Time when trading will be unlocked regularly. Format: HH:MM",
"type": "string"
},
"trade_limit": { "trade_limit": {
"description": "Minimum number of trades required during lookback period.", "description": "Minimum number of trades required during lookback period.",
"type": "number", "type": "number",
@@ -1064,7 +1064,7 @@
"default": {}, "default": {},
"properties": { "properties": {
"process_throttle_secs": { "process_throttle_secs": {
"description": "Throttle time in seconds for processing.", "description": "Minimum loop duration for one bot iteration in seconds.",
"type": "integer" "type": "integer"
}, },
"interval": { "interval": {
@@ -1105,6 +1105,15 @@
"description": "Enable position adjustment. \nUsually specified in the strategy and missing in the configuration.", "description": "Enable position adjustment. \nUsually specified in the strategy and missing in the configuration.",
"type": "boolean" "type": "boolean"
}, },
"new_pairs_days": {
"description": "Download data of new pairs for given number of days",
"type": "integer",
"default": 30
},
"download_trades": {
"description": "Download trades data by default (instead of ohlcv data).",
"type": "boolean"
},
"max_entry_position_adjustment": { "max_entry_position_adjustment": {
"description": "Maximum entry position adjustment allowed. \nUsually specified in the strategy and missing in the configuration.", "description": "Maximum entry position adjustment allowed. \nUsually specified in the strategy and missing in the configuration.",
"type": [ "type": [
@@ -1113,6 +1122,13 @@
], ],
"minimum": -1 "minimum": -1
}, },
"add_config_files": {
"description": "Additional configuration files to load.",
"type": "array",
"items": {
"type": "string"
}
},
"orderflow": { "orderflow": {
"description": "Settings related to order flow.", "description": "Settings related to order flow.",
"type": "object", "type": "object",
@@ -1208,6 +1224,11 @@
}, },
"uniqueItems": true "uniqueItems": true
}, },
"log_responses": {
"description": "Log responses from the exchange.Useful/required to debug issues with order processing.",
"type": "boolean",
"default": false
},
"unknown_fee_rate": { "unknown_fee_rate": {
"description": "Fee rate for unknown markets.", "description": "Fee rate for unknown markets.",
"type": "number" "type": "number"

View File

@@ -1,4 +1,5 @@
{ {
"$schema": "https://schema.freqtrade.io/schema.json",
"max_open_trades": 3, "max_open_trades": 3,
"stake_currency": "USDT", "stake_currency": "USDT",
"stake_amount": 0.05, "stake_amount": 0.05,

View File

@@ -1,4 +1,5 @@
{ {
"$schema": "https://schema.freqtrade.io/schema.json",
"trading_mode": "futures", "trading_mode": "futures",
"margin_mode": "isolated", "margin_mode": "isolated",
"max_open_trades": 5, "max_open_trades": 5,

View File

@@ -1,4 +1,5 @@
{ {
"$schema": "https://schema.freqtrade.io/schema.json",
"max_open_trades": 3, "max_open_trades": 3,
"stake_currency": "BTC", "stake_currency": "BTC",
"stake_amount": 0.05, "stake_amount": 0.05,

View File

@@ -1,4 +1,5 @@
{ {
"$schema": "https://schema.freqtrade.io/schema.json",
"max_open_trades": 5, "max_open_trades": 5,
"stake_currency": "EUR", "stake_currency": "EUR",
"stake_amount": 10, "stake_amount": 10,

View File

@@ -1,4 +1,4 @@
FROM python:3.11.8-slim-bookworm as base FROM python:3.11.10-slim-bookworm as base
# Setup env # Setup env
ENV LANG C.UTF-8 ENV LANG C.UTF-8
@@ -17,7 +17,7 @@ RUN mkdir /freqtrade \
&& chown ftuser:ftuser /freqtrade \ && chown ftuser:ftuser /freqtrade \
# Allow sudoers # Allow sudoers
&& echo "ftuser ALL=(ALL) NOPASSWD: /bin/chown" >> /etc/sudoers \ && echo "ftuser ALL=(ALL) NOPASSWD: /bin/chown" >> /etc/sudoers \
&& pip install --upgrade "pip<=24.0" && pip install --upgrade pip
WORKDIR /freqtrade WORKDIR /freqtrade

View File

@@ -18,15 +18,13 @@ freqtrade backtesting -c <config.json> --timeframe <tf> --strategy <strategy_nam
``` ```
This will tell freqtrade to output a pickled dictionary of strategy, pairs and corresponding This will tell freqtrade to output a pickled dictionary of strategy, pairs and corresponding
DataFrame of the candles that resulted in buy signals. Depending on how many buys your strategy DataFrame of the candles that resulted in entry and exit signals.
makes, this file may get quite large, so periodically check your `user_data/backtest_results` Depending on how many entries your strategy makes, this file may get quite large, so periodically check your `user_data/backtest_results` folder to delete old exports.
folder to delete old exports.
Before running your next backtest, make sure you either delete your old backtest results or run Before running your next backtest, make sure you either delete your old backtest results or run
backtesting with the `--cache none` option to make sure no cached results are used. backtesting with the `--cache none` option to make sure no cached results are used.
If all goes well, you should now see a `backtest-result-{timestamp}_signals.pkl` file in the If all goes well, you should now see a `backtest-result-{timestamp}_signals.pkl` and `backtest-result-{timestamp}_exited.pkl` files in the `user_data/backtest_results` folder.
`user_data/backtest_results` folder.
To analyze the entry/exit tags, we now need to use the `freqtrade backtesting-analysis` command To analyze the entry/exit tags, we now need to use the `freqtrade backtesting-analysis` command
with `--analysis-groups` option provided with space-separated arguments: with `--analysis-groups` option provided with space-separated arguments:
@@ -103,6 +101,10 @@ The indicators have to be present in your strategy's main DataFrame (either for
timeframe or for informative timeframes) otherwise they will simply be ignored in the script timeframe or for informative timeframes) otherwise they will simply be ignored in the script
output. output.
!!! Note "Indicator List"
The indicator values will be displayed for both entry and exit points. If `--indicator-list all` is specified,
only the indicators at the entry point will be shown to avoid excessively large lists, which could occur depending on the strategy.
There are a range of candle and trade-related fields that are included in the analysis so are There are a range of candle and trade-related fields that are included in the analysis so are
automatically accessible by including them on the indicator-list, and these include: automatically accessible by including them on the indicator-list, and these include:
@@ -118,6 +120,53 @@ automatically accessible by including them on the indicator-list, and these incl
- **profit_ratio :** trade profit ratio - **profit_ratio :** trade profit ratio
- **profit_abs :** absolute profit return of the trade - **profit_abs :** absolute profit return of the trade
#### Sample Output for Indicator Values
```bash
freqtrade backtesting-analysis -c user_data/config.json --analysis-groups 0 --indicator-list chikou_span tenkan_sen
```
In this example,
we aim to display the `chikou_span` and `tenkan_sen` indicator values at both the entry and exit points of trades.
A sample output for indicators might look like this:
| pair | open_date | enter_reason | exit_reason | chikou_span (entry) | tenkan_sen (entry) | chikou_span (exit) | tenkan_sen (exit) |
|-----------|---------------------------|--------------|-------------|---------------------|--------------------|--------------------|-------------------|
| DOGE/USDT | 2024-07-06 00:35:00+00:00 | | exit_signal | 0.105 | 0.106 | 0.105 | 0.107 |
| BTC/USDT | 2024-08-05 14:20:00+00:00 | | roi | 54643.440 | 51696.400 | 54386.000 | 52072.010 |
As shown in the table, `chikou_span (entry)` represents the indicator value at the time of trade entry,
while `chikou_span (exit)` reflects its value at the time of exit.
This detailed view of indicator values enhances the analysis.
The `(entry)` and `(exit)` suffixes are added to indicators
to distinguish the values at the entry and exit points of the trade.
!!! Note "Trade-wide Indicators"
Certain trade-wide indicators do not have the `(entry)` or `(exit)` suffix. These indicators include: `pair`, `stake_amount`,
`max_stake_amount`, `amount`, `open_date`, `close_date`, `open_rate`, `close_rate`, `fee_open`, `fee_close`, `trade_duration`,
`profit_ratio`, `profit_abs`, `exit_reason`,`initial_stop_loss_abs`, `initial_stop_loss_ratio`, `stop_loss_abs`, `stop_loss_ratio`,
`min_rate`, `max_rate`, `is_open`, `enter_tag`, `leverage`, `is_short`, `open_timestamp`, `close_timestamp` and `orders`
#### Filtering Indicators Based on Entry or Exit Signals
The `--indicator-list` option, by default, displays indicator values for both entry and exit signals. To filter the indicator values exclusively for entry signals, you can use the `--entry-only` argument. Similarly, to display indicator values only at exit signals, use the `--exit-only` argument.
Example: Display indicator values at entry signals:
```bash
freqtrade backtesting-analysis -c user_data/config.json --analysis-groups 0 --indicator-list chikou_span tenkan_sen --entry-only
```
Example: Display indicator values at exit signals:
```bash
freqtrade backtesting-analysis -c user_data/config.json --analysis-groups 0 --indicator-list chikou_span tenkan_sen --exit-only
```
!!! note
When using these filters, the indicator names will not be suffixed with `(entry)` or `(exit)`.
### Filtering the trade output by date ### Filtering the trade output by date

View File

@@ -30,11 +30,17 @@ class SuperDuperHyperOptLoss(IHyperOptLoss):
""" """
@staticmethod @staticmethod
def hyperopt_loss_function(results: DataFrame, trade_count: int, def hyperopt_loss_function(
min_date: datetime, max_date: datetime, *,
config: Config, processed: Dict[str, DataFrame], results: DataFrame,
backtest_stats: Dict[str, Any], trade_count: int,
*args, **kwargs) -> float: min_date: datetime,
max_date: datetime,
config: Config,
processed: Dict[str, DataFrame],
backtest_stats: Dict[str, Any],
**kwargs,
) -> float:
""" """
Objective function, returns smaller number for better results Objective function, returns smaller number for better results
This is the legacy algorithm (used until now in freqtrade). This is the legacy algorithm (used until now in freqtrade).

View File

@@ -293,6 +293,7 @@ A backtesting result will look like that:
|-----------------------------+---------------------| |-----------------------------+---------------------|
| Backtesting from | 2019-01-01 00:00:00 | | Backtesting from | 2019-01-01 00:00:00 |
| Backtesting to | 2019-05-01 00:00:00 | | Backtesting to | 2019-05-01 00:00:00 |
| Trading Mode | Spot |
| Max open trades | 3 | | Max open trades | 3 |
| | | | | |
| Total/Daily Avg Trades | 429 / 3.575 | | Total/Daily Avg Trades | 429 / 3.575 |
@@ -398,6 +399,7 @@ It contains some useful key metrics about performance of your strategy on backte
|-----------------------------+---------------------| |-----------------------------+---------------------|
| Backtesting from | 2019-01-01 00:00:00 | | Backtesting from | 2019-01-01 00:00:00 |
| Backtesting to | 2019-05-01 00:00:00 | | Backtesting to | 2019-05-01 00:00:00 |
| Trading Mode | Spot |
| Max open trades | 3 | | Max open trades | 3 |
| | | | | |
| Total/Daily Avg Trades | 429 / 3.575 | | Total/Daily Avg Trades | 429 / 3.575 |
@@ -452,6 +454,7 @@ It contains some useful key metrics about performance of your strategy on backte
- `Backtesting from` / `Backtesting to`: Backtesting range (usually defined with the `--timerange` option). - `Backtesting from` / `Backtesting to`: Backtesting range (usually defined with the `--timerange` option).
- `Max open trades`: Setting of `max_open_trades` (or `--max-open-trades`) - or number of pairs in the pairlist (whatever is lower). - `Max open trades`: Setting of `max_open_trades` (or `--max-open-trades`) - or number of pairs in the pairlist (whatever is lower).
- `Trading Mode`: Spot or Futures trading.
- `Total/Daily Avg Trades`: Identical to the total trades of the backtest output table / Total trades divided by the backtesting duration in days (this will give you information about how many trades to expect from the strategy). - `Total/Daily Avg Trades`: Identical to the total trades of the backtest output table / Total trades divided by the backtesting duration in days (this will give you information about how many trades to expect from the strategy).
- `Starting balance`: Start balance - as given by dry-run-wallet (config or command line). - `Starting balance`: Start balance - as given by dry-run-wallet (config or command line).
- `Final balance`: Final balance - starting balance + absolute profit. - `Final balance`: Final balance - starting balance + absolute profit.
@@ -530,10 +533,10 @@ 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
- Entries happen at open-price - Entries happen at open-price unless a custom price logic has been specified
- All orders are filled at the requested price (no slippage) as long as the price is within the candle's high/low range - All orders are filled at the requested price (no slippage) as long as the price is within the candle's high/low range
- Exit-signal exits happen at open-price of the consecutive candle - Exit-signal exits happen at open-price of the consecutive candle
- Exits don't free their trade slot for a new trade until the next candle - Exits free their trade slot for a new trade with a different pair
- 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
- ROI - ROI
- Exits are compared to high - but the ROI value is used (e.g. ROI = 2%, high=5% - so the exit will be at 2%) - Exits are compared to high - but the ROI value is used (e.g. ROI = 2%, high=5% - so the exit will be at 2%)

View File

@@ -12,41 +12,50 @@ This page explains the different parameters of the bot and how to run it.
``` ```
usage: freqtrade [-h] [-V] usage: freqtrade [-h] [-V]
{trade,create-userdir,new-config,new-strategy,download-data,convert-data,convert-trade-data,list-data,backtesting,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-hyperopts,list-markets,list-pairs,list-strategies,list-timeframes,show-trades,test-pairlist,install-ui,plot-dataframe,plot-profit,webserver} {trade,create-userdir,new-config,show-config,new-strategy,download-data,convert-data,convert-trade-data,trades-to-ohlcv,list-data,backtesting,backtesting-show,backtesting-analysis,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-markets,list-pairs,list-strategies,list-freqaimodels,list-timeframes,show-trades,test-pairlist,convert-db,install-ui,plot-dataframe,plot-profit,webserver,strategy-updater,lookahead-analysis,recursive-analysis}
... ...
Free, open source crypto trading bot Free, open source crypto trading bot
positional arguments: positional arguments:
{trade,create-userdir,new-config,new-strategy,download-data,convert-data,convert-trade-data,list-data,backtesting,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-hyperopts,list-markets,list-pairs,list-strategies,list-timeframes,show-trades,test-pairlist,install-ui,plot-dataframe,plot-profit,webserver} {trade,create-userdir,new-config,show-config,new-strategy,download-data,convert-data,convert-trade-data,trades-to-ohlcv,list-data,backtesting,backtesting-show,backtesting-analysis,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-markets,list-pairs,list-strategies,list-freqaimodels,list-timeframes,show-trades,test-pairlist,convert-db,install-ui,plot-dataframe,plot-profit,webserver,strategy-updater,lookahead-analysis,recursive-analysis}
trade Trade module. trade Trade module.
create-userdir Create user-data directory. create-userdir Create user-data directory.
new-config Create new config new-config Create new config
show-config Show resolved config
new-strategy Create new strategy new-strategy Create new strategy
download-data Download backtesting data. download-data Download backtesting data.
convert-data Convert candle (OHLCV) data from one format to convert-data Convert candle (OHLCV) data from one format to
another. another.
convert-trade-data Convert trade data from one format to another. convert-trade-data Convert trade data from one format to another.
trades-to-ohlcv Convert trade data to OHLCV data.
list-data List downloaded data. list-data List downloaded data.
backtesting Backtesting module. backtesting Backtesting module.
backtesting-show Show past Backtest results
backtesting-analysis
Backtest Analysis module.
edge Edge module. edge Edge module.
hyperopt Hyperopt module. hyperopt Hyperopt module.
hyperopt-list List Hyperopt results hyperopt-list List Hyperopt results
hyperopt-show Show details of Hyperopt results hyperopt-show Show details of Hyperopt results
list-exchanges Print available exchanges. list-exchanges Print available exchanges.
list-hyperopts Print available hyperopt classes.
list-markets Print markets on exchange. list-markets Print markets on exchange.
list-pairs Print pairs on exchange. list-pairs Print pairs on exchange.
list-strategies Print available strategies. list-strategies Print available strategies.
list-freqaimodels Print available freqAI models.
list-timeframes Print available timeframes for the exchange. list-timeframes Print available timeframes for the exchange.
show-trades Show trades. show-trades Show trades.
test-pairlist Test your pairlist configuration. test-pairlist Test your pairlist configuration.
convert-db Migrate database to different system
install-ui Install FreqUI install-ui Install FreqUI
plot-dataframe Plot candles with indicators. plot-dataframe Plot candles with indicators.
plot-profit Generate plot showing profits. plot-profit Generate plot showing profits.
webserver Webserver module. webserver Webserver module.
strategy-updater updates outdated strategy files to the current version
lookahead-analysis Check for potential look ahead bias.
recursive-analysis Check for potential recursive formula issue.
optional arguments: options:
-h, --help show this help message and exit -h, --help show this help message and exit
-V, --version show program's version number and exit -V, --version show program's version number and exit

View File

@@ -123,6 +123,19 @@ This is similar to using multiple `--config` parameters, but simpler in usage as
If multiple files are in the `add_config_files` section, then they will be assumed to be at identical levels, having the last occurrence override the earlier config (unless a parent already defined such a key). If multiple files are in the `add_config_files` section, then they will be assumed to be at identical levels, having the last occurrence override the earlier config (unless a parent already defined such a key).
## Editor autocomplete and validation
If you are using an editor that supports JSON schema, you can use the schema provided by Freqtrade to get autocompletion and validation of your configuration file by adding the following line to the top of your configuration file:
``` json
{
"$schema": "https://schema.freqtrade.io/schema.json",
}
```
??? Note "Develop version"
The develop schema is available as `https://schema.freqtrade.io/schema_dev.json` - though we recommend to stick to the stable version for the best experience.
## Configuration parameters ## Configuration parameters
The table below will list all configuration parameters available. The table below will list all configuration parameters available.
@@ -209,7 +222,6 @@ Mandatory parameters are marked as **Required**, which means that they are requi
| `exchange.ccxt_async_config` | Additional CCXT parameters passed to the async ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://docs.ccxt.com/#/README?id=overriding-exchange-properties-upon-instantiation) <br> **Datatype:** Dict | `exchange.ccxt_async_config` | Additional CCXT parameters passed to the async ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://docs.ccxt.com/#/README?id=overriding-exchange-properties-upon-instantiation) <br> **Datatype:** Dict
| `exchange.enable_ws` | Enable the usage of Websockets for the exchange. <br>[More information](#consuming-exchange-websockets).<br>*Defaults to `true`.* <br> **Datatype:** Boolean | `exchange.enable_ws` | Enable the usage of Websockets for the exchange. <br>[More information](#consuming-exchange-websockets).<br>*Defaults to `true`.* <br> **Datatype:** Boolean
| `exchange.markets_refresh_interval` | The interval in minutes in which markets are reloaded. <br>*Defaults to `60` minutes.* <br> **Datatype:** Positive Integer | `exchange.markets_refresh_interval` | The interval in minutes in which markets are reloaded. <br>*Defaults to `60` minutes.* <br> **Datatype:** Positive Integer
| `exchange.skip_pair_validation` | Skip pairlist validation on startup.<br>*Defaults to `false`*<br> **Datatype:** Boolean
| `exchange.skip_open_order_update` | Skips open order updates on startup should the exchange cause problems. Only relevant in live conditions.<br>*Defaults to `false`*<br> **Datatype:** Boolean | `exchange.skip_open_order_update` | Skips open order updates on startup should the exchange cause problems. Only relevant in live conditions.<br>*Defaults to `false`*<br> **Datatype:** Boolean
| `exchange.unknown_fee_rate` | Fallback value to use when calculating trading fees. This can be useful for exchanges which have fees in non-tradable currencies. The value provided here will be multiplied with the "fee cost".<br>*Defaults to `None`<br> **Datatype:** float | `exchange.unknown_fee_rate` | Fallback value to use when calculating trading fees. This can be useful for exchanges which have fees in non-tradable currencies. The value provided here will be multiplied with the "fee cost".<br>*Defaults to `None`<br> **Datatype:** float
| `exchange.log_responses` | Log relevant exchange responses. For debug mode only - use with care.<br>*Defaults to `false`*<br> **Datatype:** Boolean | `exchange.log_responses` | Log relevant exchange responses. For debug mode only - use with care.<br>*Defaults to `false`*<br> **Datatype:** Boolean

View File

@@ -423,7 +423,8 @@ You can get a list of downloaded data using the `list-data` sub-command.
usage: freqtrade list-data [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] usage: freqtrade list-data [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH]
[--userdir PATH] [--exchange EXCHANGE] [--userdir PATH] [--exchange EXCHANGE]
[--data-format-ohlcv {json,jsongz,hdf5,feather,parquet}] [--data-format-ohlcv {json,jsongz,hdf5,feather,parquet}]
[-p PAIRS [PAIRS ...]] [--data-format-trades {json,jsongz,hdf5,feather,parquet}]
[--trades] [-p PAIRS [PAIRS ...]]
[--trading-mode {spot,margin,futures}] [--trading-mode {spot,margin,futures}]
[--show-timerange] [--show-timerange]
@@ -433,6 +434,10 @@ options:
--data-format-ohlcv {json,jsongz,hdf5,feather,parquet} --data-format-ohlcv {json,jsongz,hdf5,feather,parquet}
Storage format for downloaded candle (OHLCV) data. Storage format for downloaded candle (OHLCV) data.
(default: `feather`). (default: `feather`).
--data-format-trades {json,jsongz,hdf5,feather,parquet}
Storage format for downloaded trades data. (default:
`feather`).
--trades Work on trades data instead of OHLCV data.
-p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...] -p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...]
Limit command to these pairs. Pairs are space- Limit command to these pairs. Pairs are space-
separated. separated.
@@ -465,13 +470,29 @@ Common arguments:
```bash ```bash
> freqtrade list-data --userdir ~/.freqtrade/user_data/ > freqtrade list-data --userdir ~/.freqtrade/user_data/
Found 33 pair / timeframe combinations. Found 33 pair / timeframe combinations.
pairs timeframe ┏━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━┓
---------- ----------------------------------------- ┃ Pair ┃ Timeframe ┃ Type ┃
ADA/BTC 5m, 15m, 30m, 1h, 2h, 4h, 6h, 12h, 1d ┡━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━┩
ADA/ETH 5m, 15m, 30m, 1h, 2h, 4h, 6h, 12h, 1d │ ADA/BTC │ 5m, 15m, 30m, 1h, 2h, 4h, 6h, 12h, 1d │ spot │
ETH/BTC 5m, 15m, 30m, 1h, 2h, 4h, 6h, 12h, 1d │ ADA/ETH │ 5m, 15m, 30m, 1h, 2h, 4h, 6h, 12h, 1d │ spot │
ETH/USDT 5m, 15m, 30m, 1h, 2h, 4h │ ETH/BTC │ 5m, 15m, 30m, 1h, 2h, 4h, 6h, 12h, 1d │ spot │
│ ETH/USDT │ 5m, 15m, 30m, 1h, 2h, 4h │ spot │
└───────────────┴───────────────────────────────────────────┴──────┘
```
Show all trades data including from/to timerange
``` bash
> freqtrade list-data --show --trades
Found trades data for 1 pair.
┏━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━┓
┃ Pair ┃ Type ┃ From ┃ To ┃ Trades ┃
┡━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━┩
│ XRP/ETH │ spot │ 2019-10-11 00:00:11 │ 2019-10-13 11:19:28 │ 12477 │
└─────────┴──────┴─────────────────────┴─────────────────────┴────────┘
``` ```
## Trades (tick) data ## Trades (tick) data

View File

@@ -205,7 +205,7 @@ This is called with each iteration of the bot (only if the Pairlist Handler is a
It must return the resulting pairlist (which may then be passed into the chain of Pairlist Handlers). It must return the resulting pairlist (which may then be passed into the chain of Pairlist Handlers).
Validations are optional, the parent class exposes a `_verify_blacklist(pairlist)` and `_whitelist_for_active_markets(pairlist)` to do default filtering. Use this if you limit your result to a certain number of pairs - so the end-result is not shorter than expected. Validations are optional, the parent class exposes a `verify_blacklist(pairlist)` and `_whitelist_for_active_markets(pairlist)` to do default filtering. Use this if you limit your result to a certain number of pairs - so the end-result is not shorter than expected.
#### filter_pairlist #### filter_pairlist
@@ -219,7 +219,7 @@ The default implementation in the base class simply calls the `_validate_pair()`
If overridden, it must return the resulting pairlist (which may then be passed into the next Pairlist Handler in the chain). If overridden, it must return the resulting pairlist (which may then be passed into the next Pairlist Handler in the chain).
Validations are optional, the parent class exposes a `_verify_blacklist(pairlist)` and `_whitelist_for_active_markets(pairlist)` to do default filters. Use this if you limit your result to a certain number of pairs - so the end result is not shorter than expected. Validations are optional, the parent class exposes a `verify_blacklist(pairlist)` and `_whitelist_for_active_markets(pairlist)` to do default filters. Use this if you limit your result to a certain number of pairs - so the end result is not shorter than expected.
In `VolumePairList`, this implements different methods of sorting, does early validation so only the expected number of pairs is returned. In `VolumePairList`, this implements different methods of sorting, does early validation so only the expected number of pairs is returned.
@@ -481,21 +481,24 @@ Once the PR against stable is merged (best right after merging):
### pypi ### pypi
!!! Note !!! Warning "Manual Releases"
This process is now automated as part of Github Actions. This process is automated as part of Github Actions.
Manual pypi pushes should not be necessary.
To create a pypi release, please run the following commands: ??? example "Manual release"
To manually create a pypi release, please run the following commands:
Additional requirement: `wheel`, `twine` (for uploading), account on pypi with proper permissions. Additional requirement: `wheel`, `twine` (for uploading), account on pypi with proper permissions.
``` bash ``` bash
python setup.py sdist bdist_wheel pip install -U build
python -m build --sdist --wheel
# For pypi test (to check if some change to the installation did work) # For pypi test (to check if some change to the installation did work)
twine upload --repository-url https://test.pypi.org/legacy/ dist/* twine upload --repository-url https://test.pypi.org/legacy/ dist/*
# For production: # For production:
twine upload dist/* twine upload dist/*
``` ```
Please don't push non-releases to the productive / real pypi instance. Please don't push non-releases to the productive / real pypi instance.

View File

@@ -255,18 +255,24 @@ The configuration parameter `exchange.unknown_fee_rate` can be used to specify t
## Bybit ## Bybit
Futures trading on bybit is currently supported for USDT markets, and will use isolated futures mode. Futures trading on bybit is currently supported for USDT markets, and will use isolated futures mode.
Users with unified accounts (there's no way back) can create a Sub-account which will start as "non-unified", and can therefore use isolated futures.
On startup, freqtrade will set the position mode to "One-way Mode" for the whole (sub)account. This avoids making this call over and over again (slowing down bot operations), but means that changes to this setting may result in exceptions and errors On startup, freqtrade will set the position mode to "One-way Mode" for the whole (sub)account. This avoids making this call over and over again (slowing down bot operations), but means that changes to this setting may result in exceptions and errors.
As bybit doesn't provide funding rate history, the dry-run calculation is used for live trades as well. As bybit doesn't provide funding rate history, the dry-run calculation is used for live trades as well.
API Keys for live futures trading (Subaccount on non-unified) must have the following permissions: API Keys for live futures trading must have the following permissions:
* Read-write * Read-write
* Contract - Orders * Contract - Orders
* Contract - Positions * Contract - Positions
We do strongly recommend to limit all API keys to the IP you're going to use it from. We do strongly recommend to limit all API keys to the IP you're going to use it from.
!!! Warning "Unified accounts"
Freqtrade assumes accounts to be dedicated to the bot.
We therefore recommend the usage of one subaccount per bot. This is especially important when using unified accounts.
Other configurations (multiple bots on one account, manual non-bot trades on the bot account) are not supported and may lead to unexpected behavior.
!!! Tip "Stoploss on Exchange" !!! Tip "Stoploss on Exchange"
Bybit (futures only) supports `stoploss_on_exchange` and uses `stop-loss-limit` orders. It provides great advantages, so we recommend to benefit from it by enabling stoploss on exchange. Bybit (futures only) supports `stoploss_on_exchange` and uses `stop-loss-limit` orders. It provides great advantages, so we recommend to benefit from it by enabling stoploss on exchange.
On futures, Bybit supports both `stop-limit` as well as `stop-market` orders. You can use either `"limit"` or `"market"` in the `order_types.stoploss` configuration setting to decide which type to use. On futures, Bybit supports both `stop-limit` as well as `stop-market` orders. You can use either `"limit"` or `"market"` in the `order_types.stoploss` configuration setting to decide which type to use.

View File

@@ -58,7 +58,6 @@ The plot configuration can be accessed via the "Plot Configurator" (Cog icon) bu
### Settings ### Settings
Several UI related settings can be changed by accessing the settings page. Several UI related settings can be changed by accessing the settings page.
Things you can change (among others): Things you can change (among others):

View File

@@ -2,11 +2,11 @@
Pairlist Handlers define the list of pairs (pairlist) that the bot should trade. They are configured in the `pairlists` section of the configuration settings. Pairlist Handlers define the list of pairs (pairlist) that the bot should trade. They are configured in the `pairlists` section of the configuration settings.
In your configuration, you can use Static Pairlist (defined by the [`StaticPairList`](#static-pair-list) Pairlist Handler) and Dynamic Pairlist (defined by the [`VolumePairList`](#volume-pair-list) Pairlist Handler). In your configuration, you can use Static Pairlist (defined by the [`StaticPairList`](#static-pair-list) Pairlist Handler) and Dynamic Pairlist (defined by the [`VolumePairList`](#volume-pair-list) and [`PercentChangePairList`](#percent-change-pair-list) Pairlist Handlers).
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. 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 can define either `StaticPairList`, `VolumePairList`, `ProducerPairList`, `RemotePairList` or `MarketCapPairList` 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`, `MarketCapPairList` or `PercentChangePairList` 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. 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.
@@ -22,6 +22,7 @@ You may also use something like `.*DOWN/BTC` or `.*UP/BTC` to exclude leveraged
* [`StaticPairList`](#static-pair-list) (default, if not configured differently) * [`StaticPairList`](#static-pair-list) (default, if not configured differently)
* [`VolumePairList`](#volume-pair-list) * [`VolumePairList`](#volume-pair-list)
* [`PercentChangePairList`](#percent-change-pair-list)
* [`ProducerPairList`](#producerpairlist) * [`ProducerPairList`](#producerpairlist)
* [`RemotePairList`](#remotepairlist) * [`RemotePairList`](#remotepairlist)
* [`MarketCapPairList`](#marketcappairlist) * [`MarketCapPairList`](#marketcappairlist)
@@ -54,7 +55,6 @@ It uses configuration from `exchange.pair_whitelist` and `exchange.pair_blacklis
By default, only currently enabled pairs are allowed. By default, only currently enabled pairs are allowed.
To skip pair validation against active markets, set `"allow_inactive": true` within the `StaticPairList` configuration. To skip pair validation against active markets, set `"allow_inactive": true` within the `StaticPairList` configuration.
This can be useful for backtesting expired pairs (like quarterly spot-markets). This can be useful for backtesting expired pairs (like quarterly spot-markets).
This option must be configured along with `exchange.skip_pair_validation` in the exchange configuration.
When used in a "follow-up" position (e.g. after VolumePairlist), all pairs in `'pair_whitelist'` will be added to the end of the pairlist. When used in a "follow-up" position (e.g. after VolumePairlist), all pairs in `'pair_whitelist'` will be added to the end of the pairlist.
@@ -152,6 +152,89 @@ More sophisticated approach can be used, by using `lookback_timeframe` for candl
!!! Note !!! Note
`VolumePairList` does not support backtesting mode. `VolumePairList` does not support backtesting mode.
#### Percent Change Pair List
`PercentChangePairList` filters and sorts pairs based on the percentage change in their price over the last 24 hours or any defined timeframe as part of advanced options. This allows traders to focus on assets that have experienced significant price movements, either positive or negative.
**Configuration Options**
* `number_assets`: Specifies the number of top pairs to select based on the 24-hour percentage change.
* `min_value`: Sets a minimum percentage change threshold. Pairs with a percentage change below this value will be filtered out.
* `max_value`: Sets a maximum percentage change threshold. Pairs with a percentage change above this value will be filtered out.
* `sort_direction`: Specifies the order in which pairs are sorted based on their percentage change. Accepts two values: `asc` for ascending order and `desc` for descending order.
* `refresh_period`: Defines the interval (in seconds) at which the pairlist will be refreshed. The default is 1800 seconds (30 minutes).
* `lookback_days`: Number of days to look back. When `lookback_days` is selected, the `lookback_timeframe` is defaulted to 1 day.
* `lookback_timeframe`: Timeframe to use for the lookback period.
* `lookback_period`: Number of periods to look back at.
When PercentChangePairList is used after other Pairlist Handlers, it will operate on the outputs of those handlers. If it is the leading Pairlist Handler, it will select pairs from all available markets with the specified stake currency.
`PercentChangePairList` uses ticker data from the exchange, provided via the ccxt library:
The percentage change is calculated as the change in price over the last 24 hours.
??? Note "Unsupported exchanges"
On some exchanges (like HTX), regular PercentChangePairList does not work as the api does not natively provide 24h percent change in price. This can be worked around by using candle data to calculate the percentage change. To roughly simulate 24h percent change, you can use the following configuration. Please note that these pairlists will only refresh once per day.
```json
"pairlists": [
{
"method": "PercentChangePairList",
"number_assets": 20,
"min_value": 0,
"refresh_period": 86400,
"lookback_days": 1
}
],
```
**Example Configuration to Read from Ticker**
```json
"pairlists": [
{
"method": "PercentChangePairList",
"number_assets": 15,
"min_value": -10,
"max_value": 50
}
],
```
In this configuration:
1. The top 15 pairs are selected based on the highest percentage change in price over the last 24 hours.
2. Only pairs with a percentage change between -10% and 50% are considered.
**Example Configuration to Read from Candles**
```json
"pairlists": [
{
"method": "PercentChangePairList",
"number_assets": 15,
"sort_key": "percentage",
"min_value": 0,
"refresh_period": 3600,
"lookback_timeframe": "1h",
"lookback_period": 72
}
],
```
This example builds the percent change pairs based on a rolling period of 3 days of 1-hour candles by using `lookback_timeframe` for candle size and `lookback_period` which specifies the number of candles.
The percent change in price is calculated using the following formula, which expresses the percentage difference between the current candle's close price and the previous candle's close price, as defined by the specified timeframe and lookback period:
$$ Percent Change = (\frac{Current Close - Previous Close}{Previous Close}) * 100 $$
!!! Warning "Range look back and refresh period"
When used in conjunction with `lookback_days` and `lookback_timeframe` the `refresh_period` can not be smaller than the candle size in seconds. As this will result in unnecessary requests to the exchanges API.
!!! Warning "Performance implications when using lookback range"
If used in first position in combination with lookback, the computation of the range-based percent change can be time and resource consuming, as it downloads candles for all tradable pairs. Hence it's highly advised to use the standard approach with `PercentChangePairList` to narrow the pairlist down for further percent-change calculation.
!!! Note "Backtesting"
`PercentChangePairList` does not support backtesting mode.
#### ProducerPairList #### ProducerPairList
With `ProducerPairList`, you can reuse the pairlist from a [Producer](producer-consumer.md) without explicitly defining the pairlist on each consumer. With `ProducerPairList`, you can reuse the pairlist from a [Producer](producer-consumer.md) without explicitly defining the pairlist on each consumer.
@@ -277,14 +360,21 @@ The optional `bearer_token` will be included in the requests Authorization Heade
"method": "MarketCapPairList", "method": "MarketCapPairList",
"number_assets": 20, "number_assets": 20,
"max_rank": 50, "max_rank": 50,
"refresh_period": 86400 "refresh_period": 86400,
"categories": ["layer-1"]
} }
] ]
``` ```
`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. `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). The `refresh_period` setting defines the interval (in seconds) at which the marketcap rank data will be refreshed. The default is 86,400 seconds (1 day). The pairlist cache (`refresh_period`) applies to both generating pairlists (when in the first position in the list) and filtering instances (when not in the first position in the list).
The `categories` setting specifies the [coingecko categories](https://www.coingecko.com/en/categories) from which to select coins from. The default is an empty list `[]`, meaning no category filtering is applied.
If an incorrect category string is chosen, the plugin will print the available categories from CoinGecko and fail. The category should be the ID of the category, for example, for `https://www.coingecko.com/en/categories/layer-1`, the category ID would be `layer-1`. You can pass multiple categories such as `["layer-1", "meme-token"]` to select from several categories.
!!! Warning "Many categories"
Each added category corresponds to one API call to CoinGecko. The more categories you add, the longer the pairlist generation will take, potentially causing rate limit issues.
#### AgeFilter #### AgeFilter

View File

@@ -36,6 +36,7 @@ All protection end times are rounded up to the next candle to avoid sudden, unex
| `lookback_period_candles` | Only trades that completed within the last `lookback_period_candles` candles will be considered. This setting may be ignored by some Protections. <br> **Datatype:** Positive integer (in candles). | `lookback_period_candles` | Only trades that completed within the last `lookback_period_candles` candles will be considered. This setting may be ignored by some Protections. <br> **Datatype:** Positive integer (in candles).
| `lookback_period` | Only trades that completed after `current_time - lookback_period` will be considered. <br>Cannot be used together with `lookback_period_candles`. <br>This setting may be ignored by some Protections. <br> **Datatype:** Float (in minutes) | `lookback_period` | Only trades that completed after `current_time - lookback_period` will be considered. <br>Cannot be used together with `lookback_period_candles`. <br>This setting may be ignored by some Protections. <br> **Datatype:** Float (in minutes)
| `trade_limit` | Number of trades required at minimum (not used by all Protections). <br> **Datatype:** Positive integer | `trade_limit` | Number of trades required at minimum (not used by all Protections). <br> **Datatype:** Positive integer
| `unlock_at` | Time when trading will be unlocked regularly (not used by all Protections). <br> **Datatype:** string <br>**Input Format:** "HH:MM" (24-hours)
!!! Note "Durations" !!! Note "Durations"
Durations (`stop_duration*` and `lookback_period*` can be defined in either minutes or candles). Durations (`stop_duration*` and `lookback_period*` can be defined in either minutes or candles).
@@ -44,7 +45,7 @@ All protection end times are rounded up to the next candle to avoid sudden, unex
#### Stoploss Guard #### Stoploss Guard
`StoplossGuard` selects all trades within `lookback_period` in minutes (or in candles when using `lookback_period_candles`). `StoplossGuard` selects all trades within `lookback_period` in minutes (or in candles when using `lookback_period_candles`).
If `trade_limit` or more trades resulted in stoploss, trading will stop for `stop_duration` in minutes (or in candles when using `stop_duration_candles`). If `trade_limit` or more trades resulted in stoploss, trading will stop for `stop_duration` in minutes (or in candles when using `stop_duration_candles`, or until the set time when using `unlock_at`).
This applies across all pairs, unless `only_per_pair` is set to true, which will then only look at one pair at a time. This applies across all pairs, unless `only_per_pair` is set to true, which will then only look at one pair at a time.
@@ -97,7 +98,7 @@ def protections(self):
#### Low Profit Pairs #### Low Profit Pairs
`LowProfitPairs` uses all trades for a pair within `lookback_period` in minutes (or in candles when using `lookback_period_candles`) to determine the overall profit ratio. `LowProfitPairs` uses all trades for a pair within `lookback_period` in minutes (or in candles when using `lookback_period_candles`) to determine the overall profit ratio.
If that ratio is below `required_profit`, that pair will be locked for `stop_duration` in minutes (or in candles when using `stop_duration_candles`). If that ratio is below `required_profit`, that pair will be locked for `stop_duration` in minutes (or in candles when using `stop_duration_candles`, or until the set time when using `unlock_at`).
For futures bots, setting `only_per_side` will make the bot only consider one side, and will then only lock this one side, allowing for example shorts to continue after a series of long losses. For futures bots, setting `only_per_side` will make the bot only consider one side, and will then only lock this one side, allowing for example shorts to continue after a series of long losses.
@@ -120,7 +121,7 @@ def protections(self):
#### Cooldown Period #### Cooldown Period
`CooldownPeriod` locks a pair for `stop_duration` in minutes (or in candles when using `stop_duration_candles`) after selling, avoiding a re-entry for this pair for `stop_duration` minutes. `CooldownPeriod` locks a pair for `stop_duration` in minutes (or in candles when using `stop_duration_candles`, or until the set time when using `unlock_at`) after exiting, avoiding a re-entry for this pair for `stop_duration` minutes.
The below example will stop trading a pair for 2 candles after closing a trade, allowing this pair to "cool down". The below example will stop trading a pair for 2 candles after closing a trade, allowing this pair to "cool down".

View File

@@ -0,0 +1,45 @@
## Imports necessary for a strategy
When creating a strategy, you will need to import the necessary modules and classes. The following imports are required for a strategy:
By default, we recommend the following imports as a base line for your strategy:
This will cover all imports necessary for freqtrade functions to work.
Obviously you can add more imports as needed for your strategy.
``` python
# flake8: noqa: F401
# isort: skip_file
# --- Do not remove these imports ---
import numpy as np
import pandas as pd
from datetime import datetime, timedelta, timezone
from pandas import DataFrame
from typing import Dict, Optional, Union, Tuple
from freqtrade.strategy import (
IStrategy,
Trade,
Order,
PairLocks,
informative, # @informative decorator
# Hyperopt Parameters
BooleanParameter,
CategoricalParameter,
DecimalParameter,
IntParameter,
RealParameter,
# timeframe helpers
timeframe_to_minutes,
timeframe_to_next_date,
timeframe_to_prev_date,
# Strategy helper functions
merge_informative_pair,
stoploss_from_absolute,
stoploss_from_open,
)
# --------------------------------
# Add your lib to import here
import talib.abstract as ta
from technical import qtpylib
```

View File

@@ -42,6 +42,7 @@ Please read the [exchange specific notes](exchanges.md) to learn about eventual,
- [X] [Binance](https://www.binance.com/) - [X] [Binance](https://www.binance.com/)
- [X] [Bitmart](https://bitmart.com/) - [X] [Bitmart](https://bitmart.com/)
- [X] [BingX](https://bingx.com/invite/0EM9RX) - [X] [BingX](https://bingx.com/invite/0EM9RX)
- [X] [Bybit](https://bybit.com/)
- [X] [Gate.io](https://www.gate.io/ref/6266643) - [X] [Gate.io](https://www.gate.io/ref/6266643)
- [X] [HTX](https://www.htx.com/) (Former Huobi) - [X] [HTX](https://www.htx.com/) (Former Huobi)
- [X] [Kraken](https://kraken.com/) - [X] [Kraken](https://kraken.com/)

View File

@@ -101,3 +101,4 @@ This could lead to a false-negative (the strategy will then be reported as non-b
- `lookahead-analysis` has access to everything that backtesting has too. - `lookahead-analysis` has access to everything that backtesting has too.
Please don't provoke any configs like enabling position stacking. Please don't provoke any configs like enabling position stacking.
If you decide to do so, then make doubly sure that you won't ever run out of `max_open_trades` amount and neither leftover money in your wallet. If you decide to do so, then make doubly sure that you won't ever run out of `max_open_trades` amount and neither leftover money in your wallet.
- In the results table, the `biased_indicators` column will falsely flag FreqAI target indicators defined in `set_freqai_targets()` as biased. These are not biased and can safely be ignored.

View File

@@ -1,6 +1,7 @@
markdown==3.6 markdown==3.7
mkdocs==1.6.0 mkdocs==1.6.1
mkdocs-material==9.5.29 mkdocs-material==9.5.36
mdx_truly_sane_lists==1.3 mdx_truly_sane_lists==1.3
pymdown-extensions==10.8.1 pymdown-extensions==10.10.1
jinja2==3.1.4 jinja2==3.1.4
mike==2.1.3

View File

@@ -24,6 +24,8 @@ Currently available callbacks:
!!! Tip "Callback calling sequence" !!! Tip "Callback calling sequence"
You can find the callback calling sequence in [bot-basics](bot-basics.md#bot-execution-logic) You can find the callback calling sequence in [bot-basics](bot-basics.md#bot-execution-logic)
--8<-- "includes/strategy-imports.md"
## Bot start ## Bot start
A simple callback which is called once when the strategy is loaded. A simple callback which is called once when the strategy is loaded.
@@ -41,10 +43,10 @@ class AwesomeStrategy(IStrategy):
Called only once after bot instantiation. Called only once after bot instantiation.
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy. :param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
""" """
if self.config['runmode'].value in ('live', 'dry_run'): if self.config["runmode"].value in ("live", "dry_run"):
# Assign this to the class by using self.* # Assign this to the class by using self.*
# can then be used by populate_* methods # can then be used by populate_* methods
self.custom_remote_data = requests.get('https://some_remote_source.example.com') self.custom_remote_data = requests.get("https://some_remote_source.example.com")
``` ```
@@ -57,6 +59,7 @@ seconds, unless configured differently) or once per candle in backtest/hyperopt
This can be used to perform calculations which are pair independent (apply to all pairs), loading of external data, etc. This can be used to perform calculations which are pair independent (apply to all pairs), loading of external data, etc.
``` python ``` python
# Default imports
import requests import requests
class AwesomeStrategy(IStrategy): class AwesomeStrategy(IStrategy):
@@ -71,10 +74,10 @@ class AwesomeStrategy(IStrategy):
:param current_time: datetime object, containing the current datetime :param current_time: datetime object, containing the current datetime
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy. :param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
""" """
if self.config['runmode'].value in ('live', 'dry_run'): if self.config["runmode"].value in ("live", "dry_run"):
# Assign this to the class by using self.* # Assign this to the class by using self.*
# can then be used by populate_* methods # can then be used by populate_* methods
self.remote_data = requests.get('https://some_remote_source.example.com') self.remote_data = requests.get("https://some_remote_source.example.com")
``` ```
@@ -83,6 +86,8 @@ class AwesomeStrategy(IStrategy):
Called before entering a trade, makes it possible to manage your position size when placing a new trade. Called before entering a trade, makes it possible to manage your position size when placing a new trade.
```python ```python
# Default imports
class AwesomeStrategy(IStrategy): class AwesomeStrategy(IStrategy):
def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
proposed_stake: float, min_stake: Optional[float], max_stake: float, proposed_stake: float, min_stake: Optional[float], max_stake: float,
@@ -92,13 +97,13 @@ class AwesomeStrategy(IStrategy):
dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe)
current_candle = dataframe.iloc[-1].squeeze() current_candle = dataframe.iloc[-1].squeeze()
if current_candle['fastk_rsi_1h'] > current_candle['fastd_rsi_1h']: if current_candle["fastk_rsi_1h"] > current_candle["fastd_rsi_1h"]:
if self.config['stake_amount'] == 'unlimited': if self.config["stake_amount"] == "unlimited":
# Use entire available wallet during favorable conditions when in compounding mode. # Use entire available wallet during favorable conditions when in compounding mode.
return max_stake return max_stake
else: else:
# Compound profits during favorable conditions instead of using a static stake. # Compound profits during favorable conditions instead of using a static stake.
return self.wallets.get_total_stake_amount() / self.config['max_open_trades'] return self.wallets.get_total_stake_amount() / self.config["max_open_trades"]
# Use default stake amount. # Use default stake amount.
return proposed_stake return proposed_stake
@@ -129,25 +134,27 @@ Using `custom_exit()` signals in place of stoploss though *is not recommended*.
An example of how we can use different indicators depending on the current profit and also exit trades that were open longer than one day: An example of how we can use different indicators depending on the current profit and also exit trades that were open longer than one day:
``` python ``` python
# Default imports
class AwesomeStrategy(IStrategy): class AwesomeStrategy(IStrategy):
def custom_exit(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float, def custom_exit(self, pair: str, trade: Trade, current_time: datetime, current_rate: float,
current_profit: float, **kwargs): current_profit: float, **kwargs):
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
last_candle = dataframe.iloc[-1].squeeze() last_candle = dataframe.iloc[-1].squeeze()
# Above 20% profit, sell when rsi < 80 # Above 20% profit, sell when rsi < 80
if current_profit > 0.2: if current_profit > 0.2:
if last_candle['rsi'] < 80: if last_candle["rsi"] < 80:
return 'rsi_below_80' return "rsi_below_80"
# Between 2% and 10%, sell if EMA-long above EMA-short # Between 2% and 10%, sell if EMA-long above EMA-short
if 0.02 < current_profit < 0.1: if 0.02 < current_profit < 0.1:
if last_candle['emalong'] > last_candle['emashort']: if last_candle["emalong"] > last_candle["emashort"]:
return 'ema_long_below_80' return "ema_long_below_80"
# Sell any positions at a loss if they are held for more than one day. # Sell any positions at a loss if they are held for more than one day.
if current_profit < 0.0 and (current_time - trade.open_date_utc).days >= 1: if current_profit < 0.0 and (current_time - trade.open_date_utc).days >= 1:
return 'unclog' return "unclog"
``` ```
See [Dataframe access](strategy-advanced.md#dataframe-access) for more information about dataframe use in strategy callbacks. See [Dataframe access](strategy-advanced.md#dataframe-access) for more information about dataframe use in strategy callbacks.
@@ -168,7 +175,6 @@ The absolute value of the return value is used (the sign is ignored), so returni
Returning `None` will be interpreted as "no desire to change", and is the only safe way to return when you'd like to not modify the stoploss. Returning `None` will be interpreted as "no desire to change", and is the only safe way to return when you'd like to not modify the stoploss.
`NaN` and `inf` values are considered invalid and will be ignored (identical to `None`). `NaN` and `inf` values are considered invalid and will be ignored (identical to `None`).
Stoploss on exchange works similar to `trailing_stop`, and the stoploss on exchange is updated as configured in `stoploss_on_exchange_interval` ([More details about stoploss on exchange](stoploss.md#stop-loss-on-exchangefreqtrade)). Stoploss on exchange works similar to `trailing_stop`, and the stoploss on exchange is updated as configured in `stoploss_on_exchange_interval` ([More details about stoploss on exchange](stoploss.md#stop-loss-on-exchangefreqtrade)).
!!! Note "Use of dates" !!! Note "Use of dates"
@@ -196,9 +202,7 @@ Of course, many more things are possible, and all examples can be combined at wi
To simulate a regular trailing stoploss of 4% (trailing 4% behind the maximum reached price) you would use the following very simple method: To simulate a regular trailing stoploss of 4% (trailing 4% behind the maximum reached price) you would use the following very simple method:
``` python ``` python
# additional imports required # Default imports
from datetime import datetime
from freqtrade.persistence import Trade
class AwesomeStrategy(IStrategy): class AwesomeStrategy(IStrategy):
@@ -206,7 +210,7 @@ class AwesomeStrategy(IStrategy):
use_custom_stoploss = True use_custom_stoploss = True
def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime,
current_rate: float, current_profit: float, after_fill: bool, current_rate: float, current_profit: float, after_fill: bool,
**kwargs) -> Optional[float]: **kwargs) -> Optional[float]:
""" """
@@ -236,8 +240,7 @@ class AwesomeStrategy(IStrategy):
Use the initial stoploss for the first 60 minutes, after this change to 10% trailing stoploss, and after 2 hours (120 minutes) we use a 5% trailing stoploss. Use the initial stoploss for the first 60 minutes, after this change to 10% trailing stoploss, and after 2 hours (120 minutes) we use a 5% trailing stoploss.
``` python ``` python
from datetime import datetime, timedelta # Default imports
from freqtrade.persistence import Trade
class AwesomeStrategy(IStrategy): class AwesomeStrategy(IStrategy):
@@ -245,7 +248,7 @@ class AwesomeStrategy(IStrategy):
use_custom_stoploss = True use_custom_stoploss = True
def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime,
current_rate: float, current_profit: float, after_fill: bool, current_rate: float, current_profit: float, after_fill: bool,
**kwargs) -> Optional[float]: **kwargs) -> Optional[float]:
@@ -263,8 +266,7 @@ Use the initial stoploss for the first 60 minutes, after this change to 10% trai
If an additional order fills, set stoploss to -10% below the new `open_rate` ([Averaged across all entries](#position-adjust-calculations)). If an additional order fills, set stoploss to -10% below the new `open_rate` ([Averaged across all entries](#position-adjust-calculations)).
``` python ``` python
from datetime import datetime, timedelta # Default imports
from freqtrade.persistence import Trade
class AwesomeStrategy(IStrategy): class AwesomeStrategy(IStrategy):
@@ -272,7 +274,7 @@ class AwesomeStrategy(IStrategy):
use_custom_stoploss = True use_custom_stoploss = True
def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime,
current_rate: float, current_profit: float, after_fill: bool, current_rate: float, current_profit: float, after_fill: bool,
**kwargs) -> Optional[float]: **kwargs) -> Optional[float]:
@@ -293,8 +295,7 @@ Use a different stoploss depending on the pair.
In this example, we'll trail the highest price with 10% trailing stoploss for `ETH/BTC` and `XRP/BTC`, with 5% trailing stoploss for `LTC/BTC` and with 15% for all other pairs. In this example, we'll trail the highest price with 10% trailing stoploss for `ETH/BTC` and `XRP/BTC`, with 5% trailing stoploss for `LTC/BTC` and with 15% for all other pairs.
``` python ``` python
from datetime import datetime # Default imports
from freqtrade.persistence import Trade
class AwesomeStrategy(IStrategy): class AwesomeStrategy(IStrategy):
@@ -302,13 +303,13 @@ class AwesomeStrategy(IStrategy):
use_custom_stoploss = True use_custom_stoploss = True
def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime,
current_rate: float, current_profit: float, after_fill: bool, current_rate: float, current_profit: float, after_fill: bool,
**kwargs) -> Optional[float]: **kwargs) -> Optional[float]:
if pair in ('ETH/BTC', 'XRP/BTC'): if pair in ("ETH/BTC", "XRP/BTC"):
return -0.10 return -0.10
elif pair in ('LTC/BTC'): elif pair in ("LTC/BTC"):
return -0.05 return -0.05
return -0.15 return -0.15
``` ```
@@ -320,8 +321,7 @@ Use the initial stoploss until the profit is above 4%, then use a trailing stopl
Please note that the stoploss can only increase, values lower than the current stoploss are ignored. Please note that the stoploss can only increase, values lower than the current stoploss are ignored.
``` python ``` python
from datetime import datetime, timedelta # Default imports
from freqtrade.persistence import Trade
class AwesomeStrategy(IStrategy): class AwesomeStrategy(IStrategy):
@@ -329,7 +329,7 @@ class AwesomeStrategy(IStrategy):
use_custom_stoploss = True use_custom_stoploss = True
def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime,
current_rate: float, current_profit: float, after_fill: bool, current_rate: float, current_profit: float, after_fill: bool,
**kwargs) -> Optional[float]: **kwargs) -> Optional[float]:
@@ -353,9 +353,7 @@ Instead of continuously trailing behind the current price, this example sets fix
* Once profit is > 40% - set stoploss to 25% above open price. * Once profit is > 40% - set stoploss to 25% above open price.
``` python ``` python
from datetime import datetime # Default imports
from freqtrade.persistence import Trade
from freqtrade.strategy import stoploss_from_open
class AwesomeStrategy(IStrategy): class AwesomeStrategy(IStrategy):
@@ -363,7 +361,7 @@ class AwesomeStrategy(IStrategy):
use_custom_stoploss = True use_custom_stoploss = True
def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime,
current_rate: float, current_profit: float, after_fill: bool, current_rate: float, current_profit: float, after_fill: bool,
**kwargs) -> Optional[float]: **kwargs) -> Optional[float]:
@@ -384,15 +382,17 @@ class AwesomeStrategy(IStrategy):
Absolute stoploss value may be derived from indicators stored in dataframe. Example uses parabolic SAR below the price as stoploss. Absolute stoploss value may be derived from indicators stored in dataframe. Example uses parabolic SAR below the price as stoploss.
``` python ``` python
# Default imports
class AwesomeStrategy(IStrategy): class AwesomeStrategy(IStrategy):
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# <...> # <...>
dataframe['sar'] = ta.SAR(dataframe) dataframe["sar"] = ta.SAR(dataframe)
use_custom_stoploss = True use_custom_stoploss = True
def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime,
current_rate: float, current_profit: float, after_fill: bool, current_rate: float, current_profit: float, after_fill: bool,
**kwargs) -> Optional[float]: **kwargs) -> Optional[float]:
@@ -400,7 +400,7 @@ class AwesomeStrategy(IStrategy):
last_candle = dataframe.iloc[-1].squeeze() last_candle = dataframe.iloc[-1].squeeze()
# Use parabolic sar as absolute stoploss price # Use parabolic sar as absolute stoploss price
stoploss_price = last_candle['sar'] stoploss_price = last_candle["sar"]
# Convert absolute price to percentage relative to current_rate # Convert absolute price to percentage relative to current_rate
if stoploss_price < current_rate: if stoploss_price < current_rate:
@@ -429,10 +429,7 @@ Stoploss values returned from `custom_stoploss()` must specify a percentage rela
``` python ``` python
# Default imports
from datetime import datetime
from freqtrade.persistence import Trade
from freqtrade.strategy import IStrategy, stoploss_from_open
class AwesomeStrategy(IStrategy): class AwesomeStrategy(IStrategy):
@@ -440,7 +437,7 @@ Stoploss values returned from `custom_stoploss()` must specify a percentage rela
use_custom_stoploss = True use_custom_stoploss = True
def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime,
current_rate: float, current_profit: float, after_fill: bool, current_rate: float, current_profit: float, after_fill: bool,
**kwargs) -> Optional[float]: **kwargs) -> Optional[float]:
@@ -469,38 +466,34 @@ The helper function `stoploss_from_absolute()` can be used to convert from an ab
??? Example "Returning a stoploss using absolute price from the custom stoploss function" ??? Example "Returning a stoploss using absolute price from the custom stoploss function"
If we want to trail a stop price at 2xATR below current price we can call `stoploss_from_absolute(current_rate + (side * candle['atr'] * 2), current_rate=current_rate, is_short=trade.is_short, leverage=trade.leverage)`. If we want to trail a stop price at 2xATR below current price we can call `stoploss_from_absolute(current_rate + (side * candle["atr"] * 2), current_rate=current_rate, is_short=trade.is_short, leverage=trade.leverage)`.
For futures, we need to adjust the direction (up or down), as well as adjust for leverage, since the [`custom_stoploss`](strategy-callbacks.md#custom-stoploss) callback returns the ["risk for this trade"](stoploss.md#stoploss-and-leverage) - not the relative price movement. For futures, we need to adjust the direction (up or down), as well as adjust for leverage, since the [`custom_stoploss`](strategy-callbacks.md#custom-stoploss) callback returns the ["risk for this trade"](stoploss.md#stoploss-and-leverage) - not the relative price movement.
``` python ``` python
# Default imports
from datetime import datetime
from freqtrade.persistence import Trade
from freqtrade.strategy import IStrategy, stoploss_from_absolute, timeframe_to_prev_date
class AwesomeStrategy(IStrategy): class AwesomeStrategy(IStrategy):
use_custom_stoploss = True use_custom_stoploss = True
def populate_indicators_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame: def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe['atr'] = ta.ATR(dataframe, timeperiod=14) dataframe["atr"] = ta.ATR(dataframe, timeperiod=14)
return dataframe return dataframe
def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime,
current_rate: float, current_profit: float, after_fill: bool, current_rate: float, current_profit: float, after_fill: bool,
**kwargs) -> Optional[float]: **kwargs) -> Optional[float]:
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
trade_date = timeframe_to_prev_date(self.timeframe, trade.open_date_utc) trade_date = timeframe_to_prev_date(self.timeframe, trade.open_date_utc)
candle = dataframe.iloc[-1].squeeze() candle = dataframe.iloc[-1].squeeze()
side = 1 if trade.is_short else -1 side = 1 if trade.is_short else -1
return stoploss_from_absolute(current_rate + (side * candle['atr'] * 2), return stoploss_from_absolute(current_rate + (side * candle["atr"] * 2),
current_rate=current_rate, current_rate=current_rate,
is_short=trade.is_short, is_short=trade.is_short,
leverage=trade.leverage) leverage=trade.leverage)
``` ```
--- ---
## Custom order price rules ## Custom order price rules
@@ -520,19 +513,18 @@ Each of these methods are called right before placing an order on the exchange.
### Custom order entry and exit price example ### Custom order entry and exit price example
``` python ``` python
from datetime import datetime, timedelta, timezone # Default imports
from freqtrade.persistence import Trade
class AwesomeStrategy(IStrategy): class AwesomeStrategy(IStrategy):
# ... populate_* methods # ... populate_* methods
def custom_entry_price(self, pair: str, trade: Optional['Trade'], current_time: datetime, proposed_rate: float, def custom_entry_price(self, pair: str, trade: Optional[Trade], current_time: datetime, proposed_rate: float,
entry_tag: Optional[str], side: str, **kwargs) -> float: entry_tag: Optional[str], side: str, **kwargs) -> float:
dataframe, last_updated = self.dp.get_analyzed_dataframe(pair=pair, dataframe, last_updated = self.dp.get_analyzed_dataframe(pair=pair,
timeframe=self.timeframe) timeframe=self.timeframe)
new_entryprice = dataframe['bollinger_10_lowerband'].iat[-1] new_entryprice = dataframe["bollinger_10_lowerband"].iat[-1]
return new_entryprice return new_entryprice
@@ -542,7 +534,7 @@ class AwesomeStrategy(IStrategy):
dataframe, last_updated = self.dp.get_analyzed_dataframe(pair=pair, dataframe, last_updated = self.dp.get_analyzed_dataframe(pair=pair,
timeframe=self.timeframe) timeframe=self.timeframe)
new_exitprice = dataframe['bollinger_10_upperband'].iat[-1] new_exitprice = dataframe["bollinger_10_upperband"].iat[-1]
return new_exitprice return new_exitprice
@@ -579,8 +571,7 @@ It applies a tight timeout for higher priced assets, while allowing more time to
The function must return either `True` (cancel order) or `False` (keep order alive). The function must return either `True` (cancel order) or `False` (keep order alive).
``` python ``` python
from datetime import datetime, timedelta # Default imports
from freqtrade.persistence import Trade, Order
class AwesomeStrategy(IStrategy): class AwesomeStrategy(IStrategy):
@@ -588,11 +579,11 @@ class AwesomeStrategy(IStrategy):
# Set unfilledtimeout to 25 hours, since the maximum timeout from below is 24 hours. # Set unfilledtimeout to 25 hours, since the maximum timeout from below is 24 hours.
unfilledtimeout = { unfilledtimeout = {
'entry': 60 * 25, "entry": 60 * 25,
'exit': 60 * 25 "exit": 60 * 25
} }
def check_entry_timeout(self, pair: str, trade: 'Trade', order: 'Order', def check_entry_timeout(self, pair: str, trade: Trade, order: Order,
current_time: datetime, **kwargs) -> bool: current_time: datetime, **kwargs) -> bool:
if trade.open_rate > 100 and trade.open_date_utc < current_time - timedelta(minutes=5): if trade.open_rate > 100 and trade.open_date_utc < current_time - timedelta(minutes=5):
return True return True
@@ -603,7 +594,7 @@ class AwesomeStrategy(IStrategy):
return False return False
def check_exit_timeout(self, pair: str, trade: Trade, order: 'Order', def check_exit_timeout(self, pair: str, trade: Trade, order: Order,
current_time: datetime, **kwargs) -> bool: current_time: datetime, **kwargs) -> bool:
if trade.open_rate > 100 and trade.open_date_utc < current_time - timedelta(minutes=5): if trade.open_rate > 100 and trade.open_date_utc < current_time - timedelta(minutes=5):
return True return True
@@ -620,8 +611,7 @@ class AwesomeStrategy(IStrategy):
### Custom order timeout example (using additional data) ### Custom order timeout example (using additional data)
``` python ``` python
from datetime import datetime # Default imports
from freqtrade.persistence import Trade, Order
class AwesomeStrategy(IStrategy): class AwesomeStrategy(IStrategy):
@@ -629,24 +619,24 @@ class AwesomeStrategy(IStrategy):
# Set unfilledtimeout to 25 hours, since the maximum timeout from below is 24 hours. # Set unfilledtimeout to 25 hours, since the maximum timeout from below is 24 hours.
unfilledtimeout = { unfilledtimeout = {
'entry': 60 * 25, "entry": 60 * 25,
'exit': 60 * 25 "exit": 60 * 25
} }
def check_entry_timeout(self, pair: str, trade: 'Trade', order: 'Order', def check_entry_timeout(self, pair: str, trade: Trade, order: Order,
current_time: datetime, **kwargs) -> bool: current_time: datetime, **kwargs) -> bool:
ob = self.dp.orderbook(pair, 1) ob = self.dp.orderbook(pair, 1)
current_price = ob['bids'][0][0] current_price = ob["bids"][0][0]
# Cancel buy order if price is more than 2% above the order. # Cancel buy order if price is more than 2% above the order.
if current_price > order.price * 1.02: if current_price > order.price * 1.02:
return True return True
return False return False
def check_exit_timeout(self, pair: str, trade: 'Trade', order: 'Order', def check_exit_timeout(self, pair: str, trade: Trade, order: Order,
current_time: datetime, **kwargs) -> bool: current_time: datetime, **kwargs) -> bool:
ob = self.dp.orderbook(pair, 1) ob = self.dp.orderbook(pair, 1)
current_price = ob['asks'][0][0] current_price = ob["asks"][0][0]
# Cancel sell order if price is more than 2% below the order. # Cancel sell order if price is more than 2% below the order.
if current_price < order.price * 0.98: if current_price < order.price * 0.98:
return True return True
@@ -665,6 +655,8 @@ This are the last methods that will be called before an order is placed.
`confirm_trade_entry()` can be used to abort a trade entry at the latest second (maybe because the price is not what we expect). `confirm_trade_entry()` can be used to abort a trade entry at the latest second (maybe because the price is not what we expect).
``` python ``` python
# Default imports
class AwesomeStrategy(IStrategy): class AwesomeStrategy(IStrategy):
# ... populate_* methods # ... populate_* methods
@@ -689,7 +681,7 @@ class AwesomeStrategy(IStrategy):
:param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled). :param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled).
:param current_time: datetime object, containing the current datetime :param current_time: datetime object, containing the current datetime
:param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal. :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal.
:param side: 'long' or 'short' - indicating the direction of the proposed trade :param side: "long" or "short" - indicating the direction of the proposed trade
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy. :param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
:return bool: When True is returned, then the buy-order is placed on the exchange. :return bool: When True is returned, then the buy-order is placed on the exchange.
False aborts the process False aborts the process
@@ -711,8 +703,7 @@ The exit-reasons (if applicable) will be in the following sequence:
* `trailing_stop_loss` * `trailing_stop_loss`
``` python ``` python
from freqtrade.persistence import Trade # Default imports
class AwesomeStrategy(IStrategy): class AwesomeStrategy(IStrategy):
@@ -738,14 +729,14 @@ class AwesomeStrategy(IStrategy):
or current rate for market orders. or current rate for market orders.
:param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled). :param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled).
:param exit_reason: Exit reason. :param exit_reason: Exit reason.
Can be any of ['roi', 'stop_loss', 'stoploss_on_exchange', 'trailing_stop_loss', Can be any of ["roi", "stop_loss", "stoploss_on_exchange", "trailing_stop_loss",
'exit_signal', 'force_exit', 'emergency_exit'] "exit_signal", "force_exit", "emergency_exit"]
:param current_time: datetime object, containing the current datetime :param current_time: datetime object, containing the current datetime
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy. :param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
:return bool: When True, then the exit-order is placed on the exchange. :return bool: When True, then the exit-order is placed on the exchange.
False aborts the process False aborts the process
""" """
if exit_reason == 'force_exit' and trade.calc_profit_ratio(rate) < 0: if exit_reason == "force_exit" and trade.calc_profit_ratio(rate) < 0:
# Reject force-sells with negative profit # Reject force-sells with negative profit
# This is just a sample, please adjust to your needs # This is just a sample, please adjust to your needs
# (this does not necessarily make sense, assuming you know when you're force-selling) # (this does not necessarily make sense, assuming you know when you're force-selling)
@@ -771,7 +762,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. `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. 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'`). 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. Modifications to leverage are not possible, and the stake-amount returned is assumed to be before applying leverage.
@@ -793,7 +784,7 @@ Returning a value more than the above (so remaining stake_amount would become ne
!!! Note "About stake size" !!! Note "About stake size"
Using fixed stake size means it will be the amount used for the first order, just like without position adjustment. Using fixed stake size means it will be the amount used for the first order, just like without position adjustment.
If you wish to buy additional orders with DCA, then make sure to leave enough funds in the wallet for that. 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. 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 "Stoploss calculation" !!! Warning "Stoploss calculation"
Stoploss is still calculated from the initial opening price, not averaged price. Stoploss is still calculated from the initial opening price, not averaged price.
@@ -811,9 +802,7 @@ Returning a value more than the above (so remaining stake_amount would become ne
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. 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 ``` python
from freqtrade.persistence import Trade # Default imports
from typing import Optional, Tuple, Union
class DigDeeperStrategy(IStrategy): class DigDeeperStrategy(IStrategy):
@@ -876,7 +865,7 @@ class DigDeeperStrategy(IStrategy):
if current_profit > 0.05 and trade.nr_of_successful_exits == 0: if current_profit > 0.05 and trade.nr_of_successful_exits == 0:
# Take half of the profit at +5% # Take half of the profit at +5%
return -(trade.stake_amount / 2), 'half_profit_5%' return -(trade.stake_amount / 2), "half_profit_5%"
if current_profit > -0.05: if current_profit > -0.05:
return None return None
@@ -886,7 +875,7 @@ class DigDeeperStrategy(IStrategy):
# Only buy when not actively falling price. # Only buy when not actively falling price.
last_candle = dataframe.iloc[-1].squeeze() last_candle = dataframe.iloc[-1].squeeze()
previous_candle = dataframe.iloc[-2].squeeze() previous_candle = dataframe.iloc[-2].squeeze()
if last_candle['close'] < previous_candle['close']: if last_candle["close"] < previous_candle["close"]:
return None return None
filled_entries = trade.select_filled_orders(trade.entry_side) filled_entries = trade.select_filled_orders(trade.entry_side)
@@ -904,7 +893,7 @@ class DigDeeperStrategy(IStrategy):
stake_amount = filled_entries[0].stake_amount stake_amount = filled_entries[0].stake_amount
# This then calculates current safety order size # This then calculates current safety order size
stake_amount = stake_amount * (1 + (count_of_entries * 0.25)) stake_amount = stake_amount * (1 + (count_of_entries * 0.25))
return stake_amount, '1/3rd_increase' return stake_amount, "1/3rd_increase"
except Exception as exception: except Exception as exception:
return None return None
@@ -951,8 +940,7 @@ If the cancellation of the original order fails, then the order will not be repl
Entry Orders that are cancelled via the above methods will not have this callback called. Be sure to update timeout values to match your expectations. Entry Orders that are cancelled via the above methods will not have this callback called. Be sure to update timeout values to match your expectations.
```python ```python
from freqtrade.persistence import Trade # Default imports
from datetime import timedelta, datetime
class AwesomeStrategy(IStrategy): class AwesomeStrategy(IStrategy):
@@ -977,13 +965,18 @@ class AwesomeStrategy(IStrategy):
:param proposed_rate: Rate, calculated based on pricing settings in entry_pricing. :param proposed_rate: Rate, calculated based on pricing settings in entry_pricing.
:param current_order_rate: Rate of the existing order in place. :param current_order_rate: Rate of the existing order in place.
:param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal. :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal.
:param side: 'long' or 'short' - indicating the direction of the proposed trade :param side: "long" or "short" - indicating the direction of the proposed trade
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy. :param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
:return float: New entry price value if provided :return float: New entry price value if provided
""" """
# Limit orders to use and follow SMA200 as price target for the first 10 minutes since entry trigger for BTC/USDT pair. # Limit orders to use and follow SMA200 as price target for the first 10 minutes since entry trigger for BTC/USDT pair.
if pair == 'BTC/USDT' and entry_tag == 'long_sma200' and side == 'long' and (current_time - timedelta(minutes=10)) > trade.open_date_utc: if (
pair == "BTC/USDT"
and entry_tag == "long_sma200"
and side == "long"
and (current_time - timedelta(minutes=10)) > trade.open_date_utc
):
# just cancel the order if it has been filled more than half of the amount # just cancel the order if it has been filled more than half of the amount
if order.filled > order.remaining: if order.filled > order.remaining:
return None return None
@@ -991,7 +984,7 @@ class AwesomeStrategy(IStrategy):
dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe)
current_candle = dataframe.iloc[-1].squeeze() current_candle = dataframe.iloc[-1].squeeze()
# desired price # desired price
return current_candle['sma_200'] return current_candle["sma_200"]
# default: maintain existing order # default: maintain existing order
return current_order_rate return current_order_rate
``` ```
@@ -1006,6 +999,8 @@ Values that are above `max_leverage` will be adjusted to `max_leverage`.
For markets / exchanges that don't support leverage, this method is ignored. For markets / exchanges that don't support leverage, this method is ignored.
``` python ``` python
# Default imports
class AwesomeStrategy(IStrategy): class AwesomeStrategy(IStrategy):
def leverage(self, pair: str, current_time: datetime, current_rate: float, def leverage(self, pair: str, current_time: datetime, current_rate: float,
proposed_leverage: float, max_leverage: float, entry_tag: Optional[str], side: str, proposed_leverage: float, max_leverage: float, entry_tag: Optional[str], side: str,
@@ -1019,7 +1014,7 @@ class AwesomeStrategy(IStrategy):
:param proposed_leverage: A leverage proposed by the bot. :param proposed_leverage: A leverage proposed by the bot.
:param max_leverage: Max leverage allowed on this pair :param max_leverage: Max leverage allowed on this pair
:param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal. :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal.
:param side: 'long' or 'short' - indicating the direction of the proposed trade :param side: "long" or "short" - indicating the direction of the proposed trade
:return: A leverage amount, which is between 1.0 and max_leverage. :return: A leverage amount, which is between 1.0 and max_leverage.
""" """
return 1.0 return 1.0
@@ -1036,6 +1031,8 @@ It will be called independent of the order type (entry, exit, stoploss or positi
Assuming that your strategy needs to store the high value of the candle at trade entry, this is possible with this callback as the following example show. Assuming that your strategy needs to store the high value of the candle at trade entry, this is possible with this callback as the following example show.
``` python ``` python
# Default imports
class AwesomeStrategy(IStrategy): class AwesomeStrategy(IStrategy):
def order_filled(self, pair: str, trade: Trade, order: Order, current_time: datetime, **kwargs) -> None: def order_filled(self, pair: str, trade: Trade, order: Order, current_time: datetime, **kwargs) -> None:
""" """
@@ -1052,7 +1049,7 @@ class AwesomeStrategy(IStrategy):
last_candle = dataframe.iloc[-1].squeeze() last_candle = dataframe.iloc[-1].squeeze()
if (trade.nr_of_successful_entries == 1) and (order.ft_order_side == trade.entry_side): if (trade.nr_of_successful_entries == 1) and (order.ft_order_side == trade.entry_side):
trade.set_custom_data(key='entry_candle_high', value=last_candle['high']) trade.set_custom_data(key="entry_candle_high", value=last_candle["high"])
return None return None

View File

@@ -158,7 +158,7 @@ Out of the box, freqtrade installs the following technical libraries:
- [ta-lib](https://ta-lib.github.io/ta-lib-python/) - [ta-lib](https://ta-lib.github.io/ta-lib-python/)
- [pandas-ta](https://twopirllc.github.io/pandas-ta/) - [pandas-ta](https://twopirllc.github.io/pandas-ta/)
- [technical](https://github.com/freqtrade/technical/) - [technical](https://technical.freqtrade.io)
Additional technical libraries can be installed as necessary, or custom indicators may be written / invented by the strategy author. Additional technical libraries can be installed as necessary, or custom indicators may be written / invented by the strategy author.
@@ -407,6 +407,8 @@ Currently this is `pair`, which can be accessed using `metadata['pair']` - and w
The Metadata-dict should not be modified and does not persist information across multiple calls. The Metadata-dict should not be modified and does not persist information across multiple calls.
Instead, have a look at the [Storing information](strategy-advanced.md#storing-information-persistent) section. Instead, have a look at the [Storing information](strategy-advanced.md#storing-information-persistent) section.
--8<-- "includes/strategy-imports.md"
## Strategy file loading ## Strategy file loading
By default, freqtrade will attempt to load strategies from all `.py` files within `user_data/strategies`. By default, freqtrade will attempt to load strategies from all `.py` files within `user_data/strategies`.
@@ -715,6 +717,7 @@ This is where calling `self.dp.current_whitelist()` comes in handy.
??? Note "Plotting with current_whitelist" ??? Note "Plotting with current_whitelist"
Current whitelist is not supported for `plot-dataframe`, as this command is usually used by providing an explicit pairlist - and would therefore make the return values of this method misleading. Current whitelist is not supported for `plot-dataframe`, as this command is usually used by providing an explicit pairlist - and would therefore make the return values of this method misleading.
It's also not supported for freqUI visualization in [webserver mode](utils.md#webserver-mode) - as the configuration for webserver mode doesn't require a pairlist to be set.
### *get_pair_dataframe(pair, timeframe)* ### *get_pair_dataframe(pair, timeframe)*

View File

@@ -13,19 +13,22 @@ Please follow the [documentation](https://www.freqtrade.io/en/stable/data-downlo
import os import os
from pathlib import Path from pathlib import Path
# Change directory # Change directory
# Modify this cell to insure that the output shows the correct path. # Modify this cell to insure that the output shows the correct path.
# Define all paths relative to the project root shown in the cell output # Define all paths relative to the project root shown in the cell output
project_root = "somedir/freqtrade" project_root = "somedir/freqtrade"
i=0 i = 0
try: try:
os.chdir(project_root) os.chdir(project_root)
assert Path('LICENSE').is_file() if not Path("LICENSE").is_file():
except: i = 0
while i<4 and (not Path('LICENSE').is_file()): while i < 4 and (not Path("LICENSE").is_file()):
os.chdir(Path(Path.cwd(), '../')) os.chdir(Path(Path.cwd(), "../"))
i+=1 i += 1
project_root = Path.cwd() project_root = Path.cwd()
except FileNotFoundError:
print("Please define the project root relative to the current directory")
print(Path.cwd()) print(Path.cwd())
``` ```
@@ -35,6 +38,7 @@ print(Path.cwd())
```python ```python
from freqtrade.configuration import Configuration from freqtrade.configuration import Configuration
# Customize these according to your needs. # Customize these according to your needs.
# Initialize empty configuration object # Initialize empty configuration object
@@ -58,12 +62,14 @@ pair = "BTC/USDT"
from freqtrade.data.history import load_pair_history from freqtrade.data.history import load_pair_history
from freqtrade.enums import CandleType from freqtrade.enums import CandleType
candles = load_pair_history(datadir=data_location,
timeframe=config["timeframe"], candles = load_pair_history(
pair=pair, datadir=data_location,
data_format = "json", # Make sure to update this to your data timeframe=config["timeframe"],
candle_type=CandleType.SPOT, pair=pair,
) data_format="json", # Make sure to update this to your data
candle_type=CandleType.SPOT,
)
# Confirm success # Confirm success
print(f"Loaded {len(candles)} rows of data for {pair} from {data_location}") print(f"Loaded {len(candles)} rows of data for {pair} from {data_location}")
@@ -76,14 +82,16 @@ candles.head()
```python ```python
# Load strategy using values set above # Load strategy using values set above
from freqtrade.resolvers import StrategyResolver
from freqtrade.data.dataprovider import DataProvider from freqtrade.data.dataprovider import DataProvider
from freqtrade.resolvers import StrategyResolver
strategy = StrategyResolver.load_strategy(config) strategy = StrategyResolver.load_strategy(config)
strategy.dp = DataProvider(config, None, None) strategy.dp = DataProvider(config, None, None)
strategy.ft_bot_start() strategy.ft_bot_start()
# Generate buy/sell signals using strategy # Generate buy/sell signals using strategy
df = strategy.analyze_ticker(candles, {'pair': pair}) df = strategy.analyze_ticker(candles, {"pair": pair})
df.tail() df.tail()
``` ```
@@ -102,7 +110,7 @@ df.tail()
```python ```python
# Report results # Report results
print(f"Generated {df['enter_long'].sum()} entry signals") print(f"Generated {df['enter_long'].sum()} entry signals")
data = df.set_index('date', drop=False) data = df.set_index("date", drop=False)
data.tail() data.tail()
``` ```
@@ -119,10 +127,13 @@ Analyze a trades dataframe (also used below for plotting)
```python ```python
from freqtrade.data.btanalysis import load_backtest_data, load_backtest_stats from freqtrade.data.btanalysis import load_backtest_data, load_backtest_stats
# if backtest_dir points to a directory, it'll automatically load the last backtest file. # if backtest_dir points to a directory, it'll automatically load the last backtest file.
backtest_dir = config["user_data_dir"] / "backtest_results" backtest_dir = config["user_data_dir"] / "backtest_results"
# backtest_dir can also point to a specific file # backtest_dir can also point to a specific file
# backtest_dir = config["user_data_dir"] / "backtest_results/backtest-result-2020-07-01_20-04-22.json" # backtest_dir = (
# config["user_data_dir"] / "backtest_results/backtest-result-2020-07-01_20-04-22.json"
# )
``` ```
@@ -131,24 +142,24 @@ backtest_dir = config["user_data_dir"] / "backtest_results"
# This contains all information used to generate the backtest result. # This contains all information used to generate the backtest result.
stats = load_backtest_stats(backtest_dir) stats = load_backtest_stats(backtest_dir)
strategy = 'SampleStrategy' strategy = "SampleStrategy"
# All statistics are available per strategy, so if `--strategy-list` was used during backtest, this will be reflected here as well. # All statistics are available per strategy, so if `--strategy-list` was used during backtest,
# this will be reflected here as well.
# Example usages: # Example usages:
print(stats['strategy'][strategy]['results_per_pair']) print(stats["strategy"][strategy]["results_per_pair"])
# Get pairlist used for this backtest # Get pairlist used for this backtest
print(stats['strategy'][strategy]['pairlist']) print(stats["strategy"][strategy]["pairlist"])
# Get market change (average change of all pairs from start to end of the backtest period) # Get market change (average change of all pairs from start to end of the backtest period)
print(stats['strategy'][strategy]['market_change']) print(stats["strategy"][strategy]["market_change"])
# Maximum drawdown () # Maximum drawdown ()
print(stats['strategy'][strategy]['max_drawdown']) print(stats["strategy"][strategy]["max_drawdown"])
# Maximum drawdown start and end # Maximum drawdown start and end
print(stats['strategy'][strategy]['drawdown_start']) print(stats["strategy"][strategy]["drawdown_start"])
print(stats['strategy'][strategy]['drawdown_end']) print(stats["strategy"][strategy]["drawdown_end"])
# Get strategy comparison (only relevant if multiple strategies were compared) # Get strategy comparison (only relevant if multiple strategies were compared)
print(stats['strategy_comparison']) print(stats["strategy_comparison"])
``` ```
@@ -166,24 +177,25 @@ trades.groupby("pair")["exit_reason"].value_counts()
```python ```python
# Plotting equity line (starting with 0 on day 1 and adding daily profit for each backtested day) # Plotting equity line (starting with 0 on day 1 and adding daily profit for each backtested day)
import pandas as pd
import plotly.express as px
from freqtrade.configuration import Configuration from freqtrade.configuration import Configuration
from freqtrade.data.btanalysis import load_backtest_stats from freqtrade.data.btanalysis import load_backtest_stats
import plotly.express as px
import pandas as pd
# strategy = 'SampleStrategy' # strategy = 'SampleStrategy'
# config = Configuration.from_files(["user_data/config.json"]) # config = Configuration.from_files(["user_data/config.json"])
# backtest_dir = config["user_data_dir"] / "backtest_results" # backtest_dir = config["user_data_dir"] / "backtest_results"
stats = load_backtest_stats(backtest_dir) stats = load_backtest_stats(backtest_dir)
strategy_stats = stats['strategy'][strategy] strategy_stats = stats["strategy"][strategy]
df = pd.DataFrame(columns=['dates','equity'], data=strategy_stats['daily_profit']) df = pd.DataFrame(columns=["dates", "equity"], data=strategy_stats["daily_profit"])
df['equity_daily'] = df['equity'].cumsum() df["equity_daily"] = df["equity"].cumsum()
fig = px.line(df, x="dates", y="equity_daily") fig = px.line(df, x="dates", y="equity_daily")
fig.show() fig.show()
``` ```
### Load live trading results into a pandas dataframe ### Load live trading results into a pandas dataframe
@@ -194,6 +206,7 @@ In case you did already some trading and want to analyze your performance
```python ```python
from freqtrade.data.btanalysis import load_trades_from_db from freqtrade.data.btanalysis import load_trades_from_db
# Fetch trades from database # Fetch trades from database
trades = load_trades_from_db("sqlite:///tradesv3.sqlite") trades = load_trades_from_db("sqlite:///tradesv3.sqlite")
@@ -210,8 +223,9 @@ This can be useful to find the best `max_open_trades` parameter, when used with
```python ```python
from freqtrade.data.btanalysis import analyze_trade_parallelism from freqtrade.data.btanalysis import analyze_trade_parallelism
# Analyze the above # Analyze the above
parallel_trades = analyze_trade_parallelism(trades, '5m') parallel_trades = analyze_trade_parallelism(trades, "5m")
parallel_trades.plot() parallel_trades.plot()
``` ```
@@ -222,23 +236,23 @@ Freqtrade offers interactive plotting capabilities based on plotly.
```python ```python
from freqtrade.plot.plotting import generate_candlestick_graph from freqtrade.plot.plotting import generate_candlestick_graph
# Limit graph period to keep plotly quick and reactive # Limit graph period to keep plotly quick and reactive
# Filter trades to one pair # Filter trades to one pair
trades_red = trades.loc[trades['pair'] == pair] trades_red = trades.loc[trades["pair"] == pair]
data_red = data['2019-06-01':'2019-06-10'] data_red = data["2019-06-01":"2019-06-10"]
# Generate candlestick graph # Generate candlestick graph
graph = generate_candlestick_graph(pair=pair, graph = generate_candlestick_graph(
data=data_red, pair=pair,
trades=trades_red, data=data_red,
indicators1=['sma20', 'ema50', 'ema55'], trades=trades_red,
indicators2=['rsi', 'macd', 'macdsignal', 'macdhist'] indicators1=["sma20", "ema50", "ema55"],
) indicators2=["rsi", "macd", "macdsignal", "macdhist"],
)
``` ```
@@ -248,7 +262,6 @@ graph = generate_candlestick_graph(pair=pair,
# Render graph in a separate window # Render graph in a separate window
graph.show(renderer="browser") graph.show(renderer="browser")
``` ```
## Plot average profit per trade as distribution graph ## Plot average profit per trade as distribution graph
@@ -257,12 +270,12 @@ graph.show(renderer="browser")
```python ```python
import plotly.figure_factory as ff import plotly.figure_factory as ff
hist_data = [trades.profit_ratio] hist_data = [trades.profit_ratio]
group_labels = ['profit_ratio'] # name of the dataset group_labels = ["profit_ratio"] # name of the dataset
fig = ff.create_distplot(hist_data, group_labels, bin_size=0.01) fig = ff.create_distplot(hist_data, group_labels, bin_size=0.01)
fig.show() fig.show()
``` ```
Feel free to submit an issue or Pull Request enhancing this document if you would like to share ideas on how to best analyze the data. Feel free to submit an issue or Pull Request enhancing this document if you would like to share ideas on how to best analyze the data.

View File

@@ -11,3 +11,7 @@
.rst-versions .rst-other-versions { .rst-versions .rst-other-versions {
color: white; color: white;
} }
.md-version__list {
font-weight: 500 !important;
}

View File

@@ -18,7 +18,7 @@ The following attributes / properties are available for each individual trade -
| `open_rate` | float | Rate this trade was entered at (Avg. entry rate in case of trade-adjustments). | | `open_rate` | float | Rate this trade was entered at (Avg. entry rate in case of trade-adjustments). |
| `close_rate` | float | Close rate - only set when is_open = False. | | `close_rate` | float | Close rate - only set when is_open = False. |
| `stake_amount` | float | Amount in Stake (or Quote) currency. | | `stake_amount` | float | Amount in Stake (or Quote) currency. |
| `amount` | float | Amount in Asset / Base currency that is currently owned. | | `amount` | float | Amount in Asset / Base currency that is currently owned. Will be 0.0 until the initial order fills. |
| `open_date` | datetime | Timestamp when trade was opened **use `open_date_utc` instead** | | `open_date` | datetime | Timestamp when trade was opened **use `open_date_utc` instead** |
| `open_date_utc` | datetime | Timestamp when trade was opened - in UTC. | | `open_date_utc` | datetime | Timestamp when trade was opened - in UTC. |
| `close_date` | datetime | Timestamp when trade was closed **use `close_date_utc` instead** | | `close_date` | datetime | Timestamp when trade was closed **use `close_date_utc` instead** |
@@ -130,20 +130,20 @@ Most properties here can be None as they are dependent on the exchange response.
| Attribute | DataType | Description | | Attribute | DataType | Description |
|------------|-------------|-------------| |------------|-------------|-------------|
`trade` | Trade | Trade object this order is attached to | `trade` | Trade | Trade object this order is attached to |
`ft_pair` | string | Pair this order is for | `ft_pair` | string | Pair this order is for |
`ft_is_open` | boolean | is the order filled? | `ft_is_open` | boolean | is the order filled? |
`order_type` | string | Order type as defined on the exchange - usually market, limit or stoploss | `order_type` | string | Order type as defined on the exchange - usually market, limit or stoploss |
`status` | string | Status as defined by ccxt. Usually open, closed, expired or canceled | `status` | string | Status as defined by ccxt. Usually open, closed, expired or canceled |
`side` | string | Buy or Sell | `side` | string | Buy or Sell |
`price` | float | Price the order was placed at | `price` | float | Price the order was placed at |
`average` | float | Average price the order filled at | `average` | float | Average price the order filled at |
`amount` | float | Amount in base currency | `amount` | float | Amount in base currency |
`filled` | float | Filled amount (in base currency) | `filled` | float | Filled amount (in base currency) |
`remaining` | float | Remaining amount | `remaining` | float | Remaining amount |
`cost` | float | Cost of the order - usually average * filled (*Exchange dependent on futures, may contain the cost with or without leverage and may be in contracts.*) | `cost` | float | Cost of the order - usually average * filled (*Exchange dependent on futures, may contain the cost with or without leverage and may be in contracts.*) |
`stake_amount` | float | Stake amount used for this order. *Added in 2023.7.* | `stake_amount` | float | Stake amount used for this order. *Added in 2023.7.* |
`order_date` | datetime | Order creation date **use `order_date_utc` instead** | `order_date` | datetime | Order creation date **use `order_date_utc` instead** |
`order_date_utc` | datetime | Order creation date (in UTC) | `order_date_utc` | datetime | Order creation date (in UTC) |
`order_fill_date` | datetime | Order fill date **use `order_fill_utc` instead** | `order_fill_date` | datetime | Order fill date **use `order_fill_utc` instead** |
`order_fill_date_utc` | datetime | Order fill date | `order_fill_date_utc` | datetime | Order fill date |

View File

@@ -418,8 +418,9 @@ Common arguments:
``` ```
By default, only active pairs/markets are shown. Active pairs/markets are those that can currently be traded By default, only active pairs/markets are shown. Active pairs/markets are those that can currently be traded on the exchange.
on the exchange. The see the list of all pairs/markets (not only the active ones), use the `-a`/`-all` option. You can use the `-a`/`-all` option to see the list of all pairs/markets, including the inactive ones.
Pairs may be listed as untradeable if the smallest tradeable price for the market is very small, i.e. less than `1e-11` (`0.00000000001`)
Pairs/markets are sorted by its symbol string in the printed output. Pairs/markets are sorted by its symbol string in the printed output.

View File

@@ -1,6 +1,6 @@
"""Freqtrade bot""" """Freqtrade bot"""
__version__ = "2024.7.1" __version__ = "2024.9"
if "dev" in __version__: if "dev" in __version__:
from pathlib import Path from pathlib import Path

View File

@@ -15,6 +15,7 @@ from freqtrade.commands.data_commands import (
start_convert_trades, start_convert_trades,
start_download_data, start_download_data,
start_list_data, start_list_data,
start_list_trades_data,
) )
from freqtrade.commands.db_commands import start_convert_db from freqtrade.commands.db_commands import start_convert_db
from freqtrade.commands.deploy_commands import ( from freqtrade.commands.deploy_commands import (

View File

@@ -132,7 +132,15 @@ ARGS_CONVERT_TRADES = [
"trading_mode", "trading_mode",
] ]
ARGS_LIST_DATA = ["exchange", "dataformat_ohlcv", "pairs", "trading_mode", "show_timerange"] ARGS_LIST_DATA = [
"exchange",
"dataformat_ohlcv",
"dataformat_trades",
"trades",
"pairs",
"trading_mode",
"show_timerange",
]
ARGS_DOWNLOAD_DATA = [ ARGS_DOWNLOAD_DATA = [
"pairs", "pairs",
@@ -220,6 +228,8 @@ ARGS_ANALYZE_ENTRIES_EXITS = [
"enter_reason_list", "enter_reason_list",
"exit_reason_list", "exit_reason_list",
"indicator_list", "indicator_list",
"entry_only",
"exit_only",
"timerange", "timerange",
"analysis_rejected", "analysis_rejected",
"analysis_to_csv", "analysis_to_csv",

View File

@@ -274,8 +274,6 @@ def start_new_config(args: Dict[str, Any]) -> None:
def start_show_config(args: Dict[str, Any]) -> None: def start_show_config(args: Dict[str, Any]) -> None:
config = setup_utils_configuration(args, RunMode.UTIL_EXCHANGE, set_dry=False) config = setup_utils_configuration(args, RunMode.UTIL_EXCHANGE, set_dry=False)
# TODO: Sanitize from sensitive info before printing
print("Your combined configuration is:") print("Your combined configuration is:")
config_sanitized = sanitize_config( config_sanitized = sanitize_config(
config["original_config"], show_sensitive=args.get("show_sensitive", False) config["original_config"], show_sensitive=args.get("show_sensitive", False)

View File

@@ -446,8 +446,12 @@ AVAILABLE_CLI_OPTIONS = {
), ),
"download_trades": Arg( "download_trades": Arg(
"--dl-trades", "--dl-trades",
help="Download trades instead of OHLCV data. The bot will resample trades to the " help="Download trades instead of OHLCV data.",
"desired timeframe as specified as --timeframes/-t.", action="store_true",
),
"trades": Arg(
"--trades",
help="Work on trades data instead of OHLCV data.",
action="store_true", action="store_true",
), ),
"convert_trades": Arg( "convert_trades": Arg(
@@ -715,6 +719,12 @@ AVAILABLE_CLI_OPTIONS = {
nargs="+", nargs="+",
default=[], default=[],
), ),
"entry_only": Arg(
"--entry-only", help=("Only analyze entry signals."), action="store_true", default=False
),
"exit_only": Arg(
"--exit-only", help=("Only analyze exit signals."), action="store_true", default=False
),
"analysis_rejected": Arg( "analysis_rejected": Arg(
"--rejected-signals", "--rejected-signals",
help="Analyse rejected signals", help="Analyse rejected signals",

View File

@@ -14,6 +14,7 @@ from freqtrade.data.history import download_data_main
from freqtrade.enums import CandleType, RunMode, TradingMode from freqtrade.enums import CandleType, RunMode, TradingMode
from freqtrade.exceptions import ConfigurationError from freqtrade.exceptions import ConfigurationError
from freqtrade.exchange import timeframe_to_minutes from freqtrade.exchange import timeframe_to_minutes
from freqtrade.misc import plural
from freqtrade.plugins.pairlist.pairlist_helpers import dynamic_expand_pairlist from freqtrade.plugins.pairlist.pairlist_helpers import dynamic_expand_pairlist
from freqtrade.resolvers import ExchangeResolver from freqtrade.resolvers import ExchangeResolver
from freqtrade.util import print_rich_table from freqtrade.util import print_rich_table
@@ -115,9 +116,13 @@ def start_convert_data(args: Dict[str, Any], ohlcv: bool = True) -> None:
def start_list_data(args: Dict[str, Any]) -> None: def start_list_data(args: Dict[str, Any]) -> None:
""" """
List available backtest data List available OHLCV data
""" """
if args["trades"]:
start_list_trades_data(args)
return
config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE) config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE)
from freqtrade.data.history import get_datahandler from freqtrade.data.history import get_datahandler
@@ -127,7 +132,6 @@ def start_list_data(args: Dict[str, Any]) -> None:
paircombs = dhc.ohlcv_get_available_data( paircombs = dhc.ohlcv_get_available_data(
config["datadir"], config.get("trading_mode", TradingMode.SPOT) config["datadir"], config.get("trading_mode", TradingMode.SPOT)
) )
if args["pairs"]: if args["pairs"]:
paircombs = [comb for comb in paircombs if comb[0] in args["pairs"]] paircombs = [comb for comb in paircombs if comb[0] in args["pairs"]]
title = f"Found {len(paircombs)} pair / timeframe combinations." title = f"Found {len(paircombs)} pair / timeframe combinations."
@@ -171,3 +175,51 @@ def start_list_data(args: Dict[str, Any]) -> None:
summary=title, summary=title,
table_kwargs={"min_width": 50}, table_kwargs={"min_width": 50},
) )
def start_list_trades_data(args: Dict[str, Any]) -> None:
"""
List available Trades data
"""
config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE)
from freqtrade.data.history import get_datahandler
dhc = get_datahandler(config["datadir"], config["dataformat_trades"])
paircombs = dhc.trades_get_available_data(
config["datadir"], config.get("trading_mode", TradingMode.SPOT)
)
if args["pairs"]:
paircombs = [comb for comb in paircombs if comb in args["pairs"]]
title = f"Found trades data for {len(paircombs)} {plural(len(paircombs), 'pair')}."
if not config.get("show_timerange"):
print_rich_table(
[(pair, config.get("candle_type_def", CandleType.SPOT)) for pair in sorted(paircombs)],
("Pair", "Type"),
title,
table_kwargs={"min_width": 50},
)
else:
paircombs1 = [
(pair, *dhc.trades_data_min_max(pair, config.get("trading_mode", TradingMode.SPOT)))
for pair in paircombs
]
print_rich_table(
[
(
pair,
config.get("candle_type_def", CandleType.SPOT),
start.strftime(DATETIME_PRINT_FORMAT),
end.strftime(DATETIME_PRINT_FORMAT),
str(length),
)
for pair, start, end, length in sorted(paircombs1, key=lambda x: (x[0]))
],
("Pair", "Type", "From", "To", "Trades"),
summary=title,
table_kwargs={"min_width": 50},
)

View File

@@ -12,9 +12,9 @@ from freqtrade.configuration import setup_utils_configuration
from freqtrade.enums import RunMode from freqtrade.enums import RunMode
from freqtrade.exceptions import ConfigurationError, OperationalException from freqtrade.exceptions import ConfigurationError, OperationalException
from freqtrade.exchange import list_available_exchanges, market_is_active from freqtrade.exchange import list_available_exchanges, market_is_active
from freqtrade.ft_types import ValidExchangesType
from freqtrade.misc import parse_db_uri_for_logging, plural from freqtrade.misc import parse_db_uri_for_logging, plural
from freqtrade.resolvers import ExchangeResolver, StrategyResolver from freqtrade.resolvers import ExchangeResolver, StrategyResolver
from freqtrade.types.valid_exchanges_type import ValidExchangesType
from freqtrade.util import print_rich_table from freqtrade.util import print_rich_table
@@ -32,7 +32,7 @@ def start_list_exchanges(args: Dict[str, Any]) -> None:
) )
if args["print_one_column"]: if args["print_one_column"]:
print("\n".join([e["name"] for e in available_exchanges])) print("\n".join([e["classname"] for e in available_exchanges]))
else: else:
if args["list_exchanges_all"]: if args["list_exchanges_all"]:
title = ( title = (
@@ -46,14 +46,20 @@ def start_list_exchanges(args: Dict[str, Any]) -> None:
table = Table(title=title) table = Table(title=title)
table.add_column("Exchange Name") table.add_column("Exchange Name")
table.add_column("Class Name")
table.add_column("Markets") table.add_column("Markets")
table.add_column("Reason") table.add_column("Reason")
for exchange in available_exchanges: for exchange in available_exchanges:
name = Text(exchange["name"]) name = Text(exchange["name"])
if exchange["supported"]: if exchange["supported"]:
name.append(" (Official)", style="italic") name.append(" (Supported)", style="italic")
name.stylize("green bold") name.stylize("green bold")
classname = Text(exchange["classname"])
if exchange["is_alias"]:
name.stylize("strike")
classname.stylize("strike")
classname.append(f" (use {exchange['alias_for']})", style="italic")
trade_modes = Text( trade_modes = Text(
", ".join( ", ".join(
@@ -68,6 +74,7 @@ def start_list_exchanges(args: Dict[str, Any]) -> None:
table.add_row( table.add_row(
name, name,
classname,
trade_modes, trade_modes,
exchange["comment"], exchange["comment"],
style=None if exchange["valid"] else "red", style=None if exchange["valid"] else "red",

View File

@@ -1,5 +1,6 @@
# flake8: noqa: F401 # flake8: noqa: F401
from freqtrade.configuration.asyncio_config import asyncio_setup
from freqtrade.configuration.config_secrets import sanitize_config from freqtrade.configuration.config_secrets import sanitize_config
from freqtrade.configuration.config_setup import setup_utils_configuration from freqtrade.configuration.config_setup import setup_utils_configuration
from freqtrade.configuration.config_validation import validate_config_consistency from freqtrade.configuration.config_validation import validate_config_consistency

View File

@@ -0,0 +1,10 @@
import sys
def asyncio_setup() -> None: # pragma: no cover
# Set eventloop for win32 setups
if sys.platform == "win32":
import asyncio
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())

View File

@@ -36,11 +36,6 @@ CONF_SCHEMA = {
"type": ["integer", "number"], "type": ["integer", "number"],
"minimum": -1, "minimum": -1,
}, },
"new_pairs_days": {
"description": "Download data of new pairs for given number of days",
"type": "integer",
"default": 30,
},
"timeframe": { "timeframe": {
"description": ( "description": (
f"The timeframe to use (e.g `1m`, `5m`, `15m`, `30m`, `1h` ...). {__IN_STRATEGY}" f"The timeframe to use (e.g `1m`, `5m`, `15m`, `30m`, `1h` ...). {__IN_STRATEGY}"
@@ -185,6 +180,7 @@ CONF_SCHEMA = {
"type": "boolean", "type": "boolean",
"default": False, "default": False,
}, },
# Lookahead analysis section
"minimum_trade_amount": { "minimum_trade_amount": {
"description": "Minimum amount for a trade - only used for lookahead-analysis", "description": "Minimum amount for a trade - only used for lookahead-analysis",
"type": "number", "type": "number",
@@ -480,6 +476,12 @@ CONF_SCHEMA = {
"type": "number", "type": "number",
"minimum": 0, "minimum": 0,
}, },
"unlock_at": {
"description": (
"Time when trading will be unlocked regularly. Format: HH:MM"
),
"type": "string",
},
"trade_limit": { "trade_limit": {
"description": "Minimum number of trades required during lookback period.", "description": "Minimum number of trades required during lookback period.",
"type": "number", "type": "number",
@@ -501,6 +503,7 @@ CONF_SCHEMA = {
"required": ["method"], "required": ["method"],
}, },
}, },
# RPC section
"telegram": { "telegram": {
"description": "Telegram settings.", "description": "Telegram settings.",
"type": "object", "type": "object",
@@ -701,6 +704,7 @@ CONF_SCHEMA = {
}, },
"required": ["enabled", "listen_ip_address", "listen_port", "username", "password"], "required": ["enabled", "listen_ip_address", "listen_port", "username", "password"],
}, },
# end of RPC section
"db_url": { "db_url": {
"description": "Database connection URL.", "description": "Database connection URL.",
"type": "string", "type": "string",
@@ -734,7 +738,7 @@ CONF_SCHEMA = {
"default": {}, "default": {},
"properties": { "properties": {
"process_throttle_secs": { "process_throttle_secs": {
"description": "Throttle time in seconds for processing.", "description": "Minimum loop duration for one bot iteration in seconds.",
"type": "integer", "type": "integer",
}, },
"interval": { "interval": {
@@ -763,11 +767,26 @@ CONF_SCHEMA = {
"description": f"Enable position adjustment. {__IN_STRATEGY}", "description": f"Enable position adjustment. {__IN_STRATEGY}",
"type": "boolean", "type": "boolean",
}, },
# Download data section
"new_pairs_days": {
"description": "Download data of new pairs for given number of days",
"type": "integer",
"default": 30,
},
"download_trades": {
"description": "Download trades data by default (instead of ohlcv data).",
"type": "boolean",
},
"max_entry_position_adjustment": { "max_entry_position_adjustment": {
"description": f"Maximum entry position adjustment allowed. {__IN_STRATEGY}", "description": f"Maximum entry position adjustment allowed. {__IN_STRATEGY}",
"type": ["integer", "number"], "type": ["integer", "number"],
"minimum": -1, "minimum": -1,
}, },
"add_config_files": {
"description": "Additional configuration files to load.",
"type": "array",
"items": {"type": "string"},
},
"orderflow": { "orderflow": {
"description": "Settings related to order flow.", "description": "Settings related to order flow.",
"type": "object", "type": "object",
@@ -853,6 +872,14 @@ CONF_SCHEMA = {
"items": {"type": "string"}, "items": {"type": "string"},
"uniqueItems": True, "uniqueItems": True,
}, },
"log_responses": {
"description": (
"Log responses from the exchange."
"Useful/required to debug issues with order processing."
),
"type": "boolean",
"default": False,
},
"unknown_fee_rate": { "unknown_fee_rate": {
"description": "Fee rate for unknown markets.", "description": "Fee rate for unknown markets.",
"type": "number", "type": "number",

View File

@@ -14,12 +14,16 @@ def sanitize_config(config: Config, *, show_sensitive: bool = False) -> Config:
return config return config
keys_to_remove = [ keys_to_remove = [
"exchange.key", "exchange.key",
"exchange.api_key",
"exchange.apiKey", "exchange.apiKey",
"exchange.secret", "exchange.secret",
"exchange.password", "exchange.password",
"exchange.uid", "exchange.uid",
"exchange.account_id",
"exchange.accountId", "exchange.accountId",
"exchange.wallet_address",
"exchange.walletAddress", "exchange.walletAddress",
"exchange.private_key",
"exchange.privateKey", "exchange.privateKey",
"telegram.token", "telegram.token",
"telegram.chat_id", "telegram.chat_id",
@@ -33,8 +37,10 @@ def sanitize_config(config: Config, *, show_sensitive: bool = False) -> Config:
nested_config = config nested_config = config
for nested_key in nested_keys[:-1]: for nested_key in nested_keys[:-1]:
nested_config = nested_config.get(nested_key, {}) nested_config = nested_config.get(nested_key, {})
nested_config[nested_keys[-1]] = "REDACTED" if nested_keys[-1] in nested_config:
nested_config[nested_keys[-1]] = "REDACTED"
else: else:
config[key] = "REDACTED" if key in config:
config[key] = "REDACTED"
return config return config

View File

@@ -1,6 +1,7 @@
import logging import logging
from collections import Counter from collections import Counter
from copy import deepcopy from copy import deepcopy
from datetime import datetime
from typing import Any, Dict from typing import Any, Dict
from jsonschema import Draft4Validator, validators from jsonschema import Draft4Validator, validators
@@ -201,16 +202,32 @@ def _validate_protections(conf: Dict[str, Any]) -> None:
""" """
for prot in conf.get("protections", []): for prot in conf.get("protections", []):
parsed_unlock_at = None
if (config_unlock_at := prot.get("unlock_at")) is not None:
try:
parsed_unlock_at = datetime.strptime(config_unlock_at, "%H:%M")
except ValueError:
raise ConfigurationError(f"Invalid date format for unlock_at: {config_unlock_at}.")
if "stop_duration" in prot and "stop_duration_candles" in prot: if "stop_duration" in prot and "stop_duration_candles" in prot:
raise ConfigurationError( raise ConfigurationError(
"Protections must specify either `stop_duration` or `stop_duration_candles`.\n" "Protections must specify either `stop_duration` or `stop_duration_candles`.\n"
f"Please fix the protection {prot.get('method')}" f"Please fix the protection {prot.get('method')}."
) )
if "lookback_period" in prot and "lookback_period_candles" in prot: if "lookback_period" in prot and "lookback_period_candles" in prot:
raise ConfigurationError( raise ConfigurationError(
"Protections must specify either `lookback_period` or `lookback_period_candles`.\n" "Protections must specify either `lookback_period` or `lookback_period_candles`.\n"
f"Please fix the protection {prot.get('method')}" f"Please fix the protection {prot.get('method')}."
)
if parsed_unlock_at is not None and (
"stop_duration" in prot or "stop_duration_candles" in prot
):
raise ConfigurationError(
"Protections must specify either `unlock_at`, `stop_duration` or "
"`stop_duration_candles`.\n"
f"Please fix the protection {prot.get('method')}."
) )

View File

@@ -15,7 +15,14 @@ from freqtrade.configuration.directory_operations import create_datadir, create_
from freqtrade.configuration.environment_vars import enironment_vars_to_dict from freqtrade.configuration.environment_vars import enironment_vars_to_dict
from freqtrade.configuration.load_config import load_file, load_from_files from freqtrade.configuration.load_config import load_file, load_from_files
from freqtrade.constants import Config from freqtrade.constants import Config
from freqtrade.enums import NON_UTIL_MODES, TRADE_MODES, CandleType, RunMode, TradingMode from freqtrade.enums import (
NON_UTIL_MODES,
TRADE_MODES,
CandleType,
MarginMode,
RunMode,
TradingMode,
)
from freqtrade.exceptions import OperationalException from freqtrade.exceptions import OperationalException
from freqtrade.loggers import setup_logging from freqtrade.loggers import setup_logging
from freqtrade.misc import deep_merge_dicts, parse_db_uri_for_logging from freqtrade.misc import deep_merge_dicts, parse_db_uri_for_logging
@@ -389,6 +396,7 @@ class Configuration:
config.get("trading_mode", "spot") or "spot" config.get("trading_mode", "spot") or "spot"
) )
config["trading_mode"] = TradingMode(config.get("trading_mode", "spot") or "spot") config["trading_mode"] = TradingMode(config.get("trading_mode", "spot") or "spot")
config["margin_mode"] = MarginMode(config.get("margin_mode", "") or "")
self._args_to_config( self._args_to_config(
config, argname="candle_types", logstring="Detected --candle-types: {}" config, argname="candle_types", logstring="Detected --candle-types: {}"
) )
@@ -399,6 +407,8 @@ class Configuration:
("enter_reason_list", "Analysis enter tag list: {}"), ("enter_reason_list", "Analysis enter tag list: {}"),
("exit_reason_list", "Analysis exit tag list: {}"), ("exit_reason_list", "Analysis exit tag list: {}"),
("indicator_list", "Analysis indicator list: {}"), ("indicator_list", "Analysis indicator list: {}"),
("entry_only", "Only analyze entry signals: {}"),
("exit_only", "Only analyze exit signals: {}"),
("timerange", "Filter trades by timerange: {}"), ("timerange", "Filter trades by timerange: {}"),
("analysis_rejected", "Analyse rejected signals: {}"), ("analysis_rejected", "Analyse rejected signals: {}"),
("analysis_to_csv", "Store analysis tables to CSV: {}"), ("analysis_to_csv", "Store analysis tables to CSV: {}"),
@@ -468,7 +478,7 @@ class Configuration:
else: else:
logger.info(logstring.format(config[argname])) logger.info(logstring.format(config[argname]))
if deprecated_msg: if deprecated_msg:
warnings.warn(f"DEPRECATED: {deprecated_msg}", DeprecationWarning) warnings.warn(f"DEPRECATED: {deprecated_msg}", DeprecationWarning, stacklevel=1)
def _resolve_pairs_list(self, config: Config) -> None: def _resolve_pairs_list(self, config: Config) -> None:
""" """

View File

@@ -82,6 +82,11 @@ def create_userdata_dir(directory: str, create_dir: bool = False) -> Path:
for f in sub_dirs: for f in sub_dirs:
subfolder = folder / f subfolder = folder / f
if not subfolder.is_dir(): if not subfolder.is_dir():
if subfolder.exists() or subfolder.is_symlink():
raise OperationalException(
f"File `{subfolder}` exists already and is not a directory. "
"Freqtrade requires this to be a directory."
)
subfolder.mkdir(parents=False) subfolder.mkdir(parents=False)
return folder return folder

View File

@@ -42,6 +42,7 @@ HYPEROPT_LOSS_BUILTIN = [
AVAILABLE_PAIRLISTS = [ AVAILABLE_PAIRLISTS = [
"StaticPairList", "StaticPairList",
"VolumePairList", "VolumePairList",
"PercentChangePairList",
"ProducerPairList", "ProducerPairList",
"RemotePairList", "RemotePairList",
"MarketCapPairList", "MarketCapPairList",

View File

@@ -13,10 +13,10 @@ import pandas as pd
from freqtrade.constants import LAST_BT_RESULT_FN, IntOrInf from freqtrade.constants import LAST_BT_RESULT_FN, IntOrInf
from freqtrade.exceptions import ConfigurationError, OperationalException from freqtrade.exceptions import ConfigurationError, OperationalException
from freqtrade.ft_types import BacktestHistoryEntryType, BacktestResultType
from freqtrade.misc import file_dump_json, json_load from freqtrade.misc import file_dump_json, json_load
from freqtrade.optimize.backtest_caching import get_backtest_metadata_filename from freqtrade.optimize.backtest_caching import get_backtest_metadata_filename
from freqtrade.persistence import LocalTrade, Trade, init_db from freqtrade.persistence import LocalTrade, Trade, init_db
from freqtrade.types import BacktestHistoryEntryType, BacktestResultType
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -401,7 +401,15 @@ def analyze_trade_parallelism(results: pd.DataFrame, timeframe: str) -> pd.DataF
timeframe_freq = timeframe_to_resample_freq(timeframe) timeframe_freq = timeframe_to_resample_freq(timeframe)
dates = [ dates = [
pd.Series(pd.date_range(row[1]["open_date"], row[1]["close_date"], freq=timeframe_freq)) pd.Series(
pd.date_range(
row[1]["open_date"],
row[1]["close_date"],
freq=timeframe_freq,
# Exclude right boundary - the date is the candle open date.
inclusive="left",
)
)
for row in results[["open_date", "close_date"]].iterrows() for row in results[["open_date", "close_date"]].iterrows()
] ]
deltas = [len(x) for x in dates] deltas = [len(x) for x in dates]

View File

@@ -12,7 +12,7 @@ from typing import Tuple
import numpy as np import numpy as np
import pandas as pd import pandas as pd
from freqtrade.constants import DEFAULT_ORDERFLOW_COLUMNS from freqtrade.constants import DEFAULT_ORDERFLOW_COLUMNS, Config
from freqtrade.enums import RunMode from freqtrade.enums import RunMode
from freqtrade.exceptions import DependencyException from freqtrade.exceptions import DependencyException
@@ -63,7 +63,7 @@ def _calculate_ohlcv_candle_start_and_end(df: pd.DataFrame, timeframe: str):
def populate_dataframe_with_trades( def populate_dataframe_with_trades(
cached_grouped_trades: OrderedDict[Tuple[datetime, datetime], pd.DataFrame], cached_grouped_trades: OrderedDict[Tuple[datetime, datetime], pd.DataFrame],
config, config: Config,
dataframe: pd.DataFrame, dataframe: pd.DataFrame,
trades: pd.DataFrame, trades: pd.DataFrame,
) -> Tuple[pd.DataFrame, OrderedDict[Tuple[datetime, datetime], pd.DataFrame]]: ) -> Tuple[pd.DataFrame, OrderedDict[Tuple[datetime, datetime], pd.DataFrame]]:
@@ -78,6 +78,8 @@ def populate_dataframe_with_trades(
# create columns for trades # create columns for trades
_init_dataframe_with_trades_columns(dataframe) _init_dataframe_with_trades_columns(dataframe)
if trades is None or trades.empty:
return dataframe, cached_grouped_trades
try: try:
start_time = time.time() start_time = time.time()
@@ -88,7 +90,7 @@ def populate_dataframe_with_trades(
max_candles = config_orderflow["max_candles"] max_candles = config_orderflow["max_candles"]
start_date = dataframe.tail(max_candles).date.iat[0] start_date = dataframe.tail(max_candles).date.iat[0]
# slice of trades that are before current ohlcv candles to make groupby faster # slice of trades that are before current ohlcv candles to make groupby faster
trades = trades.loc[trades.candle_start >= start_date] trades = trades.loc[trades["candle_start"] >= start_date]
trades.reset_index(inplace=True, drop=True) trades.reset_index(inplace=True, drop=True)
# group trades by candle start # group trades by candle start

View File

@@ -23,7 +23,7 @@ from freqtrade.data.history import get_datahandler, load_pair_history
from freqtrade.enums import CandleType, RPCMessageType, RunMode, TradingMode from freqtrade.enums import CandleType, RPCMessageType, RunMode, TradingMode
from freqtrade.exceptions import ExchangeError, OperationalException from freqtrade.exceptions import ExchangeError, OperationalException
from freqtrade.exchange import Exchange, timeframe_to_prev_date, timeframe_to_seconds from freqtrade.exchange import Exchange, timeframe_to_prev_date, timeframe_to_seconds
from freqtrade.exchange.types import OrderBook from freqtrade.exchange.exchange_types import OrderBook
from freqtrade.misc import append_candles_to_dataframe from freqtrade.misc import append_candles_to_dataframe
from freqtrade.rpc import RPCManager from freqtrade.rpc import RPCManager
from freqtrade.rpc.rpc_types import RPCAnalyzedDFMsg from freqtrade.rpc.rpc_types import RPCAnalyzedDFMsg
@@ -520,21 +520,15 @@ class DataProvider:
return self._exchange.trades( return self._exchange.trades(
(pair, timeframe or self._config["timeframe"], _candle_type), copy=copy (pair, timeframe or self._config["timeframe"], _candle_type), copy=copy
) )
elif self.runmode in (RunMode.BACKTEST, RunMode.HYPEROPT): else:
_candle_type = (
CandleType.from_string(candle_type)
if candle_type != ""
else self._config["candle_type_def"]
)
data_handler = get_datahandler( data_handler = get_datahandler(
self._config["datadir"], data_format=self._config["dataformat_trades"] self._config["datadir"], data_format=self._config["dataformat_trades"]
) )
trades_df = data_handler.trades_load(pair, TradingMode.FUTURES) trades_df = data_handler.trades_load(
pair, self._config.get("trading_mode", TradingMode.SPOT)
)
return trades_df return trades_df
else:
return DataFrame()
def market(self, pair: str) -> Optional[Dict[str, Any]]: def market(self, pair: str) -> Optional[Dict[str, Any]]:
""" """
Return market data for the pair Return market data for the pair

View File

@@ -1,6 +1,6 @@
import logging import logging
from pathlib import Path from pathlib import Path
from typing import List from typing import Dict, List
import joblib import joblib
import pandas as pd import pandas as pd
@@ -8,6 +8,7 @@ import pandas as pd
from freqtrade.configuration import TimeRange from freqtrade.configuration import TimeRange
from freqtrade.constants import Config from freqtrade.constants import Config
from freqtrade.data.btanalysis import ( from freqtrade.data.btanalysis import (
BT_DATA_COLUMNS,
get_latest_backtest_filename, get_latest_backtest_filename,
load_backtest_data, load_backtest_data,
load_backtest_stats, load_backtest_stats,
@@ -47,9 +48,14 @@ def _load_signal_candles(backtest_dir: Path):
return _load_backtest_analysis_data(backtest_dir, "signals") return _load_backtest_analysis_data(backtest_dir, "signals")
def _process_candles_and_indicators(pairlist, strategy_name, trades, signal_candles): def _load_exit_signal_candles(backtest_dir: Path) -> Dict[str, Dict[str, pd.DataFrame]]:
analysed_trades_dict = {} return _load_backtest_analysis_data(backtest_dir, "exited")
analysed_trades_dict[strategy_name] = {}
def _process_candles_and_indicators(
pairlist, strategy_name, trades, signal_candles, date_col: str = "open_date"
):
analysed_trades_dict: Dict[str, Dict] = {strategy_name: {}}
try: try:
logger.info(f"Processing {strategy_name} : {len(pairlist)} pairs") logger.info(f"Processing {strategy_name} : {len(pairlist)} pairs")
@@ -57,7 +63,7 @@ def _process_candles_and_indicators(pairlist, strategy_name, trades, signal_cand
for pair in pairlist: for pair in pairlist:
if pair in signal_candles[strategy_name]: if pair in signal_candles[strategy_name]:
analysed_trades_dict[strategy_name][pair] = _analyze_candles_and_indicators( analysed_trades_dict[strategy_name][pair] = _analyze_candles_and_indicators(
pair, trades, signal_candles[strategy_name][pair] pair, trades, signal_candles[strategy_name][pair], date_col
) )
except Exception as e: except Exception as e:
print(f"Cannot process entry/exit reasons for {strategy_name}: ", e) print(f"Cannot process entry/exit reasons for {strategy_name}: ", e)
@@ -65,7 +71,9 @@ def _process_candles_and_indicators(pairlist, strategy_name, trades, signal_cand
return analysed_trades_dict return analysed_trades_dict
def _analyze_candles_and_indicators(pair, trades: pd.DataFrame, signal_candles: pd.DataFrame): def _analyze_candles_and_indicators(
pair: str, trades: pd.DataFrame, signal_candles: pd.DataFrame, date_col: str = "open_date"
) -> pd.DataFrame:
buyf = signal_candles buyf = signal_candles
if len(buyf) > 0: if len(buyf) > 0:
@@ -75,8 +83,8 @@ def _analyze_candles_and_indicators(pair, trades: pd.DataFrame, signal_candles:
trades_inds = pd.DataFrame() trades_inds = pd.DataFrame()
if trades_red.shape[0] > 0 and buyf.shape[0] > 0: if trades_red.shape[0] > 0 and buyf.shape[0] > 0:
for t, v in trades_red.open_date.items(): for t, v in trades_red.iterrows():
allinds = buyf.loc[(buyf["date"] < v)] allinds = buyf.loc[(buyf["date"] < v[date_col])]
if allinds.shape[0] > 0: if allinds.shape[0] > 0:
tmp_inds = allinds.iloc[[-1]] tmp_inds = allinds.iloc[[-1]]
@@ -235,7 +243,7 @@ def _select_rows_by_tags(df, enter_reason_list, exit_reason_list):
def prepare_results( def prepare_results(
analysed_trades, stratname, enter_reason_list, exit_reason_list, timerange=None analysed_trades, stratname, enter_reason_list, exit_reason_list, timerange=None
): ) -> pd.DataFrame:
res_df = pd.DataFrame() res_df = pd.DataFrame()
for pair, trades in analysed_trades[stratname].items(): for pair, trades in analysed_trades[stratname].items():
if trades.shape[0] > 0: if trades.shape[0] > 0:
@@ -252,8 +260,11 @@ def prepare_results(
def print_results( def print_results(
res_df: pd.DataFrame, res_df: pd.DataFrame,
exit_df: pd.DataFrame,
analysis_groups: List[str], analysis_groups: List[str],
indicator_list: List[str], indicator_list: List[str],
entry_only: bool,
exit_only: bool,
csv_path: Path, csv_path: Path,
rejected_signals=None, rejected_signals=None,
to_csv=False, to_csv=False,
@@ -278,9 +289,11 @@ def print_results(
for ind in indicator_list: for ind in indicator_list:
if ind in res_df: if ind in res_df:
available_inds.append(ind) available_inds.append(ind)
ilist = ["pair", "enter_reason", "exit_reason"] + available_inds
merged_df = _merge_dfs(res_df, exit_df, available_inds, entry_only, exit_only)
_print_table( _print_table(
res_df[ilist], merged_df,
sortcols=["exit_reason"], sortcols=["exit_reason"],
show_index=False, show_index=False,
name="Indicators:", name="Indicators:",
@@ -291,6 +304,36 @@ def print_results(
print("\\No trades to show") print("\\No trades to show")
def _merge_dfs(
entry_df: pd.DataFrame,
exit_df: pd.DataFrame,
available_inds: List[str],
entry_only: bool,
exit_only: bool,
):
merge_on = ["pair", "open_date"]
signal_wide_indicators = list(set(available_inds) - set(BT_DATA_COLUMNS))
columns_to_keep = merge_on + ["enter_reason", "exit_reason"]
if exit_df is None or exit_df.empty or entry_only is True:
return entry_df[columns_to_keep + available_inds]
if exit_only is True:
return pd.merge(
entry_df[columns_to_keep],
exit_df[merge_on + signal_wide_indicators],
on=merge_on,
suffixes=(" (entry)", " (exit)"),
)
return pd.merge(
entry_df[columns_to_keep + available_inds],
exit_df[merge_on + signal_wide_indicators],
on=merge_on,
suffixes=(" (entry)", " (exit)"),
)
def _print_table( def _print_table(
df: pd.DataFrame, sortcols=None, *, show_index=False, name=None, to_csv=False, csv_path: Path df: pd.DataFrame, sortcols=None, *, show_index=False, name=None, to_csv=False, csv_path: Path
): ):
@@ -316,9 +359,16 @@ def process_entry_exit_reasons(config: Config):
enter_reason_list = config.get("enter_reason_list", ["all"]) enter_reason_list = config.get("enter_reason_list", ["all"])
exit_reason_list = config.get("exit_reason_list", ["all"]) exit_reason_list = config.get("exit_reason_list", ["all"])
indicator_list = config.get("indicator_list", []) indicator_list = config.get("indicator_list", [])
entry_only = config.get("entry_only", False)
exit_only = config.get("exit_only", False)
do_rejected = config.get("analysis_rejected", False) do_rejected = config.get("analysis_rejected", False)
to_csv = config.get("analysis_to_csv", False) to_csv = config.get("analysis_to_csv", False)
csv_path = Path(config.get("analysis_csv_path", config["exportfilename"])) csv_path = Path(config.get("analysis_csv_path", config["exportfilename"]))
if entry_only is True and exit_only is True:
raise OperationalException(
"Cannot use --entry-only and --exit-only at the same time. Please choose one."
)
if to_csv and not csv_path.is_dir(): if to_csv and not csv_path.is_dir():
raise OperationalException(f"Specified directory {csv_path} does not exist.") raise OperationalException(f"Specified directory {csv_path} does not exist.")
@@ -333,6 +383,7 @@ def process_entry_exit_reasons(config: Config):
if trades is not None and not trades.empty: if trades is not None and not trades.empty:
signal_candles = _load_signal_candles(config["exportfilename"]) signal_candles = _load_signal_candles(config["exportfilename"])
exit_signals = _load_exit_signal_candles(config["exportfilename"])
rej_df = None rej_df = None
if do_rejected: if do_rejected:
@@ -345,22 +396,35 @@ def process_entry_exit_reasons(config: Config):
timerange=timerange, timerange=timerange,
) )
analysed_trades_dict = _process_candles_and_indicators( entry_df = _generate_dfs(
config["exchange"]["pair_whitelist"], strategy_name, trades, signal_candles config["exchange"]["pair_whitelist"],
)
res_df = prepare_results(
analysed_trades_dict,
strategy_name,
enter_reason_list, enter_reason_list,
exit_reason_list, exit_reason_list,
timerange=timerange, signal_candles,
strategy_name,
timerange,
trades,
"open_date",
)
exit_df = _generate_dfs(
config["exchange"]["pair_whitelist"],
enter_reason_list,
exit_reason_list,
exit_signals,
strategy_name,
timerange,
trades,
"close_date",
) )
print_results( print_results(
res_df, entry_df,
exit_df,
analysis_groups, analysis_groups,
indicator_list, indicator_list,
entry_only,
exit_only,
rejected_signals=rej_df, rejected_signals=rej_df,
to_csv=to_csv, to_csv=to_csv,
csv_path=csv_path, csv_path=csv_path,
@@ -368,3 +432,30 @@ def process_entry_exit_reasons(config: Config):
except ValueError as e: except ValueError as e:
raise OperationalException(e) from e raise OperationalException(e) from e
def _generate_dfs(
pairlist: list,
enter_reason_list: list,
exit_reason_list: list,
signal_candles: Dict,
strategy_name: str,
timerange: TimeRange,
trades: pd.DataFrame,
date_col: str,
) -> pd.DataFrame:
analysed_trades_dict = _process_candles_and_indicators(
pairlist,
strategy_name,
trades,
signal_candles,
date_col,
)
res_df = prepare_results(
analysed_trades_dict,
strategy_name,
enter_reason_list,
exit_reason_list,
timerange=timerange,
)
return res_df

View File

@@ -12,7 +12,7 @@ from datetime import datetime, timezone
from pathlib import Path from pathlib import Path
from typing import List, Optional, Tuple, Type from typing import List, Optional, Tuple, Type
from pandas import DataFrame from pandas import DataFrame, to_datetime
from freqtrade import misc from freqtrade import misc
from freqtrade.configuration import TimeRange from freqtrade.configuration import TimeRange
@@ -32,6 +32,7 @@ logger = logging.getLogger(__name__)
class IDataHandler(ABC): class IDataHandler(ABC):
_OHLCV_REGEX = r"^([a-zA-Z_\d-]+)\-(\d+[a-zA-Z]{1,2})\-?([a-zA-Z_]*)?(?=\.)" _OHLCV_REGEX = r"^([a-zA-Z_\d-]+)\-(\d+[a-zA-Z]{1,2})\-?([a-zA-Z_]*)?(?=\.)"
_TRADES_REGEX = r"^([a-zA-Z_\d-]+)\-(trades)?(?=\.)"
def __init__(self, datadir: Path) -> None: def __init__(self, datadir: Path) -> None:
self._datadir = datadir self._datadir = datadir
@@ -166,6 +167,50 @@ class IDataHandler(ABC):
:param candle_type: Any of the enum CandleType (must match trading mode!) :param candle_type: Any of the enum CandleType (must match trading mode!)
""" """
@classmethod
def trades_get_available_data(cls, datadir: Path, trading_mode: TradingMode) -> List[str]:
"""
Returns a list of all pairs with ohlcv data available in this datadir
:param datadir: Directory to search for ohlcv files
:param trading_mode: trading-mode to be used
:return: List of Tuples of (pair, timeframe, CandleType)
"""
if trading_mode == TradingMode.FUTURES:
datadir = datadir.joinpath("futures")
_tmp = [
re.search(cls._TRADES_REGEX, p.name)
for p in datadir.glob(f"*.{cls._get_file_extension()}")
]
return [
cls.rebuild_pair_from_filename(match[1])
for match in _tmp
if match and len(match.groups()) > 1
]
def trades_data_min_max(
self,
pair: str,
trading_mode: TradingMode,
) -> Tuple[datetime, datetime, int]:
"""
Returns the min and max timestamp for the given pair's trades data.
:param pair: Pair to get min/max for
:param trading_mode: Trading mode to use (used to determine the filename)
:return: (min, max, len)
"""
df = self._trades_load(pair, trading_mode)
if df.empty:
return (
datetime.fromtimestamp(0, tz=timezone.utc),
datetime.fromtimestamp(0, tz=timezone.utc),
0,
)
return (
to_datetime(df.iloc[0]["timestamp"], unit="ms", utc=True).to_pydatetime(),
to_datetime(df.iloc[-1]["timestamp"], unit="ms", utc=True).to_pydatetime(),
len(df),
)
@classmethod @classmethod
def trades_get_pairs(cls, datadir: Path) -> List[str]: def trades_get_pairs(cls, datadir: Path) -> List[str]:
""" """
@@ -247,9 +292,13 @@ class IDataHandler(ABC):
:param timerange: Timerange to load trades for - currently not implemented :param timerange: Timerange to load trades for - currently not implemented
:return: List of trades :return: List of trades
""" """
trades = trades_df_remove_duplicates( try:
self._trades_load(pair, trading_mode, timerange=timerange) trades = self._trades_load(pair, trading_mode, timerange=timerange)
) except Exception:
logger.exception(f"Error loading trades for {pair}")
return DataFrame(columns=DEFAULT_TRADES_COLUMNS)
trades = trades_df_remove_duplicates(trades)
trades = trades_convert_types(trades) trades = trades_convert_types(trades)
return trades return trades

View File

@@ -17,7 +17,6 @@ from freqtrade.constants import (
from freqtrade.data.converter import ( from freqtrade.data.converter import (
clean_ohlcv_dataframe, clean_ohlcv_dataframe,
convert_trades_to_ohlcv, convert_trades_to_ohlcv,
ohlcv_to_dataframe,
trades_df_remove_duplicates, trades_df_remove_duplicates,
trades_list_to_df, trades_list_to_df,
) )
@@ -273,7 +272,7 @@ def _download_pair_history(
) )
# 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( new_dataframe = exchange.get_historic_ohlcv(
pair=pair, pair=pair,
timeframe=timeframe, timeframe=timeframe,
since_ms=( since_ms=(
@@ -285,10 +284,6 @@ def _download_pair_history(
candle_type=candle_type, candle_type=candle_type,
until_ms=until_ms if until_ms else None, until_ms=until_ms if until_ms else None,
) )
# TODO: Maybe move parsing to exchange class (?)
new_dataframe = ohlcv_to_dataframe(
new_data, timeframe, pair, fill_missing=False, drop_incomplete=True
)
if data.empty: if data.empty:
data = new_dataframe data = new_dataframe
else: else:
@@ -610,9 +605,6 @@ def download_data_main(config: Config) -> None:
if "timeframes" not in config: if "timeframes" not in config:
config["timeframes"] = DL_DATA_TIMEFRAMES config["timeframes"] = DL_DATA_TIMEFRAMES
# Manual validations of relevant settings
if not config["exchange"].get("skip_pair_validation", False):
exchange.validate_pairs(expanded_pairs)
logger.info( logger.info(
f"About to download pairs: {expanded_pairs}, " f"About to download pairs: {expanded_pairs}, "
f"intervals: {config['timeframes']} to {config['datadir']}" f"intervals: {config['timeframes']} to {config['datadir']}"

View File

@@ -11,3 +11,6 @@ class MarginMode(str, Enum):
CROSS = "cross" CROSS = "cross"
ISOLATED = "isolated" ISOLATED = "isolated"
NONE = "" NONE = ""
def __str__(self):
return f"{self.name.lower()}"

View File

@@ -10,3 +10,6 @@ class TradingMode(str, Enum):
SPOT = "spot" SPOT = "spot"
MARGIN = "margin" MARGIN = "margin"
FUTURES = "futures" FUTURES = "futures"
def __str__(self):
return f"{self.name.lower()}"

View File

@@ -39,6 +39,7 @@ from freqtrade.exchange.exchange_utils_timeframe import (
from freqtrade.exchange.gate import Gate from freqtrade.exchange.gate import Gate
from freqtrade.exchange.hitbtc import Hitbtc from freqtrade.exchange.hitbtc import Hitbtc
from freqtrade.exchange.htx import Htx from freqtrade.exchange.htx import Htx
from freqtrade.exchange.hyperliquid import Hyperliquid
from freqtrade.exchange.idex import Idex from freqtrade.exchange.idex import Idex
from freqtrade.exchange.kraken import Kraken from freqtrade.exchange.kraken import Kraken
from freqtrade.exchange.kucoin import Kucoin from freqtrade.exchange.kucoin import Kucoin

View File

@@ -11,7 +11,7 @@ from freqtrade.enums import CandleType, MarginMode, PriceType, TradingMode
from freqtrade.exceptions import DDosProtection, OperationalException, TemporaryError from freqtrade.exceptions import DDosProtection, OperationalException, TemporaryError
from freqtrade.exchange import Exchange from freqtrade.exchange import Exchange
from freqtrade.exchange.common import retrier from freqtrade.exchange.common import retrier
from freqtrade.exchange.types import OHLCVResponse, Tickers from freqtrade.exchange.exchange_types import FtHas, OHLCVResponse, Tickers
from freqtrade.misc import deep_merge_dicts, json_load from freqtrade.misc import deep_merge_dicts, json_load
@@ -19,7 +19,7 @@ logger = logging.getLogger(__name__)
class Binance(Exchange): class Binance(Exchange):
_ft_has: Dict = { _ft_has: FtHas = {
"stoploss_on_exchange": True, "stoploss_on_exchange": True,
"stop_price_param": "stopPrice", "stop_price_param": "stopPrice",
"stop_price_prop": "stopPrice", "stop_price_prop": "stopPrice",
@@ -30,9 +30,9 @@ class Binance(Exchange):
"trades_pagination_arg": "fromId", "trades_pagination_arg": "fromId",
"trades_has_history": True, "trades_has_history": True,
"l2_limit_range": [5, 10, 20, 50, 100, 500, 1000], "l2_limit_range": [5, 10, 20, 50, 100, 500, 1000],
"ws.enabled": True, "ws_enabled": True,
} }
_ft_has_futures: Dict = { _ft_has_futures: FtHas = {
"stoploss_order_types": {"limit": "stop", "market": "stop_market"}, "stoploss_order_types": {"limit": "stop", "market": "stop_market"},
"order_time_in_force": ["GTC", "FOK", "IOC"], "order_time_in_force": ["GTC", "FOK", "IOC"],
"tickers_have_price": False, "tickers_have_price": False,
@@ -43,7 +43,7 @@ class Binance(Exchange):
PriceType.LAST: "CONTRACT_PRICE", PriceType.LAST: "CONTRACT_PRICE",
PriceType.MARK: "MARK_PRICE", PriceType.MARK: "MARK_PRICE",
}, },
"ws.enabled": False, "ws_enabled": False,
} }
_supported_trading_mode_margin_pairs: List[Tuple[TradingMode, MarginMode]] = [ _supported_trading_mode_margin_pairs: List[Tuple[TradingMode, MarginMode]] = [
@@ -192,7 +192,7 @@ class Binance(Exchange):
if maintenance_amt is None: if maintenance_amt is None:
raise OperationalException( raise OperationalException(
"Parameter maintenance_amt is required by Binance.liquidation_price" "Parameter maintenance_amt is required by Binance.liquidation_price"
f"for {self.trading_mode.value}" f"for {self.trading_mode}"
) )
if self.trading_mode == TradingMode.FUTURES: if self.trading_mode == TradingMode.FUTURES:

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,9 @@
"""Bingx exchange subclass""" """Bingx exchange subclass"""
import logging import logging
from typing import Dict
from freqtrade.exchange import Exchange from freqtrade.exchange import Exchange
from freqtrade.exchange.exchange_types import FtHas
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -15,7 +15,7 @@ class Bingx(Exchange):
with this exchange. with this exchange.
""" """
_ft_has: Dict = { _ft_has: FtHas = {
"ohlcv_candle_limit": 1000, "ohlcv_candle_limit": 1000,
"stoploss_on_exchange": True, "stoploss_on_exchange": True,
"stoploss_order_types": {"limit": "limit", "market": "market"}, "stoploss_order_types": {"limit": "limit", "market": "market"},

View File

@@ -1,9 +1,9 @@
"""Bitmart exchange subclass""" """Bitmart exchange subclass"""
import logging import logging
from typing import Dict
from freqtrade.exchange import Exchange from freqtrade.exchange import Exchange
from freqtrade.exchange.exchange_types import FtHas
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -15,7 +15,7 @@ class Bitmart(Exchange):
with this exchange. with this exchange.
""" """
_ft_has: Dict = { _ft_has: FtHas = {
"stoploss_on_exchange": False, # Bitmart API does not support stoploss orders "stoploss_on_exchange": False, # Bitmart API does not support stoploss orders
"ohlcv_candle_limit": 200, "ohlcv_candle_limit": 200,
"trades_has_history": False, # Endpoint doesn't seem to support pagination "trades_has_history": False, # Endpoint doesn't seem to support pagination

View File

@@ -1,9 +1,11 @@
"""Kucoin exchange subclass.""" """Bitvavo exchange subclass."""
import logging import logging
from typing import Dict
from ccxt import DECIMAL_PLACES
from freqtrade.exchange import Exchange from freqtrade.exchange import Exchange
from freqtrade.exchange.exchange_types import FtHas
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -19,6 +21,14 @@ class Bitvavo(Exchange):
may still not work as expected. may still not work as expected.
""" """
_ft_has: Dict = { _ft_has: FtHas = {
"ohlcv_candle_limit": 1440, "ohlcv_candle_limit": 1440,
} }
@property
def precisionMode(self) -> int:
"""
Exchange ccxt precisionMode
Override due to https://github.com/ccxt/ccxt/issues/20408
"""
return DECIMAL_PLACES

View File

@@ -11,6 +11,7 @@ from freqtrade.enums import CandleType, MarginMode, PriceType, TradingMode
from freqtrade.exceptions import DDosProtection, ExchangeError, OperationalException, TemporaryError from freqtrade.exceptions import DDosProtection, ExchangeError, OperationalException, TemporaryError
from freqtrade.exchange import Exchange from freqtrade.exchange import Exchange
from freqtrade.exchange.common import retrier from freqtrade.exchange.common import retrier
from freqtrade.exchange.exchange_types import FtHas
from freqtrade.util.datetime_helpers import dt_now, dt_ts from freqtrade.util.datetime_helpers import dt_now, dt_ts
@@ -29,14 +30,14 @@ class Bybit(Exchange):
unified_account = False unified_account = False
_ft_has: Dict = { _ft_has: FtHas = {
"ohlcv_candle_limit": 1000, "ohlcv_candle_limit": 1000,
"ohlcv_has_history": True, "ohlcv_has_history": True,
"order_time_in_force": ["GTC", "FOK", "IOC", "PO"], "order_time_in_force": ["GTC", "FOK", "IOC", "PO"],
"ws.enabled": True, "ws_enabled": True,
"trades_has_history": False, # Endpoint doesn't support pagination "trades_has_history": False, # Endpoint doesn't support pagination
} }
_ft_has_futures: Dict = { _ft_has_futures: FtHas = {
"ohlcv_has_history": True, "ohlcv_has_history": True,
"mark_ohlcv_timeframe": "4h", "mark_ohlcv_timeframe": "4h",
"funding_fee_timeframe": "8h", "funding_fee_timeframe": "8h",
@@ -89,10 +90,8 @@ class Bybit(Exchange):
# Returns a tuple of bools, first for margin, second for Account # Returns a tuple of bools, first for margin, second for Account
if is_unified and len(is_unified) > 1 and is_unified[1]: if is_unified and len(is_unified) > 1 and is_unified[1]:
self.unified_account = True self.unified_account = True
logger.info("Bybit: Unified account.") logger.info(
raise OperationalException( "Bybit: Unified account. Assuming dedicated subaccount for this bot."
"Bybit: Unified account is not supported. "
"Please use a standard (sub)account."
) )
else: else:
self.unified_account = False self.unified_account = False
@@ -239,7 +238,13 @@ class Bybit(Exchange):
return orders return orders
def fetch_order(self, order_id: str, pair: str, params: Optional[Dict] = None) -> Dict: def fetch_order(self, order_id: str, pair: str, params: Optional[Dict] = None) -> Dict:
if self.exchange_has("fetchOrder"):
# Set acknowledged to True to avoid ccxt exception
params = {"acknowledged": True}
order = super().fetch_order(order_id, pair, params) order = super().fetch_order(order_id, pair, params)
if not order:
order = self.fetch_order_emulated(order_id, pair, {})
if ( if (
order.get("status") == "canceled" order.get("status") == "canceled"
and order.get("filled") == 0.0 and order.get("filled") == 0.0

View File

@@ -1,9 +1,9 @@
"""CoinbasePro exchange subclass""" """CoinbasePro exchange subclass"""
import logging import logging
from typing import Dict
from freqtrade.exchange import Exchange from freqtrade.exchange import Exchange
from freqtrade.exchange.exchange_types import FtHas
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -19,6 +19,6 @@ class Coinbasepro(Exchange):
may still not work as expected. may still not work as expected.
""" """
_ft_has: Dict = { _ft_has: FtHas = {
"ohlcv_candle_limit": 300, "ohlcv_candle_limit": 300,
} }

View File

@@ -54,6 +54,7 @@ SUPPORTED_EXCHANGES = [
"binance", "binance",
"bingx", "bingx",
"bitmart", "bitmart",
"bybit",
"gate", "gate",
"htx", "htx",
"kraken", "kraken",
@@ -163,6 +164,10 @@ F = TypeVar("F", bound=Callable[..., Any])
def retrier(_func: F) -> F: ... def retrier(_func: F) -> F: ...
@overload
def retrier(_func: F, *, retries=API_RETRY_COUNT) -> F: ...
@overload @overload
def retrier(*, retries=API_RETRY_COUNT) -> Callable[[F], F]: ... def retrier(*, retries=API_RETRY_COUNT) -> Callable[[F], F]: ...

View File

@@ -1,9 +1,9 @@
"""Crypto.com exchange subclass""" """Crypto.com exchange subclass"""
import logging import logging
from typing import Dict
from freqtrade.exchange import Exchange from freqtrade.exchange import Exchange
from freqtrade.exchange.exchange_types import FtHas
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -14,6 +14,6 @@ class Cryptocom(Exchange):
Contains adjustments needed for Freqtrade to work with this exchange. Contains adjustments needed for Freqtrade to work with this exchange.
""" """
_ft_has: Dict = { _ft_has: FtHas = {
"ohlcv_candle_limit": 300, "ohlcv_candle_limit": 300,
} }

View File

@@ -67,6 +67,15 @@ from freqtrade.exchange.common import (
retrier, retrier,
retrier_async, retrier_async,
) )
from freqtrade.exchange.exchange_types import (
CcxtBalances,
CcxtPosition,
FtHas,
OHLCVResponse,
OrderBook,
Ticker,
Tickers,
)
from freqtrade.exchange.exchange_utils import ( from freqtrade.exchange.exchange_utils import (
ROUND, ROUND,
ROUND_DOWN, ROUND_DOWN,
@@ -88,7 +97,6 @@ from freqtrade.exchange.exchange_utils_timeframe import (
timeframe_to_seconds, timeframe_to_seconds,
) )
from freqtrade.exchange.exchange_ws import ExchangeWS from freqtrade.exchange.exchange_ws import ExchangeWS
from freqtrade.exchange.types import OHLCVResponse, OrderBook, Ticker, Tickers
from freqtrade.misc import ( from freqtrade.misc import (
chunks, chunks,
deep_merge_dicts, deep_merge_dicts,
@@ -96,7 +104,6 @@ from freqtrade.misc import (
file_load_json, file_load_json,
safe_value_fallback2, safe_value_fallback2,
) )
from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist
from freqtrade.util import dt_from_ts, dt_now from freqtrade.util import dt_from_ts, dt_now
from freqtrade.util.datetime_helpers import dt_humanize_delta, dt_ts, format_ms_time from freqtrade.util.datetime_helpers import dt_humanize_delta, dt_ts, format_ms_time
from freqtrade.util.periodic_cache import PeriodicCache from freqtrade.util.periodic_cache import PeriodicCache
@@ -115,10 +122,11 @@ class Exchange:
# Dict to specify which options each exchange implements # Dict to specify which options each exchange implements
# This defines defaults, which can be selectively overridden by subclasses using _ft_has # This defines defaults, which can be selectively overridden by subclasses using _ft_has
# or by specifying them in the configuration. # or by specifying them in the configuration.
_ft_has_default: Dict = { _ft_has_default: FtHas = {
"stoploss_on_exchange": False, "stoploss_on_exchange": False,
"stop_price_param": "stopLossPrice", # Used for stoploss_on_exchange request "stop_price_param": "stopLossPrice", # Used for stoploss_on_exchange request
"stop_price_prop": "stopLossPrice", # Used for stoploss_on_exchange response parsing "stop_price_prop": "stopLossPrice", # Used for stoploss_on_exchange response parsing
"stoploss_order_types": {},
"order_time_in_force": ["GTC"], "order_time_in_force": ["GTC"],
"ohlcv_params": {}, "ohlcv_params": {},
"ohlcv_candle_limit": 500, "ohlcv_candle_limit": 500,
@@ -128,6 +136,7 @@ class Exchange:
# Check https://github.com/ccxt/ccxt/issues/10767 for removal of ohlcv_volume_currency # Check https://github.com/ccxt/ccxt/issues/10767 for removal of ohlcv_volume_currency
"ohlcv_volume_currency": "base", # "base" or "quote" "ohlcv_volume_currency": "base", # "base" or "quote"
"tickers_have_quoteVolume": True, "tickers_have_quoteVolume": True,
"tickers_have_percentage": True,
"tickers_have_bid_ask": True, # bid / ask empty for fetch_tickers "tickers_have_bid_ask": True, # bid / ask empty for fetch_tickers
"tickers_have_price": True, "tickers_have_price": True,
"trades_limit": 1000, # Limit for 1 call to fetch_trades "trades_limit": 1000, # Limit for 1 call to fetch_trades
@@ -146,10 +155,10 @@ class Exchange:
"marketOrderRequiresPrice": False, "marketOrderRequiresPrice": False,
"exchange_has_overrides": {}, # Dictionary overriding ccxt's "has". "exchange_has_overrides": {}, # Dictionary overriding ccxt's "has".
# Expected to be in the format {"fetchOHLCV": True} or {"fetchOHLCV": False} # Expected to be in the format {"fetchOHLCV": True} or {"fetchOHLCV": False}
"ws.enabled": False, # Set to true for exchanges with tested websocket support "ws_enabled": False, # Set to true for exchanges with tested websocket support
} }
_ft_has: Dict = {} _ft_has: FtHas = {}
_ft_has_futures: Dict = {} _ft_has_futures: FtHas = {}
_supported_trading_mode_margin_pairs: List[Tuple[TradingMode, MarginMode]] = [ _supported_trading_mode_margin_pairs: List[Tuple[TradingMode, MarginMode]] = [
# TradingMode.SPOT always supported and not required in this list # TradingMode.SPOT always supported and not required in this list
@@ -253,7 +262,7 @@ class Exchange:
exchange_conf.get("ccxt_async_config", {}), ccxt_async_config exchange_conf.get("ccxt_async_config", {}), ccxt_async_config
) )
self._api_async = self._init_ccxt(exchange_conf, False, ccxt_async_config) self._api_async = self._init_ccxt(exchange_conf, False, ccxt_async_config)
self._has_watch_ohlcv = self.exchange_has("watchOHLCV") and self._ft_has["ws.enabled"] self._has_watch_ohlcv = self.exchange_has("watchOHLCV") and self._ft_has["ws_enabled"]
if ( if (
self._config["runmode"] in TRADE_MODES self._config["runmode"] in TRADE_MODES
and exchange_conf.get("enable_ws", True) and exchange_conf.get("enable_ws", True)
@@ -315,20 +324,19 @@ class Exchange:
asyncio.set_event_loop(loop) asyncio.set_event_loop(loop)
return loop return loop
def validate_config(self, config): def validate_config(self, config: Config) -> None:
# Check if timeframe is available # Check if timeframe is available
self.validate_timeframes(config.get("timeframe")) self.validate_timeframes(config.get("timeframe"))
# Check if all pairs are available # Check if all pairs are available
self.validate_stakecurrency(config["stake_currency"]) self.validate_stakecurrency(config["stake_currency"])
if not config["exchange"].get("skip_pair_validation"):
self.validate_pairs(config["exchange"]["pair_whitelist"])
self.validate_ordertypes(config.get("order_types", {})) self.validate_ordertypes(config.get("order_types", {}))
self.validate_order_time_in_force(config.get("order_time_in_force", {})) self.validate_order_time_in_force(config.get("order_time_in_force", {}))
self.validate_trading_mode_and_margin_mode(self.trading_mode, self.margin_mode) self.validate_trading_mode_and_margin_mode(self.trading_mode, self.margin_mode)
self.validate_pricing(config["exit_pricing"]) self.validate_pricing(config["exit_pricing"])
self.validate_pricing(config["entry_pricing"]) self.validate_pricing(config["entry_pricing"])
self.validate_orderflow(config["exchange"]) self.validate_orderflow(config["exchange"])
self.validate_freqai(config)
def _init_ccxt( def _init_ccxt(
self, exchange_config: Dict[str, Any], sync: bool, ccxt_kwargs: Dict[str, Any] self, exchange_config: Dict[str, Any], sync: bool, ccxt_kwargs: Dict[str, Any]
@@ -352,14 +360,18 @@ class Exchange:
raise OperationalException(f"Exchange {name} is not supported by ccxt") raise OperationalException(f"Exchange {name} is not supported by ccxt")
ex_config = { ex_config = {
"apiKey": exchange_config.get("apiKey", exchange_config.get("key")), "apiKey": exchange_config.get(
"api_key", exchange_config.get("apiKey", exchange_config.get("key"))
),
"secret": exchange_config.get("secret"), "secret": exchange_config.get("secret"),
"password": exchange_config.get("password"), "password": exchange_config.get("password"),
"uid": exchange_config.get("uid", ""), "uid": exchange_config.get("uid", ""),
"accountId": exchange_config.get("accountId", ""), "accountId": exchange_config.get("account_id", exchange_config.get("accountId", "")),
# DEX attributes: # DEX attributes:
"walletAddress": exchange_config.get("walletAddress"), "walletAddress": exchange_config.get(
"privateKey": exchange_config.get("privateKey"), "wallet_address", exchange_config.get("walletAddress")
),
"privateKey": exchange_config.get("private_key", exchange_config.get("privateKey")),
} }
if ccxt_kwargs: if ccxt_kwargs:
logger.info("Applying additional ccxt config: %s", ccxt_kwargs) logger.info("Applying additional ccxt config: %s", ccxt_kwargs)
@@ -411,7 +423,17 @@ class Exchange:
@property @property
def precisionMode(self) -> int: def precisionMode(self) -> int:
"""exchange ccxt precisionMode""" """Exchange ccxt precisionMode"""
return self._api.precisionMode
@property
def precision_mode_price(self) -> int:
"""
Exchange ccxt precisionMode used for price
Workaround for ccxt limitation to not have precisionMode for price
if it differs for an exchange
Might need to be updated if https://github.com/ccxt/ccxt/issues/20408 is fixed.
"""
return self._api.precisionMode return self._api.precisionMode
def additional_exchange_init(self) -> None: def additional_exchange_init(self) -> None:
@@ -443,7 +465,7 @@ class Exchange:
""" """
return int( return int(
self._ft_has.get("ohlcv_candle_limit_per_timeframe", {}).get( self._ft_has.get("ohlcv_candle_limit_per_timeframe", {}).get(
timeframe, self._ft_has.get("ohlcv_candle_limit") timeframe, str(self._ft_has.get("ohlcv_candle_limit"))
) )
) )
@@ -541,7 +563,7 @@ class Exchange:
else: else:
return self._trades[pair_interval] return self._trades[pair_interval]
else: else:
return DataFrame() return DataFrame(columns=DEFAULT_TRADES_COLUMNS)
def get_contract_size(self, pair: str) -> Optional[float]: def get_contract_size(self, pair: str) -> Optional[float]:
if self.trading_mode == TradingMode.FUTURES: if self.trading_mode == TradingMode.FUTURES:
@@ -598,11 +620,21 @@ class Exchange:
if self._exchange_ws: if self._exchange_ws:
self._exchange_ws.reset_connections() self._exchange_ws.reset_connections()
async def _api_reload_markets(self, reload: bool = False) -> Dict[str, Any]:
try:
return await self._api_async.load_markets(reload=reload, params={})
except ccxt.DDoSProtection as e:
raise DDosProtection(e) from e
except (ccxt.OperationFailed, ccxt.ExchangeError) as e:
raise TemporaryError(
f"Error in reload_markets due to {e.__class__.__name__}. Message: {e}"
) from e
except ccxt.BaseError as e:
raise TemporaryError(e) from e
def _load_async_markets(self, reload: bool = False) -> Dict[str, Any]: def _load_async_markets(self, reload: bool = False) -> Dict[str, Any]:
try: try:
markets = self.loop.run_until_complete( markets = self.loop.run_until_complete(self._api_reload_markets(reload=reload))
self._api_async.load_markets(reload=reload, params={})
)
if isinstance(markets, Exception): if isinstance(markets, Exception):
raise markets raise markets
@@ -626,8 +658,10 @@ class Exchange:
return None return None
logger.debug("Performing scheduled market reload..") logger.debug("Performing scheduled market reload..")
try: try:
# on initial load, we retry 3 times to ensure we get the markets
retries: int = 3 if force else 0
# Reload async markets, then assign them to sync api # Reload async markets, then assign them to sync api
self._markets = self._load_async_markets(reload=True) self._markets = retrier(self._load_async_markets, retries=retries)(reload=True)
self._api.set_markets(self._api_async.markets, self._api_async.currencies) self._api.set_markets(self._api_async.markets, self._api_async.currencies)
# Assign options array, as it contains some temporary information from the exchange. # Assign options array, as it contains some temporary information from the exchange.
self._api.options = self._api_async.options self._api.options = self._api_async.options
@@ -665,54 +699,6 @@ class Exchange:
f"Available currencies are: {', '.join(quote_currencies)}" f"Available currencies are: {', '.join(quote_currencies)}"
) )
def validate_pairs(self, pairs: List[str]) -> None:
"""
Checks if all given pairs are tradable on the current exchange.
:param pairs: list of pairs
:raise: OperationalException if one pair is not available
:return: None
"""
if not self.markets:
logger.warning("Unable to validate pairs (assuming they are correct).")
return
extended_pairs = expand_pairlist(pairs, list(self.markets), keep_invalid=True)
invalid_pairs = []
for pair in extended_pairs:
# Note: ccxt has BaseCurrency/QuoteCurrency format for pairs
if self.markets and pair not in self.markets:
raise OperationalException(
f"Pair {pair} is not available on {self.name} {self.trading_mode.value}. "
f"Please remove {pair} from your whitelist."
)
# From ccxt Documentation:
# markets.info: An associative array of non-common market properties,
# including fees, rates, limits and other general market information.
# The internal info array is different for each particular market,
# its contents depend on the exchange.
# It can also be a string or similar ... so we need to verify that first.
elif isinstance(self.markets[pair].get("info"), dict) and self.markets[pair].get(
"info", {}
).get("prohibitedIn", False):
# Warn users about restricted pairs in whitelist.
# We cannot determine reliably if Users are affected.
logger.warning(
f"Pair {pair} is restricted for some users on this exchange."
f"Please check if you are impacted by this restriction "
f"on the exchange and eventually remove {pair} from your whitelist."
)
if (
self._config["stake_currency"]
and self.get_pair_quote_currency(pair) != self._config["stake_currency"]
):
invalid_pairs.append(pair)
if invalid_pairs:
raise OperationalException(
f"Stake-currency '{self._config['stake_currency']}' not compatible with "
f"pair-whitelist. Please remove the following pairs: {invalid_pairs}"
)
def get_valid_pair_combination(self, curr_1: str, curr_2: str) -> str: def get_valid_pair_combination(self, curr_1: str, curr_2: str) -> str:
""" """
Get valid pair combination of curr_1 and curr_2 by trying both combinations. Get valid pair combination of curr_1 and curr_2 by trying both combinations.
@@ -804,6 +790,13 @@ class Exchange:
f"Trade data not available for {self.name}. Can't use orderflow feature." f"Trade data not available for {self.name}. Can't use orderflow feature."
) )
def validate_freqai(self, config: Config) -> None:
freqai_enabled = config.get("freqai", {}).get("enabled", False)
if freqai_enabled and not self._ft_has["ohlcv_has_history"]:
raise ConfigurationError(
f"Historic OHLCV data not available for {self.name}. Can't use freqAI."
)
def validate_required_startup_candles(self, startup_candles: int, timeframe: str) -> int: def validate_required_startup_candles(self, startup_candles: int, timeframe: str) -> int:
""" """
Checks if required startup_candles is more than ohlcv_candle_limit(). Checks if required startup_candles is more than ohlcv_candle_limit().
@@ -858,7 +851,7 @@ class Exchange:
): ):
mm_value = margin_mode and margin_mode.value mm_value = margin_mode and margin_mode.value
raise OperationalException( raise OperationalException(
f"Freqtrade does not support {mm_value} {trading_mode.value} on {self.name}" f"Freqtrade does not support {mm_value} {trading_mode} on {self.name}"
) )
def get_option(self, param: str, default: Optional[Any] = None) -> Any: def get_option(self, param: str, default: Optional[Any] = None) -> Any:
@@ -908,7 +901,10 @@ class Exchange:
For stoploss calculations, must use ROUND_UP for longs, and ROUND_DOWN for shorts. For stoploss calculations, must use ROUND_UP for longs, and ROUND_DOWN for shorts.
""" """
return price_to_precision( return price_to_precision(
price, self.get_precision_price(pair), self.precisionMode, rounding_mode=rounding_mode price,
self.get_precision_price(pair),
self.precision_mode_price,
rounding_mode=rounding_mode,
) )
def price_get_one_pip(self, pair: str, price: float) -> float: def price_get_one_pip(self, pair: str, price: float) -> float:
@@ -1645,7 +1641,7 @@ class Exchange:
return order return order
@retrier @retrier
def get_balances(self) -> dict: def get_balances(self) -> CcxtBalances:
try: try:
balances = self._api.fetch_balance() balances = self._api.fetch_balance()
# Remove additional info from ccxt results # Remove additional info from ccxt results
@@ -1665,7 +1661,7 @@ class Exchange:
raise OperationalException(e) from e raise OperationalException(e) from e
@retrier @retrier
def fetch_positions(self, pair: Optional[str] = None) -> List[Dict]: def fetch_positions(self, pair: Optional[str] = None) -> List[CcxtPosition]:
""" """
Fetch positions from the exchange. Fetch positions from the exchange.
If no pair is given, all positions are returned. If no pair is given, all positions are returned.
@@ -1677,7 +1673,7 @@ class Exchange:
symbols = [] symbols = []
if pair: if pair:
symbols.append(pair) symbols.append(pair)
positions: List[Dict] = self._api.fetch_positions(symbols) positions: List[CcxtPosition] = self._api.fetch_positions(symbols)
self._log_exchange_response("fetch_positions", positions) self._log_exchange_response("fetch_positions", positions)
return positions return positions
except ccxt.DDoSProtection as e: except ccxt.DDoSProtection as e:
@@ -2227,7 +2223,7 @@ class Exchange:
candle_type: CandleType, candle_type: CandleType,
is_new_pair: bool = False, is_new_pair: bool = False,
until_ms: Optional[int] = None, until_ms: Optional[int] = None,
) -> List: ) -> DataFrame:
""" """
Get candle history using asyncio and returns the list of candles. Get candle history using asyncio and returns the list of candles.
Handles all async work for this. Handles all async work for this.
@@ -2237,7 +2233,7 @@ class Exchange:
:param since_ms: Timestamp in milliseconds to get history from :param since_ms: Timestamp in milliseconds to get history from
:param until_ms: Timestamp in milliseconds to get history up to :param until_ms: Timestamp in milliseconds to get history up to
:param candle_type: '', mark, index, premiumIndex, or funding_rate :param candle_type: '', mark, index, premiumIndex, or funding_rate
:return: List with candle (OHLCV) data :return: Dataframe with candle (OHLCV) data
""" """
pair, _, _, data, _ = self.loop.run_until_complete( pair, _, _, data, _ = self.loop.run_until_complete(
self._async_get_historic_ohlcv( self._async_get_historic_ohlcv(
@@ -2250,7 +2246,7 @@ class Exchange:
) )
) )
logger.info(f"Downloaded data for {pair} with length {len(data)}.") logger.info(f"Downloaded data for {pair} with length {len(data)}.")
return data return ohlcv_to_dataframe(data, timeframe, pair, fill_missing=False, drop_incomplete=True)
async def _async_get_historic_ohlcv( async def _async_get_historic_ohlcv(
self, self,
@@ -2469,17 +2465,17 @@ class Exchange:
logger.debug("Refreshing candle (OHLCV) data for %d pairs", len(pair_list)) logger.debug("Refreshing candle (OHLCV) data for %d pairs", len(pair_list))
# Gather coroutines to run # Gather coroutines to run
input_coroutines, cached_pairs = self._build_ohlcv_dl_jobs(pair_list, since_ms, cache) ohlcv_dl_jobs, cached_pairs = self._build_ohlcv_dl_jobs(pair_list, since_ms, cache)
results_df = {} results_df = {}
# Chunk requests into batches of 100 to avoid overwhelming ccxt Throttling # Chunk requests into batches of 100 to avoid overwhelming ccxt Throttling
for input_coro in chunks(input_coroutines, 100): for dl_jobs_batch in chunks(ohlcv_dl_jobs, 100):
async def gather_stuff(coro): async def gather_coroutines(coro):
return await asyncio.gather(*coro, return_exceptions=True) return await asyncio.gather(*coro, return_exceptions=True)
with self._loop_lock: with self._loop_lock:
results = self.loop.run_until_complete(gather_stuff(input_coro)) results = self.loop.run_until_complete(gather_coroutines(dl_jobs_batch))
for res in results: for res in results:
if isinstance(res, Exception): if isinstance(res, Exception):
@@ -2607,12 +2603,13 @@ class Exchange:
except (ccxt.OperationFailed, ccxt.ExchangeError) as e: except (ccxt.OperationFailed, ccxt.ExchangeError) as e:
raise TemporaryError( raise TemporaryError(
f"Could not fetch historical candle (OHLCV) data " f"Could not fetch historical candle (OHLCV) data "
f"for pair {pair} due to {e.__class__.__name__}. " f"for {pair}, {timeframe}, {candle_type} due to {e.__class__.__name__}. "
f"Message: {e}" f"Message: {e}"
) from e ) from e
except ccxt.BaseError as e: except ccxt.BaseError as e:
raise OperationalException( raise OperationalException(
f"Could not fetch historical candle (OHLCV) data for pair {pair}. Message: {e}" f"Could not fetch historical candle (OHLCV) data for "
f"{pair}, {timeframe}, {candle_type}. Message: {e}"
) from e ) from e
async def _fetch_funding_rate_history( async def _fetch_funding_rate_history(
@@ -2677,6 +2674,94 @@ class Exchange:
self._trades[(pair, timeframe, c_type)] = trades_df self._trades[(pair, timeframe, c_type)] = trades_df
return trades_df return trades_df
async def _build_trades_dl_jobs(
self, pairwt: PairWithTimeframe, data_handler, cache: bool
) -> Tuple[PairWithTimeframe, Optional[DataFrame]]:
"""
Build coroutines to refresh trades for (they're then called through async.gather)
"""
pair, timeframe, candle_type = pairwt
since_ms = None
new_ticks: List = []
all_stored_ticks_df = DataFrame(columns=DEFAULT_TRADES_COLUMNS + ["date"])
first_candle_ms = self.needed_candle_for_trades_ms(timeframe, candle_type)
# refresh, if
# a. not in _trades
# b. no cache used
# c. need new data
is_in_cache = (pair, timeframe, candle_type) in self._trades
if (
not is_in_cache
or not cache
or self._now_is_time_to_refresh_trades(pair, timeframe, candle_type)
):
logger.debug(f"Refreshing TRADES data for {pair}")
# fetch trades since latest _trades and
# store together with existing trades
try:
until = None
from_id = None
if is_in_cache:
from_id = self._trades[(pair, timeframe, candle_type)].iloc[-1]["id"]
until = dt_ts() # now
else:
until = int(timeframe_to_prev_date(timeframe).timestamp()) * 1000
all_stored_ticks_df = data_handler.trades_load(
f"{pair}-cached", self.trading_mode
)
if not all_stored_ticks_df.empty:
if (
all_stored_ticks_df.iloc[-1]["timestamp"] > first_candle_ms
and all_stored_ticks_df.iloc[0]["timestamp"] <= first_candle_ms
):
# Use cache and populate further
last_cached_ms = all_stored_ticks_df.iloc[-1]["timestamp"]
from_id = all_stored_ticks_df.iloc[-1]["id"]
# only use cached if it's closer than first_candle_ms
since_ms = (
last_cached_ms
if last_cached_ms > first_candle_ms
else first_candle_ms
)
else:
# Skip cache, it's too old
all_stored_ticks_df = DataFrame(
columns=DEFAULT_TRADES_COLUMNS + ["date"]
)
# from_id overrules with exchange set to id paginate
[_, new_ticks] = await self._async_get_trade_history(
pair,
since=since_ms if since_ms else first_candle_ms,
until=until,
from_id=from_id,
)
except Exception:
logger.exception(f"Refreshing TRADES data for {pair} failed")
return pairwt, None
if new_ticks:
all_stored_ticks_list = all_stored_ticks_df[DEFAULT_TRADES_COLUMNS].values.tolist()
all_stored_ticks_list.extend(new_ticks)
trades_df = self._process_trades_df(
pair,
timeframe,
candle_type,
all_stored_ticks_list,
cache,
first_required_candle_date=first_candle_ms,
)
data_handler.trades_store(
f"{pair}-cached", trades_df[DEFAULT_TRADES_COLUMNS], self.trading_mode
)
return pairwt, trades_df
else:
logger.error(f"No new ticks for {pair}")
return pairwt, None
def refresh_latest_trades( def refresh_latest_trades(
self, self,
pair_list: ListPairsWithTimeframes, pair_list: ListPairsWithTimeframes,
@@ -2697,90 +2782,25 @@ class Exchange:
self._config["datadir"], data_format=self._config["dataformat_trades"] self._config["datadir"], data_format=self._config["dataformat_trades"]
) )
logger.debug("Refreshing TRADES data for %d pairs", len(pair_list)) logger.debug("Refreshing TRADES data for %d pairs", len(pair_list))
since_ms = None
results_df = {} results_df = {}
for pair, timeframe, candle_type in set(pair_list): trades_dl_jobs = []
new_ticks: List = [] for pair_wt in set(pair_list):
all_stored_ticks_df = DataFrame(columns=DEFAULT_TRADES_COLUMNS + ["date"]) trades_dl_jobs.append(self._build_trades_dl_jobs(pair_wt, data_handler, cache))
first_candle_ms = self.needed_candle_for_trades_ms(timeframe, candle_type)
# refresh, if
# a. not in _trades
# b. no cache used
# c. need new data
is_in_cache = (pair, timeframe, candle_type) in self._trades
if (
not is_in_cache
or not cache
or self._now_is_time_to_refresh_trades(pair, timeframe, candle_type)
):
logger.debug(f"Refreshing TRADES data for {pair}")
# fetch trades since latest _trades and
# store together with existing trades
try:
until = None
from_id = None
if is_in_cache:
from_id = self._trades[(pair, timeframe, candle_type)].iloc[-1]["id"]
until = dt_ts() # now
else: async def gather_coroutines(coro):
until = int(timeframe_to_prev_date(timeframe).timestamp()) * 1000 return await asyncio.gather(*coro, return_exceptions=True)
all_stored_ticks_df = data_handler.trades_load(
f"{pair}-cached", self.trading_mode
)
if not all_stored_ticks_df.empty: for dl_job_chunk in chunks(trades_dl_jobs, 100):
if ( with self._loop_lock:
all_stored_ticks_df.iloc[-1]["timestamp"] > first_candle_ms results = self.loop.run_until_complete(gather_coroutines(dl_job_chunk))
and all_stored_ticks_df.iloc[0]["timestamp"] <= first_candle_ms
):
# Use cache and populate further
last_cached_ms = all_stored_ticks_df.iloc[-1]["timestamp"]
from_id = all_stored_ticks_df.iloc[-1]["id"]
# only use cached if it's closer than first_candle_ms
since_ms = (
last_cached_ms
if last_cached_ms > first_candle_ms
else first_candle_ms
)
else:
# Skip cache, it's too old
all_stored_ticks_df = DataFrame(
columns=DEFAULT_TRADES_COLUMNS + ["date"]
)
# from_id overrules with exchange set to id paginate for res in results:
[_, new_ticks] = self.get_historic_trades( if isinstance(res, Exception):
pair, logger.warning(f"Async code raised an exception: {repr(res)}")
since=since_ms if since_ms else first_candle_ms,
until=until,
from_id=from_id,
)
except Exception:
logger.exception(f"Refreshing TRADES data for {pair} failed")
continue continue
pairwt, trades_df = res
if new_ticks: if trades_df is not None:
all_stored_ticks_list = all_stored_ticks_df[ results_df[pairwt] = trades_df
DEFAULT_TRADES_COLUMNS
].values.tolist()
all_stored_ticks_list.extend(new_ticks)
trades_df = self._process_trades_df(
pair,
timeframe,
candle_type,
all_stored_ticks_list,
cache,
first_required_candle_date=first_candle_ms,
)
results_df[(pair, timeframe, candle_type)] = trades_df
data_handler.trades_store(
f"{pair}-cached", trades_df[DEFAULT_TRADES_COLUMNS], self.trading_mode
)
else:
logger.error(f"No new ticks for {pair}")
return results_df return results_df
@@ -3574,7 +3594,7 @@ class Exchange:
Wherein, "+" or "-" depends on whether the contract goes long or short: Wherein, "+" or "-" depends on whether the contract goes long or short:
"-" for long, and "+" for short. "-" for long, and "+" for short.
okex: https://www.okex.com/support/hc/en-us/articles/ okex: https://www.okx.com/support/hc/en-us/articles/
360053909592-VI-Introduction-to-the-isolated-mode-of-Single-Multi-currency-Portfolio-margin 360053909592-VI-Introduction-to-the-isolated-mode-of-Single-Multi-currency-Portfolio-margin
:param pair: Pair to calculate liquidation price for :param pair: Pair to calculate liquidation price for

View File

@@ -0,0 +1,98 @@
from typing import Dict, List, Optional, Tuple, TypedDict
from freqtrade.enums import CandleType
class FtHas(TypedDict, total=False):
order_time_in_force: List[str]
exchange_has_overrides: Dict[str, bool]
marketOrderRequiresPrice: bool
# Stoploss on exchange
stoploss_on_exchange: bool
stop_price_param: str
stop_price_prop: str
stop_price_type_field: str
stop_price_type_value_mapping: Dict
stoploss_order_types: Dict[str, str]
# ohlcv
ohlcv_params: Dict
ohlcv_candle_limit: int
ohlcv_has_history: bool
ohlcv_partial_candle: bool
ohlcv_require_since: bool
ohlcv_volume_currency: str
ohlcv_candle_limit_per_timeframe: Dict[str, int]
# Tickers
tickers_have_quoteVolume: bool
tickers_have_percentage: bool
tickers_have_bid_ask: bool
tickers_have_price: bool
# Trades
trades_limit: int
trades_pagination: str
trades_pagination_arg: str
trades_has_history: bool
trades_pagination_overlap: bool
# Orderbook
l2_limit_range: Optional[List[int]]
l2_limit_range_required: bool
# Futures
ccxt_futures_name: str # usually swap
mark_ohlcv_price: str
mark_ohlcv_timeframe: str
funding_fee_timeframe: str
floor_leverage: bool
needs_trading_fees: bool
order_props_in_contracts: List[str]
# Websocket control
ws_enabled: bool
class Ticker(TypedDict):
symbol: str
ask: Optional[float]
askVolume: Optional[float]
bid: Optional[float]
bidVolume: Optional[float]
last: Optional[float]
quoteVolume: Optional[float]
baseVolume: Optional[float]
percentage: Optional[float]
# Several more - only listing required.
Tickers = Dict[str, Ticker]
class OrderBook(TypedDict):
symbol: str
bids: List[Tuple[float, float]]
asks: List[Tuple[float, float]]
timestamp: Optional[int]
datetime: Optional[str]
nonce: Optional[int]
class CcxtBalance(TypedDict):
free: float
used: float
total: float
CcxtBalances = Dict[str, CcxtBalance]
class CcxtPosition(TypedDict):
symbol: str
side: str
contracts: float
leverage: float
collateral: Optional[float]
initialMargin: Optional[float]
liquidationPrice: Optional[float]
# pair, timeframe, candleType, OHLCV, drop last?,
OHLCVResponse = Tuple[str, str, CandleType, List, bool]

View File

@@ -2,6 +2,7 @@
Exchange support utils Exchange support utils
""" """
import inspect
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
from math import ceil, floor from math import ceil, floor
from typing import Any, Dict, List, Optional, Tuple from typing import Any, Dict, List, Optional, Tuple
@@ -25,7 +26,7 @@ from freqtrade.exchange.common import (
SUPPORTED_EXCHANGES, SUPPORTED_EXCHANGES,
) )
from freqtrade.exchange.exchange_utils_timeframe import timeframe_to_minutes, timeframe_to_prev_date from freqtrade.exchange.exchange_utils_timeframe import timeframe_to_minutes, timeframe_to_prev_date
from freqtrade.types import ValidExchangesType from freqtrade.ft_types import ValidExchangesType
from freqtrade.util import FtPrecise from freqtrade.util import FtPrecise
@@ -53,9 +54,9 @@ def available_exchanges(ccxt_module: Optional[CcxtModuleType] = None) -> List[st
return [x for x in exchanges if validate_exchange(x)[0]] return [x for x in exchanges if validate_exchange(x)[0]]
def validate_exchange(exchange: str) -> Tuple[bool, str, bool]: def validate_exchange(exchange: str) -> Tuple[bool, str, Optional[ccxt.Exchange]]:
""" """
returns: can_use, reason returns: can_use, reason, exchange_object
with Reason including both missing and missing_opt with Reason including both missing and missing_opt
""" """
try: try:
@@ -64,11 +65,10 @@ def validate_exchange(exchange: str) -> Tuple[bool, str, bool]:
ex_mod = getattr(ccxt.async_support, exchange.lower())() ex_mod = getattr(ccxt.async_support, exchange.lower())()
if not ex_mod or not ex_mod.has: if not ex_mod or not ex_mod.has:
return False, "", False return False, "", None
result = True result = True
reason = "" reason = ""
is_dex = getattr(ex_mod, "dex", False)
missing = [ missing = [
k k
for k, v in EXCHANGE_HAS_REQUIRED.items() for k, v in EXCHANGE_HAS_REQUIRED.items()
@@ -87,19 +87,24 @@ def validate_exchange(exchange: str) -> Tuple[bool, str, bool]:
if missing_opt: if missing_opt:
reason += f"{'. ' if reason else ''}missing opt: {', '.join(missing_opt)}. " reason += f"{'. ' if reason else ''}missing opt: {', '.join(missing_opt)}. "
return result, reason, is_dex return result, reason, ex_mod
def _build_exchange_list_entry( def _build_exchange_list_entry(
exchange_name: str, exchangeClasses: Dict[str, Any] exchange_name: str, exchangeClasses: Dict[str, Any]
) -> ValidExchangesType: ) -> ValidExchangesType:
valid, comment, is_dex = validate_exchange(exchange_name) valid, comment, ex_mod = validate_exchange(exchange_name)
result: ValidExchangesType = { result: ValidExchangesType = {
"name": exchange_name, "name": getattr(ex_mod, "name", exchange_name),
"classname": exchange_name,
"valid": valid, "valid": valid,
"supported": exchange_name.lower() in SUPPORTED_EXCHANGES, "supported": exchange_name.lower() in SUPPORTED_EXCHANGES,
"comment": comment, "comment": comment,
"dex": is_dex, "dex": getattr(ex_mod, "dex", False),
"is_alias": getattr(ex_mod, "alias", False),
"alias_for": inspect.getmro(ex_mod.__class__)[1]().id
if getattr(ex_mod, "alias", False)
else None,
"trade_modes": [{"trading_mode": "spot", "margin_mode": ""}], "trade_modes": [{"trading_mode": "spot", "margin_mode": ""}],
} }
if resolved := exchangeClasses.get(exchange_name.lower()): if resolved := exchangeClasses.get(exchange_name.lower()):

View File

@@ -11,7 +11,7 @@ import ccxt
from freqtrade.constants import Config, PairWithTimeframe from freqtrade.constants import Config, PairWithTimeframe
from freqtrade.enums.candletype import CandleType from freqtrade.enums.candletype import CandleType
from freqtrade.exchange.exchange import timeframe_to_seconds from freqtrade.exchange.exchange import timeframe_to_seconds
from freqtrade.exchange.types import OHLCVResponse from freqtrade.exchange.exchange_types import OHLCVResponse
from freqtrade.util import dt_ts, format_ms_time from freqtrade.util import dt_ts, format_ms_time

View File

@@ -7,6 +7,7 @@ from typing import Any, Dict, List, Optional, Tuple
from freqtrade.constants import BuySell from freqtrade.constants import BuySell
from freqtrade.enums import MarginMode, PriceType, TradingMode from freqtrade.enums import MarginMode, PriceType, TradingMode
from freqtrade.exchange import Exchange from freqtrade.exchange import Exchange
from freqtrade.exchange.exchange_types import FtHas
from freqtrade.misc import safe_value_fallback2 from freqtrade.misc import safe_value_fallback2
@@ -23,7 +24,7 @@ class Gate(Exchange):
may still not work as expected. may still not work as expected.
""" """
_ft_has: Dict = { _ft_has: FtHas = {
"ohlcv_candle_limit": 1000, "ohlcv_candle_limit": 1000,
"order_time_in_force": ["GTC", "IOC"], "order_time_in_force": ["GTC", "IOC"],
"stoploss_on_exchange": True, "stoploss_on_exchange": True,
@@ -34,7 +35,7 @@ class Gate(Exchange):
"trades_has_history": False, # Endpoint would support this - but ccxt doesn't. "trades_has_history": False, # Endpoint would support this - but ccxt doesn't.
} }
_ft_has_futures: Dict = { _ft_has_futures: FtHas = {
"needs_trading_fees": True, "needs_trading_fees": True,
"marketOrderRequiresPrice": False, "marketOrderRequiresPrice": False,
"stop_price_type_field": "price_type", "stop_price_type_field": "price_type",

View File

@@ -1,7 +1,7 @@
import logging import logging
from typing import Dict
from freqtrade.exchange import Exchange from freqtrade.exchange import Exchange
from freqtrade.exchange.exchange_types import FtHas
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -17,6 +17,6 @@ class Hitbtc(Exchange):
may still not work as expected. may still not work as expected.
""" """
_ft_has: Dict = { _ft_has: FtHas = {
"ohlcv_candle_limit": 1000, "ohlcv_candle_limit": 1000,
} }

View File

@@ -5,6 +5,7 @@ from typing import Dict
from freqtrade.constants import BuySell from freqtrade.constants import BuySell
from freqtrade.exchange import Exchange from freqtrade.exchange import Exchange
from freqtrade.exchange.exchange_types import FtHas
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -16,7 +17,7 @@ class Htx(Exchange):
with this exchange. with this exchange.
""" """
_ft_has: Dict = { _ft_has: FtHas = {
"stoploss_on_exchange": True, "stoploss_on_exchange": True,
"stop_price_param": "stopPrice", "stop_price_param": "stopPrice",
"stop_price_prop": "stopPrice", "stop_price_prop": "stopPrice",

View File

@@ -3,7 +3,11 @@
import logging import logging
from typing import Dict from typing import Dict
from ccxt import SIGNIFICANT_DIGITS
from freqtrade.enums import TradingMode
from freqtrade.exchange import Exchange from freqtrade.exchange import Exchange
from freqtrade.exchange.exchange_types import FtHas
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -14,11 +18,28 @@ class Hyperliquid(Exchange):
Contains adjustments needed for Freqtrade to work with this exchange. Contains adjustments needed for Freqtrade to work with this exchange.
""" """
_ft_has: Dict = { _ft_has: FtHas = {
# Only the most recent 5000 candles are available according to the # Only the most recent 5000 candles are available according to the
# exchange's API documentation. # exchange's API documentation.
"ohlcv_has_history": True, "ohlcv_has_history": False,
"ohlcv_candle_limit": 5000, "ohlcv_candle_limit": 5000,
"trades_has_history": False, # Trades endpoint doesn't seem available. "trades_has_history": False, # Trades endpoint doesn't seem available.
"exchange_has_overrides": {"fetchTrades": False}, "exchange_has_overrides": {"fetchTrades": False},
} }
@property
def _ccxt_config(self) -> Dict:
# Parameters to add directly to ccxt sync/async initialization.
# ccxt defaults to swap mode.
config = {}
if self.trading_mode == TradingMode.SPOT:
config.update({"options": {"defaultType": "spot"}})
config.update(super()._ccxt_config)
return config
@property
def precision_mode_price(self) -> int:
"""
Override the default precision mode for price.
"""
return SIGNIFICANT_DIGITS

View File

@@ -1,9 +1,9 @@
"""Idex exchange subclass""" """Idex exchange subclass"""
import logging import logging
from typing import Dict
from freqtrade.exchange import Exchange from freqtrade.exchange import Exchange
from freqtrade.exchange.exchange_types import FtHas
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -15,6 +15,6 @@ class Idex(Exchange):
with this exchange. with this exchange.
""" """
_ft_has: Dict = { _ft_has: FtHas = {
"ohlcv_candle_limit": 1000, "ohlcv_candle_limit": 1000,
} }

View File

@@ -12,7 +12,7 @@ from freqtrade.enums import MarginMode, TradingMode
from freqtrade.exceptions import DDosProtection, OperationalException, TemporaryError from freqtrade.exceptions import DDosProtection, OperationalException, TemporaryError
from freqtrade.exchange import Exchange from freqtrade.exchange import Exchange
from freqtrade.exchange.common import retrier from freqtrade.exchange.common import retrier
from freqtrade.exchange.types import Tickers from freqtrade.exchange.exchange_types import CcxtBalances, FtHas, Tickers
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -20,7 +20,7 @@ logger = logging.getLogger(__name__)
class Kraken(Exchange): class Kraken(Exchange):
_params: Dict = {"trading_agreement": "agree"} _params: Dict = {"trading_agreement": "agree"}
_ft_has: Dict = { _ft_has: FtHas = {
"stoploss_on_exchange": True, "stoploss_on_exchange": True,
"stop_price_param": "stopLossPrice", "stop_price_param": "stopLossPrice",
"stop_price_prop": "stopLossPrice", "stop_price_prop": "stopLossPrice",
@@ -57,7 +57,7 @@ class Kraken(Exchange):
return super().get_tickers(symbols=symbols, cached=cached) return super().get_tickers(symbols=symbols, cached=cached)
@retrier @retrier
def get_balances(self) -> dict: def get_balances(self) -> CcxtBalances:
if self._config["dry_run"]: if self._config["dry_run"]:
return {} return {}
@@ -78,6 +78,7 @@ class Kraken(Exchange):
# x["side"], x["amount"], # x["side"], x["amount"],
) )
for x in orders for x in orders
if x["remaining"] is not None and (x["side"] == "sell" or x["price"] is not None)
] ]
for bal in balances: for bal in balances:
if not isinstance(balances[bal], dict): if not isinstance(balances[bal], dict):

View File

@@ -5,6 +5,7 @@ from typing import Dict
from freqtrade.constants import BuySell from freqtrade.constants import BuySell
from freqtrade.exchange import Exchange from freqtrade.exchange import Exchange
from freqtrade.exchange.exchange_types import FtHas
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -20,7 +21,7 @@ class Kucoin(Exchange):
may still not work as expected. may still not work as expected.
""" """
_ft_has: Dict = { _ft_has: FtHas = {
"stoploss_on_exchange": True, "stoploss_on_exchange": True,
"stop_price_param": "stopPrice", "stop_price_param": "stopPrice",
"stop_price_prop": "stopPrice", "stop_price_prop": "stopPrice",

View File

@@ -13,7 +13,8 @@ from freqtrade.exceptions import (
TemporaryError, 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 API_RETRY_COUNT, retrier
from freqtrade.exchange.exchange_types import FtHas
from freqtrade.misc import safe_value_fallback2 from freqtrade.misc import safe_value_fallback2
from freqtrade.util import dt_now, dt_ts from freqtrade.util import dt_now, dt_ts
@@ -27,15 +28,16 @@ class Okx(Exchange):
Contains adjustments needed for Freqtrade to work with this exchange. Contains adjustments needed for Freqtrade to work with this exchange.
""" """
_ft_has: Dict = { _ft_has: FtHas = {
"ohlcv_candle_limit": 100, # Warning, special case with data prior to X months "ohlcv_candle_limit": 100, # Warning, special case with data prior to X months
"mark_ohlcv_timeframe": "4h", "mark_ohlcv_timeframe": "4h",
"funding_fee_timeframe": "8h", "funding_fee_timeframe": "8h",
"stoploss_order_types": {"limit": "limit"}, "stoploss_order_types": {"limit": "limit"},
"stoploss_on_exchange": True, "stoploss_on_exchange": True,
"trades_has_history": False, # Endpoint doesn't have a "since" parameter "trades_has_history": False, # Endpoint doesn't have a "since" parameter
"ws_enabled": True,
} }
_ft_has_futures: Dict = { _ft_has_futures: FtHas = {
"tickers_have_quoteVolume": False, "tickers_have_quoteVolume": False,
"stop_price_type_field": "slTriggerPxType", "stop_price_type_field": "slTriggerPxType",
"stop_price_type_value_mapping": { "stop_price_type_value_mapping": {
@@ -43,6 +45,7 @@ class Okx(Exchange):
PriceType.MARK: "index", PriceType.MARK: "index",
PriceType.INDEX: "mark", PriceType.INDEX: "mark",
}, },
"ws_enabled": True,
} }
_supported_trading_mode_margin_pairs: List[Tuple[TradingMode, MarginMode]] = [ _supported_trading_mode_margin_pairs: List[Tuple[TradingMode, MarginMode]] = [
@@ -205,6 +208,7 @@ class Okx(Exchange):
order["type"] = "stoploss" order["type"] = "stoploss"
return order return order
@retrier(retries=API_RETRY_COUNT)
def fetch_stoploss_order(self, order_id: str, pair: str, params: Optional[Dict] = None) -> Dict: def fetch_stoploss_order(self, order_id: str, pair: str, params: Optional[Dict] = None) -> Dict:
if self._config["dry_run"]: if self._config["dry_run"]:
return self.fetch_dry_run_order(order_id) return self.fetch_dry_run_order(order_id)
@@ -214,8 +218,20 @@ class Okx(Exchange):
order_reg = self._api.fetch_order(order_id, pair, params=params1) order_reg = self._api.fetch_order(order_id, pair, params=params1)
self._log_exchange_response("fetch_stoploss_order", order_reg) self._log_exchange_response("fetch_stoploss_order", order_reg)
return self._convert_stop_order(pair, order_id, order_reg) return self._convert_stop_order(pair, order_id, order_reg)
except ccxt.OrderNotFound: except (ccxt.OrderNotFound, ccxt.InvalidOrder):
pass pass
except ccxt.DDoSProtection as e:
raise DDosProtection(e) from e
except (ccxt.OperationFailed, ccxt.ExchangeError) as e:
raise TemporaryError(
f"Could not get order due to {e.__class__.__name__}. Message: {e}"
) from e
except ccxt.BaseError as e:
raise OperationalException(e) from e
return self._fetch_stop_order_fallback(order_id, pair)
def _fetch_stop_order_fallback(self, order_id: str, pair: str) -> Dict:
params2 = {"stop": True, "ordType": "conditional"} params2 = {"stop": True, "ordType": "conditional"}
for method in ( for method in (
self._api.fetch_open_orders, self._api.fetch_open_orders,
@@ -228,8 +244,16 @@ class Okx(Exchange):
if orders_f: if orders_f:
order = orders_f[0] order = orders_f[0]
return self._convert_stop_order(pair, order_id, order) return self._convert_stop_order(pair, order_id, order)
except ccxt.BaseError: except (ccxt.OrderNotFound, ccxt.InvalidOrder):
pass pass
except ccxt.DDoSProtection as e:
raise DDosProtection(e) from e
except (ccxt.OperationFailed, ccxt.ExchangeError) as e:
raise TemporaryError(
f"Could not get order due to {e.__class__.__name__}. Message: {e}"
) from e
except ccxt.BaseError as e:
raise OperationalException(e) from e
raise RetryableOrderError(f"StoplossOrder not found (pair: {pair} id: {order_id}).") raise RetryableOrderError(f"StoplossOrder not found (pair: {pair} id: {order_id}).")
def get_order_id_conditional(self, order: Dict[str, Any]) -> str: def get_order_id_conditional(self, order: Dict[str, Any]) -> str:

View File

@@ -1,30 +0,0 @@
from typing import Dict, List, Optional, Tuple, TypedDict
from freqtrade.enums import CandleType
class Ticker(TypedDict):
symbol: str
ask: Optional[float]
askVolume: Optional[float]
bid: Optional[float]
bidVolume: Optional[float]
last: Optional[float]
quoteVolume: Optional[float]
baseVolume: Optional[float]
# Several more - only listing required.
class OrderBook(TypedDict):
symbol: str
bids: List[Tuple[float, float]]
asks: List[Tuple[float, float]]
timestamp: Optional[int]
datetime: Optional[str]
nonce: Optional[int]
Tickers = Dict[str, Ticker]
# pair, timeframe, candleType, OHLCV, drop last?,
OHLCVResponse = Tuple[str, str, CandleType, List, bool]

View File

@@ -86,9 +86,6 @@ class BasePyTorchRegressor(BasePyTorchModel):
dk.feature_pipeline = self.define_data_pipeline(threads=dk.thread_count) dk.feature_pipeline = self.define_data_pipeline(threads=dk.thread_count)
dk.label_pipeline = self.define_label_pipeline(threads=dk.thread_count) dk.label_pipeline = self.define_label_pipeline(threads=dk.thread_count)
dd["train_labels"], _, _ = dk.label_pipeline.fit_transform(dd["train_labels"])
dd["test_labels"], _, _ = dk.label_pipeline.transform(dd["test_labels"])
(dd["train_features"], dd["train_labels"], dd["train_weights"]) = ( (dd["train_features"], dd["train_labels"], dd["train_weights"]) = (
dk.feature_pipeline.fit_transform( dk.feature_pipeline.fit_transform(
dd["train_features"], dd["train_labels"], dd["train_weights"] dd["train_features"], dd["train_labels"], dd["train_weights"]

View File

@@ -141,7 +141,7 @@ class PyTorchTransformerRegressor(BasePyTorchRegressor):
pred_df = pd.DataFrame(yb.detach().numpy(), columns=dk.label_list) pred_df = pd.DataFrame(yb.detach().numpy(), columns=dk.label_list)
pred_df, _, _ = dk.label_pipeline.inverse_transform(pred_df) pred_df, _, _ = dk.label_pipeline.inverse_transform(pred_df)
if self.freqai_info.get("DI_threshold", 0) > 0: if self.ft_params.get("DI_threshold", 0) > 0:
dk.DI_values = dk.feature_pipeline["di"].di_values dk.DI_values = dk.feature_pipeline["di"].di_values
else: else:
dk.DI_values = np.zeros(outliers.shape[0]) dk.DI_values = np.zeros(outliers.shape[0])

View File

@@ -22,6 +22,7 @@ from freqtrade.edge import Edge
from freqtrade.enums import ( from freqtrade.enums import (
ExitCheckTuple, ExitCheckTuple,
ExitType, ExitType,
MarginMode,
RPCMessageType, RPCMessageType,
SignalDirection, SignalDirection,
State, State,
@@ -61,7 +62,7 @@ from freqtrade.rpc.rpc_types import (
) )
from freqtrade.strategy.interface import IStrategy from freqtrade.strategy.interface import IStrategy
from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper
from freqtrade.util import MeasureTime from freqtrade.util import FtPrecise, MeasureTime
from freqtrade.util.migrations.binance_mig import migrate_binance_futures_names from freqtrade.util.migrations.binance_mig import migrate_binance_futures_names
from freqtrade.wallets import Wallets from freqtrade.wallets import Wallets
@@ -108,6 +109,7 @@ class FreqtradeBot(LoggingMixin):
PairLocks.timeframe = self.config["timeframe"] PairLocks.timeframe = self.config["timeframe"]
self.trading_mode: TradingMode = self.config.get("trading_mode", TradingMode.SPOT) self.trading_mode: TradingMode = self.config.get("trading_mode", TradingMode.SPOT)
self.margin_mode: MarginMode = self.config.get("margin_mode", MarginMode.NONE)
self.last_process: Optional[datetime] = None self.last_process: Optional[datetime] = None
# RPC runs in separate threads, can start handling external commands just after # RPC runs in separate threads, can start handling external commands just after
@@ -374,6 +376,7 @@ class FreqtradeBot(LoggingMixin):
if trade.exchange != self.exchange.id: if trade.exchange != self.exchange.id:
continue continue
trade.precision_mode = self.exchange.precisionMode trade.precision_mode = self.exchange.precisionMode
trade.precision_mode_price = self.exchange.precision_mode_price
trade.amount_precision = self.exchange.get_precision_amount(trade.pair) trade.amount_precision = self.exchange.get_precision_amount(trade.pair)
trade.price_precision = self.exchange.get_precision_price(trade.pair) trade.price_precision = self.exchange.get_precision_price(trade.pair)
trade.contract_size = self.exchange.get_contract_size(trade.pair) trade.contract_size = self.exchange.get_contract_size(trade.pair)
@@ -541,7 +544,11 @@ class FreqtradeBot(LoggingMixin):
) )
else: else:
trade.exit_reason = prev_exit_reason trade.exit_reason = prev_exit_reason
total = self.wallets.get_total(trade.base_currency) if trade.base_currency else 0 total = (
self.wallets.get_owned(trade.pair, trade.base_currency)
if trade.base_currency
else 0
)
if total < trade.amount: if total < trade.amount:
if trade.fully_canceled_entry_order_count == len(trade.orders): if trade.fully_canceled_entry_order_count == len(trade.orders):
logger.warning( logger.warning(
@@ -777,7 +784,14 @@ class FreqtradeBot(LoggingMixin):
if stake_amount is not None and stake_amount < 0.0: if stake_amount is not None and stake_amount < 0.0:
# We should decrease our position # We should decrease our position
amount = self.exchange.amount_to_contract_precision( amount = self.exchange.amount_to_contract_precision(
trade.pair, abs(float(stake_amount * trade.amount / trade.stake_amount)) trade.pair,
abs(
float(
FtPrecise(stake_amount)
* FtPrecise(trade.amount)
/ FtPrecise(trade.stake_amount)
)
),
) )
if amount == 0.0: if amount == 0.0:
@@ -973,7 +987,7 @@ class FreqtradeBot(LoggingMixin):
base_currency=base_currency, base_currency=base_currency,
stake_currency=self.config["stake_currency"], stake_currency=self.config["stake_currency"],
stake_amount=stake_amount, stake_amount=stake_amount,
amount=amount, amount=0,
is_open=True, is_open=True,
amount_requested=amount_requested, amount_requested=amount_requested,
fee_open=fee, fee_open=fee,
@@ -992,6 +1006,7 @@ class FreqtradeBot(LoggingMixin):
amount_precision=self.exchange.get_precision_amount(pair), amount_precision=self.exchange.get_precision_amount(pair),
price_precision=self.exchange.get_precision_price(pair), price_precision=self.exchange.get_precision_price(pair),
precision_mode=self.exchange.precisionMode, precision_mode=self.exchange.precisionMode,
precision_mode_price=self.exchange.precision_mode_price,
contract_size=self.exchange.get_contract_size(pair), contract_size=self.exchange.get_contract_size(pair),
) )
stoploss = self.strategy.stoploss if not self.edge else self.edge.get_stoploss(pair) stoploss = self.strategy.stoploss if not self.edge else self.edge.get_stoploss(pair)
@@ -2210,7 +2225,11 @@ class FreqtradeBot(LoggingMixin):
# TODO: should shorting/leverage be supported by Edge, # TODO: should shorting/leverage be supported by Edge,
# then this will need to be fixed. # then this will need to be fixed.
trade.adjust_stop_loss(trade.open_rate, self.strategy.stoploss, initial=True) trade.adjust_stop_loss(trade.open_rate, self.strategy.stoploss, initial=True)
if order.ft_order_side == trade.entry_side or (trade.amount > 0 and trade.is_open): if (
order.ft_order_side == trade.entry_side
or (trade.amount > 0 and trade.is_open)
or self.margin_mode == MarginMode.CROSS
):
# Must also run for partial exits # Must also run for partial exits
# TODO: Margin will need to use interest_rate as well. # TODO: Margin will need to use interest_rate as well.
# interest_rate = self.exchange.get_interest_rate() # interest_rate = self.exchange.get_interest_rate()

View File

@@ -1,8 +1,8 @@
# flake8: noqa: F401 # flake8: noqa: F401
from freqtrade.types.backtest_result_type import ( from freqtrade.ft_types.backtest_result_type import (
BacktestHistoryEntryType, BacktestHistoryEntryType,
BacktestMetadataType, BacktestMetadataType,
BacktestResultType, BacktestResultType,
get_BacktestResultType_default, get_BacktestResultType_default,
) )
from freqtrade.types.valid_exchanges_type import ValidExchangesType from freqtrade.ft_types.valid_exchanges_type import ValidExchangesType

View File

@@ -1,5 +1,5 @@
# Used for list-exchanges # Used for list-exchanges
from typing import List from typing import List, Optional
from typing_extensions import TypedDict from typing_extensions import TypedDict
@@ -11,8 +11,11 @@ class TradeModeType(TypedDict):
class ValidExchangesType(TypedDict): class ValidExchangesType(TypedDict):
name: str name: str
classname: str
valid: bool valid: bool
supported: bool supported: bool
comment: str comment: str
dex: bool dex: bool
is_alias: bool
alias_for: Optional[str]
trade_modes: List[TradeModeType] trade_modes: List[TradeModeType]

View File

@@ -15,6 +15,7 @@ if sys.version_info < (3, 9): # pragma: no cover
from freqtrade import __version__ from freqtrade import __version__
from freqtrade.commands import Arguments from freqtrade.commands import Arguments
from freqtrade.configuration import asyncio_setup
from freqtrade.constants import DOCS_LINK from freqtrade.constants import DOCS_LINK
from freqtrade.exceptions import ConfigurationError, FreqtradeException, OperationalException from freqtrade.exceptions import ConfigurationError, FreqtradeException, OperationalException
from freqtrade.loggers import setup_logging_pre from freqtrade.loggers import setup_logging_pre
@@ -33,6 +34,7 @@ def main(sysargv: Optional[List[str]] = None) -> None:
return_code: Any = 1 return_code: Any = 1
try: try:
setup_logging_pre() setup_logging_pre()
asyncio_setup()
arguments = Arguments(sysargv) arguments = Arguments(sysargv)
args = arguments.get_parsed_arg() args = arguments.get_parsed_arg()

View File

@@ -128,7 +128,10 @@ def round_dict(d, n):
return {k: (round(v, n) if isinstance(v, float) else v) for k, v in d.items()} return {k: (round(v, n) if isinstance(v, float) else v) for k, v in d.items()}
def safe_value_fallback(obj: dict, key1: str, key2: Optional[str] = None, default_value=None): DictMap = Union[Dict[str, Any], Mapping[str, Any]]
def safe_value_fallback(obj: DictMap, key1: str, key2: Optional[str] = None, default_value=None):
""" """
Search a value in obj, return this if it's not None. Search a value in obj, return this if it's not None.
Then search key2 in obj - return that if it's not none - then use default_value. Then search key2 in obj - return that if it's not none - then use default_value.
@@ -142,10 +145,7 @@ def safe_value_fallback(obj: dict, key1: str, key2: Optional[str] = None, defaul
return default_value return default_value
dictMap = Union[Dict[str, Any], Mapping[str, Any]] def safe_value_fallback2(dict1: DictMap, dict2: DictMap, key1: str, key2: str, default_value=None):
def safe_value_fallback2(dict1: dictMap, dict2: dictMap, key1: str, key2: str, default_value=None):
""" """
Search a value in dict1, return this if it's not None. Search a value in dict1, return this if it's not None.
Fall back to dict2 - return key2 from dict2 if it's not None. Fall back to dict2 - return key2 from dict2 if it's not None.

View File

@@ -1,7 +1,7 @@
import logging import logging
import time import time
from pathlib import Path from pathlib import Path
from typing import Any, Dict, List from typing import Any, Dict, List, Union
import pandas as pd import pandas as pd
from rich.text import Text from rich.text import Text
@@ -19,7 +19,9 @@ logger = logging.getLogger(__name__)
class LookaheadAnalysisSubFunctions: class LookaheadAnalysisSubFunctions:
@staticmethod @staticmethod
def text_table_lookahead_analysis_instances( def text_table_lookahead_analysis_instances(
config: Dict[str, Any], lookahead_instances: List[LookaheadAnalysis] config: Dict[str, Any],
lookahead_instances: List[LookaheadAnalysis],
caption: Union[str, None] = None,
): ):
headers = [ headers = [
"filename", "filename",
@@ -65,7 +67,9 @@ class LookaheadAnalysisSubFunctions:
] ]
) )
print_rich_table(data, headers, summary="Lookahead Analysis") print_rich_table(
data, headers, summary="Lookahead Analysis", table_kwargs={"caption": caption}
)
return data return data
@staticmethod @staticmethod
@@ -239,8 +243,24 @@ class LookaheadAnalysisSubFunctions:
# report the results # report the results
if lookaheadAnalysis_instances: if lookaheadAnalysis_instances:
caption: Union[str, None] = None
if any(
[
any(
[
indicator.startswith("&")
for indicator in inst.current_analysis.false_indicators
]
)
for inst in lookaheadAnalysis_instances
]
):
caption = (
"Any indicators in 'biased_indicators' which are used within "
"set_freqai_targets() can be ignored."
)
LookaheadAnalysisSubFunctions.text_table_lookahead_analysis_instances( LookaheadAnalysisSubFunctions.text_table_lookahead_analysis_instances(
config, lookaheadAnalysis_instances config, lookaheadAnalysis_instances, caption=caption
) )
if config.get("lookahead_analysis_exportfilename") is not None: if config.get("lookahead_analysis_exportfilename") is not None:
LookaheadAnalysisSubFunctions.export_to_csv(config, lookaheadAnalysis_instances) LookaheadAnalysisSubFunctions.export_to_csv(config, lookaheadAnalysis_instances)

View File

@@ -14,6 +14,7 @@ from freqtrade.loggers.set_log_levels import (
) )
from freqtrade.optimize.backtesting import Backtesting from freqtrade.optimize.backtesting import Backtesting
from freqtrade.optimize.base_analysis import BaseAnalysis, VarHolder from freqtrade.optimize.base_analysis import BaseAnalysis, VarHolder
from freqtrade.resolvers import StrategyResolver
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -21,10 +22,19 @@ logger = logging.getLogger(__name__)
class RecursiveAnalysis(BaseAnalysis): class RecursiveAnalysis(BaseAnalysis):
def __init__(self, config: Dict[str, Any], strategy_obj: Dict): def __init__(self, config: Dict[str, Any], strategy_obj: Dict):
self._startup_candle = config.get("startup_candle", [199, 399, 499, 999, 1999]) self._startup_candle = list(
map(int, config.get("startup_candle", [199, 399, 499, 999, 1999]))
)
super().__init__(config, strategy_obj) super().__init__(config, strategy_obj)
strat = StrategyResolver.load_strategy(config)
self._strat_scc = strat.startup_candle_count
if self._strat_scc not in self._startup_candle:
self._startup_candle.append(self._strat_scc)
self._startup_candle.sort()
self.partial_varHolder_array: List[VarHolder] = [] self.partial_varHolder_array: List[VarHolder] = []
self.partial_varHolder_lookahead_array: List[VarHolder] = [] self.partial_varHolder_lookahead_array: List[VarHolder] = []
@@ -58,9 +68,13 @@ class RecursiveAnalysis(BaseAnalysis):
values_diff = compare_df.loc[indicator] values_diff = compare_df.loc[indicator]
values_diff_self = values_diff.loc["self"] values_diff_self = values_diff.loc["self"]
values_diff_other = values_diff.loc["other"] values_diff_other = values_diff.loc["other"]
diff = (values_diff_other - values_diff_self) / values_diff_self * 100
self.dict_recursive[indicator][part.startup_candle] = f"{diff:.3f}%" if values_diff_self and values_diff_other:
diff = (values_diff_other - values_diff_self) / values_diff_self * 100
str_diff = f"{diff:.3f}%"
else:
str_diff = "NaN"
self.dict_recursive[indicator][part.startup_candle] = str_diff
else: else:
logger.info("No variance on indicator(s) found due to recursive formula.") logger.info("No variance on indicator(s) found due to recursive formula.")
@@ -174,7 +188,7 @@ class RecursiveAnalysis(BaseAnalysis):
start_date_partial = end_date_full - timedelta(minutes=int(timeframe_minutes)) start_date_partial = end_date_full - timedelta(minutes=int(timeframe_minutes))
for startup_candle in self._startup_candle: for startup_candle in self._startup_candle:
self.fill_partial_varholder(start_date_partial, int(startup_candle)) self.fill_partial_varholder(start_date_partial, startup_candle)
# Restore verbosity, so it's not too quiet for the next strategy # Restore verbosity, so it's not too quiet for the next strategy
restore_verbosity_for_bias_tester() restore_verbosity_for_bias_tester()

View File

@@ -17,9 +17,13 @@ class RecursiveAnalysisSubFunctions:
@staticmethod @staticmethod
def text_table_recursive_analysis_instances(recursive_instances: List[RecursiveAnalysis]): def text_table_recursive_analysis_instances(recursive_instances: List[RecursiveAnalysis]):
startups = recursive_instances[0]._startup_candle startups = recursive_instances[0]._startup_candle
strat_scc = recursive_instances[0]._strat_scc
headers = ["Indicators"] headers = ["Indicators"]
for candle in startups: for candle in startups:
headers.append(str(candle)) if candle == strat_scc:
headers.append(f"{candle} (from strategy)")
else:
headers.append(str(candle))
data = [] data = []
for inst in recursive_instances: for inst in recursive_instances:

View File

@@ -36,6 +36,7 @@ from freqtrade.exchange import (
timeframe_to_seconds, timeframe_to_seconds,
) )
from freqtrade.exchange.exchange import Exchange from freqtrade.exchange.exchange import Exchange
from freqtrade.ft_types import BacktestResultType, get_BacktestResultType_default
from freqtrade.mixins import LoggingMixin from freqtrade.mixins import LoggingMixin
from freqtrade.optimize.backtest_caching import get_strategy_run_id from freqtrade.optimize.backtest_caching import get_strategy_run_id
from freqtrade.optimize.bt_progress import BTProgress from freqtrade.optimize.bt_progress import BTProgress
@@ -61,7 +62,6 @@ from freqtrade.plugins.protectionmanager import ProtectionManager
from freqtrade.resolvers import ExchangeResolver, StrategyResolver from freqtrade.resolvers import ExchangeResolver, StrategyResolver
from freqtrade.strategy.interface import IStrategy from freqtrade.strategy.interface import IStrategy
from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper
from freqtrade.types import BacktestResultType, get_BacktestResultType_default
from freqtrade.util import FtPrecise from freqtrade.util import FtPrecise
from freqtrade.util.migrations import migrate_data from freqtrade.util.migrations import migrate_data
from freqtrade.wallets import Wallets from freqtrade.wallets import Wallets
@@ -122,6 +122,7 @@ class Backtesting:
self.processed_dfs: Dict[str, Dict] = {} self.processed_dfs: Dict[str, Dict] = {}
self.rejected_dict: Dict[str, List] = {} self.rejected_dict: Dict[str, List] = {}
self.rejected_df: Dict[str, Dict] = {} self.rejected_df: Dict[str, Dict] = {}
self.exited_dfs: Dict[str, Dict] = {}
self._exchange_name = self.config["exchange"]["name"] self._exchange_name = self.config["exchange"]["name"]
if not exchange: if not exchange:
@@ -181,6 +182,7 @@ class Backtesting:
self.fee = max(fee for fee in fees if fee is not None) self.fee = max(fee for fee in fees if fee is not None)
logger.info(f"Using fee {self.fee:.4%} - worst case fee from exchange (lowest tier).") logger.info(f"Using fee {self.fee:.4%} - worst case fee from exchange (lowest tier).")
self.precision_mode = self.exchange.precisionMode self.precision_mode = self.exchange.precisionMode
self.precision_mode_price = self.exchange.precision_mode_price
if self.config.get("freqai_backtest_live_models", False): if self.config.get("freqai_backtest_live_models", False):
from freqtrade.freqai.utils import get_timerange_backtest_live_models from freqtrade.freqai.utils import get_timerange_backtest_live_models
@@ -329,15 +331,15 @@ class Backtesting:
else: else:
self.detail_data = {} self.detail_data = {}
if self.trading_mode == TradingMode.FUTURES: if self.trading_mode == TradingMode.FUTURES:
self.funding_fee_timeframe: str = self.exchange.get_option("funding_fee_timeframe") funding_fee_timeframe: str = self.exchange.get_option("funding_fee_timeframe")
self.funding_fee_timeframe_secs: int = timeframe_to_seconds(self.funding_fee_timeframe) self.funding_fee_timeframe_secs: int = timeframe_to_seconds(funding_fee_timeframe)
mark_timeframe: str = self.exchange.get_option("mark_ohlcv_timeframe") mark_timeframe: str = self.exchange.get_option("mark_ohlcv_timeframe")
# Load additional futures data. # Load additional futures data.
funding_rates_dict = history.load_data( funding_rates_dict = history.load_data(
datadir=self.config["datadir"], datadir=self.config["datadir"],
pairs=self.pairlists.whitelist, pairs=self.pairlists.whitelist,
timeframe=self.funding_fee_timeframe, timeframe=funding_fee_timeframe,
timerange=self.timerange, timerange=self.timerange,
startup_candles=0, startup_candles=0,
fail_without_data=True, fail_without_data=True,
@@ -785,7 +787,7 @@ class Backtesting:
) )
if rate is not None and rate != close_rate: if rate is not None and rate != close_rate:
close_rate = price_to_precision( close_rate = price_to_precision(
rate, trade.price_precision, self.precision_mode rate, trade.price_precision, self.precision_mode_price
) )
# We can't place orders lower than current low. # We can't place orders lower than current low.
# freqtrade does not support this in live, and the order would fill immediately # freqtrade does not support this in live, and the order would fill immediately
@@ -929,7 +931,9 @@ class Backtesting:
# We can't place orders higher than current high (otherwise it'd be a stop limit entry) # We can't place orders higher than current high (otherwise it'd be a stop limit entry)
# which freqtrade does not support in live. # which freqtrade does not support in live.
if new_rate is not None and new_rate != propose_rate: if new_rate is not None and new_rate != propose_rate:
propose_rate = price_to_precision(new_rate, price_precision, self.precision_mode) propose_rate = price_to_precision(
new_rate, price_precision, self.precision_mode_price
)
if direction == "short": if direction == "short":
propose_rate = max(propose_rate, row[LOW_IDX]) propose_rate = max(propose_rate, row[LOW_IDX])
else: else:
@@ -1095,7 +1099,7 @@ class Backtesting:
open_rate_requested=propose_rate, open_rate_requested=propose_rate,
open_date=current_time, open_date=current_time,
stake_amount=stake_amount, stake_amount=stake_amount,
amount=amount, amount=0,
amount_requested=amount, amount_requested=amount,
fee_open=self.fee, fee_open=self.fee,
fee_close=self.fee, fee_close=self.fee,
@@ -1109,6 +1113,7 @@ class Backtesting:
amount_precision=precision_amount, amount_precision=precision_amount,
price_precision=precision_price, price_precision=precision_price,
precision_mode=self.precision_mode, precision_mode=self.precision_mode,
precision_mode_price=self.precision_mode_price,
contract_size=contract_size, contract_size=contract_size,
orders=[], orders=[],
) )
@@ -1161,12 +1166,10 @@ class Backtesting:
self._exit_trade( self._exit_trade(
trade, exit_row, exit_row[OPEN_IDX], trade.amount, ExitType.FORCE_EXIT.value trade, exit_row, exit_row[OPEN_IDX], trade.amount, ExitType.FORCE_EXIT.value
) )
trade.orders[-1].close_bt_order(exit_row[DATE_IDX].to_pydatetime(), trade)
trade.close_date = exit_row[DATE_IDX].to_pydatetime()
trade.exit_reason = ExitType.FORCE_EXIT.value trade.exit_reason = ExitType.FORCE_EXIT.value
trade.close(exit_row[OPEN_IDX], show_msg=False) self._process_exit_order(
LocalTrade.close_bt_trade(trade) trade.orders[-1], trade, exit_row[DATE_IDX].to_pydatetime(), exit_row, pair
)
def trade_slot_available(self, open_trade_count: int) -> bool: def trade_slot_available(self, open_trade_count: int) -> bool:
# Always allow trades when max_open_trades is enabled. # Always allow trades when max_open_trades is enabled.
@@ -1332,10 +1335,9 @@ class Backtesting:
pair: str, pair: str,
current_time: datetime, current_time: datetime,
end_date: datetime, end_date: datetime,
open_trade_count_start: int,
trade_dir: Optional[LongShort], trade_dir: Optional[LongShort],
is_first: bool = True, is_first: bool = True,
) -> int: ) -> None:
""" """
NOTE: This method is used by Hyperopt at each iteration. Please keep it optimized. NOTE: This method is used by Hyperopt at each iteration. Please keep it optimized.
@@ -1345,7 +1347,6 @@ class Backtesting:
# 1. Manage currently open orders of active trades # 1. Manage currently open orders of active trades
if self.manage_open_orders(t, current_time, row): if self.manage_open_orders(t, current_time, row):
# Close trade # Close trade
open_trade_count_start -= 1
LocalTrade.remove_bt_trade(t) LocalTrade.remove_bt_trade(t)
self.wallets.update() self.wallets.update()
@@ -1361,13 +1362,9 @@ class Backtesting:
and trade_dir is not None and trade_dir is not None
and not PairLocks.is_pair_locked(pair, row[DATE_IDX], trade_dir) and not PairLocks.is_pair_locked(pair, row[DATE_IDX], trade_dir)
): ):
if self.trade_slot_available(open_trade_count_start): if self.trade_slot_available(LocalTrade.bt_open_open_trade_count):
trade = self._enter_trade(pair, row, trade_dir) trade = self._enter_trade(pair, row, trade_dir)
if trade: if trade:
# TODO: hacky workaround to avoid opening > max_open_trades
# This emulates previous behavior - not sure if this is correct
# Prevents entering if the trade-slot was freed in this candle
open_trade_count_start += 1
self.wallets.update() self.wallets.update()
else: else:
self._collate_rejected(pair, row) self._collate_rejected(pair, row)
@@ -1386,7 +1383,28 @@ class Backtesting:
order = trade.select_order(trade.exit_side, is_open=True) order = trade.select_order(trade.exit_side, is_open=True)
if order: if order:
self._process_exit_order(order, trade, current_time, row, pair) self._process_exit_order(order, trade, current_time, row, pair)
return open_trade_count_start
def time_pair_generator(
self, start_date: datetime, end_date: datetime, increment: timedelta, pairs: List[str]
):
"""
Backtest time and pair generator
"""
current_time = start_date + increment
self.progress.init_step(
BacktestState.BACKTEST, int((end_date - start_date) / self.timeframe_td)
)
while current_time <= end_date:
is_first = True
# Pairs that have open trades should be processed first
new_pairlist = list(dict.fromkeys([t.pair for t in LocalTrade.bt_trades_open] + pairs))
for pair in new_pairlist:
yield current_time, pair, is_first
is_first = False
self.progress.increment()
current_time += increment
def backtest(self, processed: Dict, start_date: datetime, end_date: datetime) -> Dict[str, Any]: def backtest(self, processed: Dict, start_date: datetime, end_date: datetime) -> Dict[str, Any]:
""" """
@@ -1411,87 +1429,75 @@ class Backtesting:
# Indexes per pair, so some pairs are allowed to have a missing start. # Indexes per pair, so some pairs are allowed to have a missing start.
indexes: Dict = defaultdict(int) indexes: Dict = defaultdict(int)
current_time = start_date + self.timeframe_td
self.progress.init_step(
BacktestState.BACKTEST, int((end_date - start_date) / self.timeframe_td)
)
# Loop timerange and get candle for each pair at that point in time # Loop timerange and get candle for each pair at that point in time
while current_time <= end_date: for current_time, pair, is_first in self.time_pair_generator(
open_trade_count_start = LocalTrade.bt_open_open_trade_count start_date, end_date, self.timeframe_td, list(data.keys())
self.check_abort() ):
strategy_safe_wrapper(self.strategy.bot_loop_start, supress_error=True)( if is_first:
current_time=current_time self.check_abort()
) strategy_safe_wrapper(self.strategy.bot_loop_start, supress_error=True)(
for i, pair in enumerate(data): current_time=current_time
row_index = indexes[pair] )
row = self.validate_row(data, pair, row_index, current_time) row_index = indexes[pair]
if not row: row = self.validate_row(data, pair, row_index, current_time)
if not row:
continue
row_index += 1
indexes[pair] = row_index
self.dataprovider._set_dataframe_max_index(self.required_startup + row_index)
self.dataprovider._set_dataframe_max_date(current_time)
current_detail_time: datetime = row[DATE_IDX].to_pydatetime()
trade_dir: Optional[LongShort] = self.check_for_trade_entry(row)
if (
(trade_dir is not None or len(LocalTrade.bt_trades_open_pp[pair]) > 0)
and self.timeframe_detail
and pair in self.detail_data
):
# Spread out into detail timeframe.
# Should only happen when we are either in a trade for this pair
# or when we got the signal for a new trade.
exit_candle_end = current_detail_time + self.timeframe_td
detail_data = self.detail_data[pair]
detail_data = detail_data.loc[
(detail_data["date"] >= current_detail_time)
& (detail_data["date"] < exit_candle_end)
].copy()
if len(detail_data) == 0:
# Fall back to "regular" data if no detail data was found for this candle
self.backtest_loop(row, pair, current_time, end_date, trade_dir)
continue continue
detail_data.loc[:, "enter_long"] = row[LONG_IDX]
row_index += 1 detail_data.loc[:, "exit_long"] = row[ELONG_IDX]
indexes[pair] = row_index detail_data.loc[:, "enter_short"] = row[SHORT_IDX]
self.dataprovider._set_dataframe_max_index(self.required_startup + row_index) detail_data.loc[:, "exit_short"] = row[ESHORT_IDX]
self.dataprovider._set_dataframe_max_date(current_time) detail_data.loc[:, "enter_tag"] = row[ENTER_TAG_IDX]
current_detail_time: datetime = row[DATE_IDX].to_pydatetime() detail_data.loc[:, "exit_tag"] = row[EXIT_TAG_IDX]
trade_dir: Optional[LongShort] = self.check_for_trade_entry(row) is_first = True
current_time_det = current_time
if ( for det_row in detail_data[HEADERS].values.tolist():
(trade_dir is not None or len(LocalTrade.bt_trades_open_pp[pair]) > 0) self.dataprovider._set_dataframe_max_date(current_time_det)
and self.timeframe_detail self.backtest_loop(
and pair in self.detail_data det_row,
): pair,
# Spread out into detail timeframe. current_time_det,
# Should only happen when we are either in a trade for this pair end_date,
# or when we got the signal for a new trade. trade_dir,
exit_candle_end = current_detail_time + self.timeframe_td is_first,
detail_data = self.detail_data[pair]
detail_data = detail_data.loc[
(detail_data["date"] >= current_detail_time)
& (detail_data["date"] < exit_candle_end)
].copy()
if len(detail_data) == 0:
# Fall back to "regular" data if no detail data was found for this candle
open_trade_count_start = self.backtest_loop(
row, pair, current_time, end_date, open_trade_count_start, trade_dir
)
continue
detail_data.loc[:, "enter_long"] = row[LONG_IDX]
detail_data.loc[:, "exit_long"] = row[ELONG_IDX]
detail_data.loc[:, "enter_short"] = row[SHORT_IDX]
detail_data.loc[:, "exit_short"] = row[ESHORT_IDX]
detail_data.loc[:, "enter_tag"] = row[ENTER_TAG_IDX]
detail_data.loc[:, "exit_tag"] = row[EXIT_TAG_IDX]
is_first = True
current_time_det = current_time
for det_row in detail_data[HEADERS].values.tolist():
self.dataprovider._set_dataframe_max_date(current_time_det)
open_trade_count_start = self.backtest_loop(
det_row,
pair,
current_time_det,
end_date,
open_trade_count_start,
trade_dir,
is_first,
)
current_time_det += self.timeframe_detail_td
is_first = False
else:
self.dataprovider._set_dataframe_max_date(current_time)
open_trade_count_start = self.backtest_loop(
row, pair, current_time, end_date, open_trade_count_start, trade_dir
) )
current_time_det += self.timeframe_detail_td
# Move time one configured time_interval ahead. is_first = False
self.progress.increment() else:
current_time += self.timeframe_td self.dataprovider._set_dataframe_max_date(current_time)
self.backtest_loop(row, pair, current_time, end_date, trade_dir)
self.handle_left_open(LocalTrade.bt_trades_open_pp, data=data) self.handle_left_open(LocalTrade.bt_trades_open_pp, data=data)
self.wallets.update() self.wallets.update()
results = trade_list_to_dataframe(LocalTrade.trades) results = trade_list_to_dataframe(LocalTrade.bt_trades)
return { return {
"results": results, "results": results,
"config": self.strategy.config, "config": self.strategy.config,
@@ -1559,11 +1565,14 @@ class Backtesting:
and self.dataprovider.runmode == RunMode.BACKTEST and self.dataprovider.runmode == RunMode.BACKTEST
): ):
self.processed_dfs[strategy_name] = generate_trade_signal_candles( self.processed_dfs[strategy_name] = generate_trade_signal_candles(
preprocessed_tmp, results preprocessed_tmp, results, "open_date"
) )
self.rejected_df[strategy_name] = generate_rejected_signals( self.rejected_df[strategy_name] = generate_rejected_signals(
preprocessed_tmp, self.rejected_dict preprocessed_tmp, self.rejected_dict
) )
self.exited_dfs[strategy_name] = generate_trade_signal_candles(
preprocessed_tmp, results, "close_date"
)
return min_date, max_date return min_date, max_date
@@ -1639,7 +1648,11 @@ class Backtesting:
and self.dataprovider.runmode == RunMode.BACKTEST and self.dataprovider.runmode == RunMode.BACKTEST
): ):
store_backtest_analysis_results( store_backtest_analysis_results(
self.config["exportfilename"], self.processed_dfs, self.rejected_df, dt_appendix self.config["exportfilename"],
self.processed_dfs,
self.rejected_df,
self.exited_dfs,
dt_appendix,
) )
# Results may be mixed up now. Sort them so they follow --strategy-list order. # Results may be mixed up now. Sort them so they follow --strategy-list order.

View File

@@ -17,7 +17,6 @@ import rapidjson
from joblib import Parallel, cpu_count, delayed, dump, load, wrap_non_picklable_objects from joblib import Parallel, cpu_count, delayed, dump, load, wrap_non_picklable_objects
from joblib.externals import cloudpickle from joblib.externals import cloudpickle
from pandas import DataFrame from pandas import DataFrame
from rich.align import Align
from rich.console import Console from rich.console import Console
from freqtrade.constants import DATETIME_PRINT_FORMAT, FTHYPT_FILEVERSION, LAST_BT_RESULT_FN, Config from freqtrade.constants import DATETIME_PRINT_FORMAT, FTHYPT_FILEVERSION, LAST_BT_RESULT_FN, Config
@@ -80,7 +79,7 @@ class Hyperopt:
self.max_open_trades_space: List[Dimension] = [] self.max_open_trades_space: List[Dimension] = []
self.dimensions: List[Dimension] = [] self.dimensions: List[Dimension] = []
self._hyper_out: HyperoptOutput = HyperoptOutput() self._hyper_out: HyperoptOutput = HyperoptOutput(streaming=True)
self.config = config self.config = config
self.min_date: datetime self.min_date: datetime
@@ -168,7 +167,9 @@ class Hyperopt:
cloudpickle.register_pickle_by_value(sys.modules[modules.__module__]) cloudpickle.register_pickle_by_value(sys.modules[modules.__module__])
self.hyperopt_pickle_magic(modules.__bases__) self.hyperopt_pickle_magic(modules.__bases__)
def _get_params_dict(self, dimensions: List[Dimension], raw_params: List[Any]) -> Dict: def _get_params_dict(
self, dimensions: List[Dimension], raw_params: List[Any]
) -> Dict[str, Any]:
# Ensure the number of dimensions match # Ensure the number of dimensions match
# the number of parameters in the list. # the number of parameters in the list.
if len(raw_params) != len(dimensions): if len(raw_params) != len(dimensions):
@@ -317,7 +318,7 @@ class Hyperopt:
+ self.max_open_trades_space + self.max_open_trades_space
) )
def assign_params(self, params_dict: Dict, category: str) -> None: def assign_params(self, params_dict: Dict[str, Any], category: str) -> None:
""" """
Assign hyperoptable parameters Assign hyperoptable parameters
""" """
@@ -404,7 +405,12 @@ class Hyperopt:
) )
def _get_results_dict( def _get_results_dict(
self, backtesting_results, min_date, max_date, params_dict, processed: Dict[str, DataFrame] self,
backtesting_results: Dict[str, Any],
min_date: datetime,
max_date: datetime,
params_dict: Dict[str, Any],
processed: Dict[str, DataFrame],
) -> Dict[str, Any]: ) -> Dict[str, Any]:
params_details = self._get_params_details(params_dict) params_details = self._get_params_details(params_dict)
@@ -628,7 +634,7 @@ class Hyperopt:
# Define progressbar # Define progressbar
with get_progress_tracker( with get_progress_tracker(
console=console, console=console,
cust_objs=[Align.center(self._hyper_out.table)], cust_callables=[self._hyper_out],
) as pbar: ) as pbar:
task = pbar.add_task("Epochs", total=self.total_epochs) task = pbar.add_task("Epochs", total=self.total_epochs)

View File

@@ -1,6 +1,8 @@
import sys import sys
from typing import List, Optional, Union from os import get_terminal_size
from typing import Any, List, Optional
from rich.align import Align
from rich.console import Console from rich.console import Console
from rich.table import Table from rich.table import Table
from rich.text import Text from rich.text import Text
@@ -11,7 +13,16 @@ from freqtrade.util import fmt_coin
class HyperoptOutput: class HyperoptOutput:
def __init__(self): def __init__(self, streaming=False) -> None:
self._results: List[Any] = []
self._streaming = streaming
self.__init_table()
def __call__(self, *args: Any, **kwds: Any) -> Any:
return Align.center(self.table)
def __init_table(self) -> None:
"""Initialize table"""
self.table = Table( self.table = Table(
title="Hyperopt results", title="Hyperopt results",
) )
@@ -26,17 +37,6 @@ class HyperoptOutput:
self.table.add_column("Objective", justify="right") self.table.add_column("Objective", justify="right")
self.table.add_column("Max Drawdown (Acct)", justify="right") self.table.add_column("Max Drawdown (Acct)", justify="right")
def _add_row(self, data: List[Union[str, Text]]):
"""Add single row"""
row_to_add: List[Union[str, Text]] = [r if isinstance(r, Text) else str(r) for r in data]
self.table.add_row(*row_to_add)
def _add_rows(self, data: List[List[Union[str, Text]]]):
"""add multiple rows"""
for row in data:
self._add_row(row)
def print(self, console: Optional[Console] = None, *, print_colorized=True): def print(self, console: Optional[Console] = None, *, print_colorized=True):
if not console: if not console:
console = Console( console = Console(
@@ -55,8 +55,28 @@ class HyperoptOutput:
) -> None: ) -> None:
"""Format one or multiple rows and add them""" """Format one or multiple rows and add them"""
stake_currency = config["stake_currency"] stake_currency = config["stake_currency"]
self._results.extend(results)
for r in results: max_rows: Optional[int] = None
if self._streaming:
try:
ts = get_terminal_size()
# Get terminal size.
# Account for header, borders, and for the progress bar.
# This assumes that lines don't wrap.
if ts.columns < 148:
# If the terminal is too small, we can't display the table properly.
# We will halve the number of rows to display.
max_rows = -(int(ts.lines / 2) - 6)
else:
max_rows = -(ts.lines - 6)
except OSError:
# If we can't get the terminal size, we will just display the last 10 rows.
pass
self.__init_table()
for r in self._results[max_rows:]:
self.table.add_row( self.table.add_row(
*[ *[
# "Best": # "Best":

View File

@@ -2,8 +2,8 @@ import logging
from typing import Any, Dict, List, Literal, Union from typing import Any, Dict, List, Literal, Union
from freqtrade.constants import UNLIMITED_STAKE_AMOUNT, Config from freqtrade.constants import UNLIMITED_STAKE_AMOUNT, Config
from freqtrade.ft_types import BacktestResultType
from freqtrade.optimize.optimize_reports.optimize_reports import generate_periodic_breakdown_stats from freqtrade.optimize.optimize_reports.optimize_reports import generate_periodic_breakdown_stats
from freqtrade.types import BacktestResultType
from freqtrade.util import decimals_per_coin, fmt_coin, print_rich_table from freqtrade.util import decimals_per_coin, fmt_coin, print_rich_table
@@ -263,12 +263,32 @@ def text_table_add_metrics(strat_results: Dict) -> None:
else [] else []
) )
trading_mode = (
(
[
(
"Trading Mode",
(
""
if not strat_results.get("margin_mode")
or strat_results.get("trading_mode", "spot") == "spot"
else f"{strat_results['margin_mode'].capitalize()} "
)
+ f"{strat_results['trading_mode'].capitalize()}",
)
]
)
if "trading_mode" in strat_results
else []
)
# Newly added fields should be ignored if they are missing in strat_results. hyperopt-show # Newly added fields should be ignored if they are missing in strat_results. hyperopt-show
# command stores these results and newer version of freqtrade must be able to handle old # command stores these results and newer version of freqtrade must be able to handle old
# results with missing new fields. # results with missing new fields.
metrics = [ metrics = [
("Backtesting from", strat_results["backtest_start"]), ("Backtesting from", strat_results["backtest_start"]),
("Backtesting to", strat_results["backtest_end"]), ("Backtesting to", strat_results["backtest_end"]),
*trading_mode,
("Max open trades", strat_results["max_open_trades"]), ("Max open trades", strat_results["max_open_trades"]),
("", ""), # Empty line to improve readability ("", ""), # Empty line to improve readability
( (

View File

@@ -5,9 +5,9 @@ from typing import Dict, Optional
from pandas import DataFrame from pandas import DataFrame
from freqtrade.constants import LAST_BT_RESULT_FN from freqtrade.constants import LAST_BT_RESULT_FN
from freqtrade.ft_types import BacktestResultType
from freqtrade.misc import file_dump_joblib, file_dump_json from freqtrade.misc import file_dump_joblib, file_dump_json
from freqtrade.optimize.backtest_caching import get_backtest_metadata_filename from freqtrade.optimize.backtest_caching import get_backtest_metadata_filename
from freqtrade.types import BacktestResultType
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -90,7 +90,12 @@ def _store_backtest_analysis_data(
def store_backtest_analysis_results( def store_backtest_analysis_results(
recordfilename: Path, candles: Dict[str, Dict], trades: Dict[str, Dict], dtappendix: str recordfilename: Path,
candles: Dict[str, Dict],
trades: Dict[str, Dict],
exited: Dict[str, Dict],
dtappendix: str,
) -> None: ) -> None:
_store_backtest_analysis_data(recordfilename, candles, dtappendix, "signals") _store_backtest_analysis_data(recordfilename, candles, dtappendix, "signals")
_store_backtest_analysis_data(recordfilename, trades, dtappendix, "rejected") _store_backtest_analysis_data(recordfilename, trades, dtappendix, "rejected")
_store_backtest_analysis_data(recordfilename, exited, dtappendix, "exited")

View File

@@ -17,7 +17,7 @@ from freqtrade.data.metrics import (
calculate_sharpe, calculate_sharpe,
calculate_sortino, calculate_sortino,
) )
from freqtrade.types import BacktestResultType from freqtrade.ft_types import BacktestResultType
from freqtrade.util import decimals_per_coin, fmt_coin from freqtrade.util import decimals_per_coin, fmt_coin
@@ -25,8 +25,8 @@ logger = logging.getLogger(__name__)
def generate_trade_signal_candles( def generate_trade_signal_candles(
preprocessed_df: Dict[str, DataFrame], bt_results: Dict[str, Any] preprocessed_df: Dict[str, DataFrame], bt_results: Dict[str, Any], date_col: str
) -> DataFrame: ) -> Dict[str, DataFrame]:
signal_candles_only = {} signal_candles_only = {}
for pair in preprocessed_df.keys(): for pair in preprocessed_df.keys():
signal_candles_only_df = DataFrame() signal_candles_only_df = DataFrame()
@@ -36,8 +36,8 @@ def generate_trade_signal_candles(
pairresults = resdf.loc[(resdf["pair"] == pair)] pairresults = resdf.loc[(resdf["pair"] == pair)]
if pairdf.shape[0] > 0: if pairdf.shape[0] > 0:
for t, v in pairresults.open_date.items(): for t, v in pairresults.iterrows():
allinds = pairdf.loc[(pairdf["date"] < v)] allinds = pairdf.loc[(pairdf["date"] < v[date_col])]
signal_inds = allinds.iloc[[-1]] signal_inds = allinds.iloc[[-1]]
signal_candles_only_df = concat( signal_candles_only_df = concat(
[signal_candles_only_df.infer_objects(), signal_inds.infer_objects()] [signal_candles_only_df.infer_objects(), signal_inds.infer_objects()]
@@ -504,6 +504,8 @@ def generate_strategy_stats(
"exit_profit_only": config["exit_profit_only"], "exit_profit_only": config["exit_profit_only"],
"exit_profit_offset": config["exit_profit_offset"], "exit_profit_offset": config["exit_profit_offset"],
"ignore_roi_if_entry_signal": config["ignore_roi_if_entry_signal"], "ignore_roi_if_entry_signal": config["ignore_roi_if_entry_signal"],
"trading_mode": config["trading_mode"],
"margin_mode": config["margin_mode"],
**periodic_breakdown, **periodic_breakdown,
**daily_stats, **daily_stats,
**trade_stats, **trade_stats,

Some files were not shown because too many files have changed in this diff Show More