Compare commits

..

703 Commits

Author SHA1 Message Date
Matthias 73363d925f Update download-artifact to pattern. 2024-03-31 17:37:57 +02:00
Matthias ad56462fa1 Merge pull request #10025 from freqtrade/new_release
New release
2024-03-31 13:36:51 +02:00
Matthias a6bfe1a4af Bump version to 2024.3 2024-03-31 09:54:34 +02:00
Matthias f960e343cc Merge branch 'stable' into new_release 2024-03-31 09:54:05 +02:00
Matthias 2072a625bd Align build job names 2024-03-31 09:33:55 +02:00
Matthias 8792d56251 Update dockerhub-description workflow to new approach 2024-03-31 09:29:00 +02:00
Matthias 556db08b0a Rename CI files 2024-03-31 09:28:13 +02:00
Matthias b651e13bd6 Schedule leverage tier updates 2024-03-31 09:27:03 +02:00
Matthias 13ae0f81ca Merge pull request #10024 from freqtrade/update/pre-commit-hooks
Update Binance Leverage Tiers
2024-03-31 09:26:17 +02:00
xmatthias a534340cc1 chore: update pre-commit hooks 2024-03-31 07:15:47 +00:00
Matthias 067a7315f2 Use proper secrets syntax 2024-03-31 09:15:05 +02:00
Matthias f405b7d1cd Add CI step for leverage tier updates 2024-03-31 09:04:39 +02:00
Matthias 178a0a5db0 Fix typo in update script 2024-03-31 08:32:25 +02:00
Matthias 055c4396d1 De-dent pre-commit update workflow 2024-03-31 08:32:18 +02:00
Matthias 9726c4ae21 don't use cum profit 2024-03-30 11:28:23 +01:00
Matthias 7895eeb3b6 Fix tests after modified output 2024-03-30 11:20:41 +01:00
Matthias 0a186eb8b7 Remove Cum Profit %
it's a missleading metric in any case where stake-amount is not 100% identical.
2024-03-30 11:18:56 +01:00
Matthias 79719bbe82 Add freqtrade-client programmatic use sample 2024-03-30 09:29:09 +01:00
Matthias f2335c5db9 Merge pull request #10015 from freqtrade/feat/clients
Add freqtrade-clients as individually installable dependency
2024-03-30 09:21:01 +01:00
Matthias e100a06785 Use regex to match logs in rest-client tests 2024-03-29 19:09:23 +01:00
Matthias d606d6d08a Use a path that works on windows, too. 2024-03-29 17:03:31 +01:00
Matthias 01f09ca5c8 Update command name and documentation 2024-03-29 16:38:13 +01:00
Matthias 8ad0375073 Update docstring 2024-03-29 16:16:34 +01:00
Matthias e8cea35f5e Have Pool parameters be kw only 2024-03-29 16:15:50 +01:00
Matthias 95fe0d5d82 Merge pull request #10016 from froggleston/feat/clients
Add support for requests pool size configuration
2024-03-29 16:09:56 +01:00
Matthias 411322357c Slightly enhance test cov 2024-03-29 15:58:33 +01:00
Matthias d047ac67a8 Remove unused import 2024-03-29 15:13:45 +01:00
Matthias e072522ed1 Improve tests 2024-03-29 13:18:11 +01:00
Matthias e971f043f4 Improve test cov 2024-03-29 13:12:01 +01:00
froggleston 66422ce9a9 Add support for requests pool size configuration 2024-03-29 11:52:00 +00:00
Matthias 0a0105c31e Re-add --cov args 2024-03-29 12:46:05 +01:00
Matthias a6ad36d08b Coverage for ft_client ... 2024-03-29 12:33:28 +01:00
Matthias 7eb91c74a8 CI - always install ft_client 2024-03-29 12:32:00 +01:00
Matthias 79b8363b7b Depend on freqtrade-client on install 2024-03-29 12:31:25 +01:00
Matthias ca9d792d2a License to link 2024-03-29 11:08:40 +01:00
Matthias fba028abd1 Fix version number 2024-03-29 10:41:48 +01:00
Matthias 3f226c5022 Also install ft-client in linux CI 2024-03-29 10:35:55 +01:00
Matthias afe3d3ebda Move Version check to the top 2024-03-29 10:26:44 +01:00
Matthias 01bb7706e4 Fix messed up version-align script 2024-03-29 10:26:18 +01:00
Matthias 772dbeaf7e Fix file permissions 2024-03-29 10:15:35 +01:00
Matthias 0c026f950b Ensure versions are aligned between freqtrade and freqtrade_client 2024-03-29 10:03:15 +01:00
Matthias f0e0957579 Don't set same parameters twice 2024-03-29 09:57:12 +01:00
Matthias 7e3e65ecd8 Add tests for rest client 2024-03-29 09:52:25 +01:00
Matthias bef15b5238 Add license for client file, exclude tests directory 2024-03-29 09:17:37 +01:00
Matthias 127f61ccb9 Improve odering of things in ft-client 2024-03-29 09:08:21 +01:00
Matthias bd49ad6420 Add CI step for ft_rest_client 2024-03-29 09:07:43 +01:00
Matthias 46622f6172 Add minimal readme 2024-03-29 09:00:22 +01:00
Matthias 5888da5bc5 Re-add rest_client script 2024-03-29 08:39:28 +01:00
Matthias bfe739da3c Add pyproject.toml for ft_client installs 2024-03-29 08:34:00 +01:00
Matthias 1036a890b8 Split rest client script 2024-03-29 08:33:41 +01:00
Matthias 3401e3506b Move rest_client to it's own sub-structure 2024-03-29 08:09:03 +01:00
Matthias e75d46321e Exclude user_data 2024-03-29 07:47:27 +01:00
Matthias 26311663e3 Add classifier for python 3.12 2024-03-29 07:37:21 +01:00
Matthias bda17b59e7 Merge pull request #10013 from stevanStevic/feat/add-max-value-to-volume-pair-list
10009: Add `max_value` to volume pair list
2024-03-29 07:06:27 +01:00
stevanStevic 1ec04a8dc8 Add support for max_value in volume pair list 2024-03-28 22:20:34 +01:00
Matthias 78442e36e7 Fix small grammar error 2024-03-28 19:36:43 +01:00
Matthias 82565a9667 Merge pull request #9955 from Axel-CH/feature/trade-lifecycle-callbacks
Feature: trade lifecycle callbacks
2024-03-28 19:35:53 +01:00
Matthias 38e7b0e8ae Update bot basics logs to align with reality 2024-03-28 06:55:45 +01:00
Matthias e49ab2593c Update / improve docs 2024-03-28 06:49:02 +01:00
Matthias c433f49089 Remove temporary gate workaround 2024-03-27 19:59:30 +01:00
Matthias 553094c3c6 Assign 'status_stop' for gate stop orders 2024-03-27 19:50:52 +01:00
Matthias 06c4431c31 Merge pull request #10006 from freqtrade/update/pre-commit-hooks
Update pre-commit hooks
2024-03-26 05:56:21 +01:00
xmatthias 5b4d286637 chore: update pre-commit hooks 2024-03-26 03:03:29 +00:00
Matthias bcc766a8ff Merge pull request #10004 from freqtrade/docs/identifier
Update freqai-configuration.md
2024-03-25 21:01:48 +01:00
Matthias 666f2fc10a Fix bug where amount becomes 0
closes #10003
2024-03-25 20:59:39 +01:00
Matthias 50cd36acbd Remove unused test method 2024-03-25 20:59:39 +01:00
Robert Caulk dfd46a19b5 Update freqai-configuration.md 2024-03-25 19:56:43 +01:00
Matthias 41d508867e Merge pull request #10001 from freqtrade/fix/epoch-definition
Update PyTorchModelTrainer.py
2024-03-25 19:50:02 +01:00
Axel-CH f60d6c8f65 remove duplicate call of order_filled callback 2024-03-25 13:12:13 -04:00
Robert Caulk 18e34632d8 Update PyTorchModelTrainer.py
The n_epochs should be defined using the `max` not the `min` function.
2024-03-25 09:21:32 +01:00
Matthias 526d7fad62 Merge pull request #10000 from freqtrade/dependabot/pip/develop/sqlalchemy-2.0.29
Bump sqlalchemy from 2.0.28 to 2.0.29
2024-03-25 08:15:07 +01:00
Matthias e90a68cc1a Merge pull request #9993 from freqtrade/dependabot/pip/develop/pytest-89b78a57f6
Bump the pytest group with 2 updates
2024-03-25 07:28:06 +01:00
Matthias b81735e718 Merge pull request #9999 from freqtrade/dependabot/pip/develop/ccxt-4.2.82
Bump ccxt from 4.2.78 to 4.2.82
2024-03-25 07:15:16 +01:00
Matthias f1f272b88f Bump pre-commit sqlalchemy 2024-03-25 06:43:54 +01:00
Matthias 90ae723a74 Merge pull request #9998 from freqtrade/dependabot/pip/develop/mkdocs-material-9.5.15
Bump mkdocs-material from 9.5.14 to 9.5.15
2024-03-25 06:37:00 +01:00
dependabot[bot] 392598086c Bump the pytest group with 2 updates
Bumps the pytest group with 2 updates: [pytest-cov](https://github.com/pytest-dev/pytest-cov) and [pytest-mock](https://github.com/pytest-dev/pytest-mock).


Updates `pytest-cov` from 4.1.0 to 5.0.0
- [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest-cov/compare/v4.1.0...v5.0.0)

Updates `pytest-mock` from 3.12.0 to 3.14.0
- [Release notes](https://github.com/pytest-dev/pytest-mock/releases)
- [Changelog](https://github.com/pytest-dev/pytest-mock/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest-mock/compare/v3.12.0...v3.14.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-25 05:31:16 +00:00
Matthias 691b7d2628 Merge pull request #9994 from freqtrade/dependabot/pip/develop/ruff-0.3.4
Bump ruff from 0.3.3 to 0.3.4
2024-03-25 06:29:52 +01:00
Matthias cdb2a3aa90 Merge pull request #9995 from freqtrade/dependabot/pip/develop/nbconvert-7.16.3
Bump nbconvert from 7.16.2 to 7.16.3
2024-03-25 06:29:29 +01:00
Matthias 0f24b8d132 Merge pull request #9996 from freqtrade/dependabot/pip/develop/pre-commit-3.7.0
Bump pre-commit from 3.6.2 to 3.7.0
2024-03-25 06:29:13 +01:00
Matthias e8a18c0524 Merge pull request #9997 from freqtrade/dependabot/pip/develop/time-machine-2.14.1
Bump time-machine from 2.14.0 to 2.14.1
2024-03-25 06:28:47 +01:00
dependabot[bot] 16083b2f63 Bump sqlalchemy from 2.0.28 to 2.0.29
Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 2.0.28 to 2.0.29.
- [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-03-25 03:15:36 +00:00
dependabot[bot] bbeba6ab3a Bump ccxt from 4.2.78 to 4.2.82
Bumps [ccxt](https://github.com/ccxt/ccxt) from 4.2.78 to 4.2.82.
- [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.2.78...4.2.82)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-25 03:14:57 +00:00
dependabot[bot] 710a953c43 Bump mkdocs-material from 9.5.14 to 9.5.15
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.14 to 9.5.15.
- [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.14...9.5.15)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-25 03:14:41 +00:00
dependabot[bot] 9d5cb103dd Bump time-machine from 2.14.0 to 2.14.1
Bumps [time-machine](https://github.com/adamchainz/time-machine) from 2.14.0 to 2.14.1.
- [Changelog](https://github.com/adamchainz/time-machine/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/adamchainz/time-machine/compare/2.14.0...2.14.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-25 03:14:28 +00:00
dependabot[bot] 674ec6acb2 Bump pre-commit from 3.6.2 to 3.7.0
Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 3.6.2 to 3.7.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.6.2...v3.7.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-03-25 03:14:12 +00:00
dependabot[bot] 6e6a93f0ce Bump nbconvert from 7.16.2 to 7.16.3
Bumps [nbconvert](https://github.com/jupyter/nbconvert) from 7.16.2 to 7.16.3.
- [Release notes](https://github.com/jupyter/nbconvert/releases)
- [Changelog](https://github.com/jupyter/nbconvert/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jupyter/nbconvert/compare/v7.16.2...v7.16.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-25 03:14:00 +00:00
dependabot[bot] 74641c5659 Bump ruff from 0.3.3 to 0.3.4
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.3.3 to 0.3.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/v0.3.3...v0.3.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-03-25 03:13:52 +00:00
Matthias ea634e5cef Add test for backtesting 2024-03-24 11:54:25 +01:00
Matthias f6a8cb4698 update docs (fix typos) 2024-03-24 11:44:55 +01:00
Matthias 2854186b14 Adjust test to work against usdt values 2024-03-24 11:28:54 +01:00
Matthias 093fd48cf9 Update test to use default_conf_usdt 2024-03-24 11:28:24 +01:00
Matthias 7f5c38fd5a Update environment variable config to clarify that config cannot be set via environment variable
closes #9991
2024-03-23 08:08:55 +01:00
Matthias 82a9bdca5e Merge pull request #9986 from freqtrade/feat/show_config
add show-config command
2024-03-22 06:39:04 +01:00
Axel-CH b2a6722687 remove order filled cb from handle_stoploss_on_exchange 2024-03-21 21:51:52 -04:00
Axel-CH e07f3d266e add test for order_filled callback sl on exchange filled case 2024-03-21 20:53:11 -04:00
Axel-CH 6d3e3b5bfa add test for order_filled for freqtradebot update_trade_state 2024-03-21 20:19:53 -04:00
Axel-CH 72225daa6a add order_filled callback call for stoploss orders 2024-03-21 19:36:58 -04:00
Matthias 720232a047 Merge pull request #9983 from freqtrade/dependabot/pip/develop/pyarrow-15.0.2
Bump pyarrow from 15.0.1 to 15.0.2
2024-03-21 11:26:38 +01:00
Matthias 6e88cbfcab Update prebuilt pyarrow wheels 2024-03-21 09:25:22 +01:00
Matthias 7d6d3d38f7 Merge pull request #9975 from freqtrade/feat/configError
exception ConfigurationError
2024-03-21 08:38:45 +01:00
Matthias 163d03ac62 Update CI schedule to run towards the middle of the night. 2024-03-21 07:25:08 +01:00
Matthias b4c9541fb8 Add hints about show-config to configuration docs 2024-03-21 07:24:17 +01:00
Matthias d0bda7c2ce Add documentation for show-config utility command 2024-03-21 07:19:02 +01:00
Matthias d2a6781379 Revert one error to OperationalException 2024-03-21 07:02:23 +01:00
Matthias cc7d341afc Add overrides for Exchange Support 2024-03-21 06:31:34 +01:00
Matthias 3ea1cccda9 Merge pull request #9981 from freqtrade/dependabot/pip/develop/pytest-50b44610e0
Bump the pytest group with 1 update
2024-03-21 06:31:27 +01:00
Matthias 94c0f41564 Merge pull request #9982 from freqtrade/dependabot/pip/develop/uvicorn-0.29.0
Bump uvicorn from 0.28.0 to 0.29.0
2024-03-21 06:02:32 +01:00
Matthias b4ad6122cf Merge pull request #9984 from freqtrade/dependabot/pip/develop/ccxt-4.2.78
Bump ccxt from 4.2.67 to 4.2.78
2024-03-21 06:02:15 +01:00
dependabot[bot] 0d9775918f Bump ccxt from 4.2.67 to 4.2.78
Bumps [ccxt](https://github.com/ccxt/ccxt) from 4.2.67 to 4.2.78.
- [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.2.67...4.2.78)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-20 19:49:46 +00:00
dependabot[bot] 150b7f85de Bump pyarrow from 15.0.1 to 15.0.2
Bumps [pyarrow](https://github.com/apache/arrow) from 15.0.1 to 15.0.2.
- [Commits](https://github.com/apache/arrow/compare/go/v15.0.1...go/v15.0.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-20 19:49:28 +00:00
dependabot[bot] 40898f522a Bump uvicorn from 0.28.0 to 0.29.0
Bumps [uvicorn](https://github.com/encode/uvicorn) from 0.28.0 to 0.29.0.
- [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.28.0...0.29.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-20 19:49:20 +00:00
dependabot[bot] d6ff8ebbc0 Bump the pytest group with 1 update
Bumps the pytest group with 1 update: [pytest-asyncio](https://github.com/pytest-dev/pytest-asyncio).


Updates `pytest-asyncio` from 0.23.5.post1 to 0.23.6
- [Release notes](https://github.com/pytest-dev/pytest-asyncio/releases)
- [Commits](https://github.com/pytest-dev/pytest-asyncio/compare/v0.23.5.post1...v0.23.6)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-20 19:49:12 +00:00
Matthias 65105f7768 Don't modify original config 2024-03-20 07:22:12 +01:00
Matthias 82ff4d5879 Add explicit test for config sanitization 2024-03-20 07:15:02 +01:00
Matthias b1fe5b6d8a Add "sensitive information" warning output 2024-03-20 07:12:17 +01:00
Matthias 6dc4fa9dbf Add test for show-sensitive 2024-03-20 07:07:00 +01:00
Matthias 81d1a662a2 Add --show-sensitive CLI option 2024-03-20 07:06:24 +01:00
Matthias 6b7935f1ae Add initial test ... 2024-03-19 20:23:51 +01:00
Matthias 38ae3ac03c Add "sanitize config" section 2024-03-19 19:30:35 +01:00
Matthias 3059d66ece use original config for printing 2024-03-19 19:19:26 +01:00
Matthias 7543b2681d Add show_config command 2024-03-19 19:10:30 +01:00
Matthias 8e5097d9a9 Update setup-tools-config for non-dry mode 2024-03-19 19:10:30 +01:00
Matthias 21dcb4a6a1 update setup_utils_configuration to not force-set dry-run mode 2024-03-19 19:10:30 +01:00
Matthias 35253f9c8d Add overrides for Exchange Support 2024-03-19 18:19:22 +01:00
Matthias 6219a25901 Add documentation link, re-align imports 2024-03-19 07:20:38 +01:00
Matthias 669076a29f Add test to ensure configuration error is displayed as expected 2024-03-19 07:15:14 +01:00
Matthias abd096dcd8 Update exchange tests to config error in a few places 2024-03-19 07:13:28 +01:00
Matthias 4a097bd644 More usages for ConfigurationError 2024-03-19 07:13:17 +01:00
Matthias 64019e0e6c More configError usages 2024-03-19 07:08:05 +01:00
Matthias 3621ba034b Use configError in more places 2024-03-19 07:06:43 +01:00
Matthias 17d052df3f Configerror handling for bt api endpoint 2024-03-19 07:04:28 +01:00
Matthias ebd516cadb Use combined exception handler at startup 2024-03-19 07:02:48 +01:00
Matthias b5548dbee0 Add proper handler for config-error 2024-03-19 06:58:29 +01:00
Matthias 3a64749678 Raise ConfigurationError on config validation 2024-03-19 06:41:23 +01:00
Matthias ca94bbe994 Add configuration error to Exception hierarchy 2024-03-19 06:39:34 +01:00
Matthias 85c150b68e Merge pull request #9973 from freqtrade/update/pre-commit-hooks
Update pre-commit hooks
2024-03-19 06:24:59 +01:00
xmatthias 02570e285d chore: update pre-commit hooks 2024-03-19 03:03:31 +00:00
Axel-CH 996fcb6f56 fix current_time parameter of order_filled usage in backtest 2024-03-18 22:07:43 -04:00
Axel-CH 6941953a8b update doc details about order_filled callback details 2024-03-18 21:38:58 -04:00
Axel-CH 08c1866cdc fix mypy artype error 2024-03-18 20:57:47 -04:00
Axel-CH d1e1b8410b add order parameter to order_filled callback 2024-03-18 20:47:27 -04:00
Axel-CH cd986ced45 moove order_filled callback position from update_trade_state to _update_trade_after_fill 2024-03-18 17:56:46 -04:00
Axel-CH 18a3489a6f add order_filled callback to bot and backtest 2024-03-18 15:46:47 -04:00
Matthias a0a4230339 Merge pull request #9962 from freqtrade/dependabot/pip/develop/types-a1a02855fc
Bump the types group with 1 update
2024-03-18 11:56:13 +01:00
Matthias 592e86a6e3 Merge pull request #9966 from freqtrade/dependabot/pip/develop/plotly-5.20.0
Bump plotly from 5.19.0 to 5.20.0
2024-03-18 11:55:54 +01:00
Matthias 1dda952172 Merge pull request #9963 from freqtrade/dependabot/pip/develop/markdown-3.6
Bump markdown from 3.5.2 to 3.6
2024-03-18 11:55:32 +01:00
Matthias 6df04d7e3e Merge pull request #9967 from freqtrade/dependabot/pip/develop/ruff-0.3.3
Bump ruff from 0.3.2 to 0.3.3
2024-03-18 07:06:32 +01:00
Matthias e54ee0b4b3 Merge pull request #9968 from freqtrade/dependabot/pip/develop/pydantic-2.6.4
Bump pydantic from 2.6.3 to 2.6.4
2024-03-18 07:06:02 +01:00
Matthias dafc968602 Merge pull request #9941 from stash86/bt-metrics
Simplify the return value for full exit using adjust trade
2024-03-18 06:34:12 +01:00
Matthias d4ddcbc784 Bump types-dateutil in pre-commit config 2024-03-18 06:25:14 +01:00
dependabot[bot] 8395192e95 Bump markdown from 3.5.2 to 3.6
Bumps [markdown](https://github.com/Python-Markdown/markdown) from 3.5.2 to 3.6.
- [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.5.2...3.6)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-18 05:22:44 +00:00
Matthias 76ff0689f0 Merge pull request #9965 from freqtrade/dependabot/pip/develop/mkdocs-material-9.5.14
Bump mkdocs-material from 9.5.13 to 9.5.14
2024-03-18 06:22:02 +01:00
dependabot[bot] 28575a9ef3 Bump pydantic from 2.6.3 to 2.6.4
Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.6.3 to 2.6.4.
- [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.6.3...v2.6.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-18 03:29:20 +00:00
dependabot[bot] 80be86533e Bump ruff from 0.3.2 to 0.3.3
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.3.2 to 0.3.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/v0.3.2...v0.3.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-03-18 03:29:15 +00:00
dependabot[bot] 2725ee5772 Bump plotly from 5.19.0 to 5.20.0
Bumps [plotly](https://github.com/plotly/plotly.py) from 5.19.0 to 5.20.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.19.0...v5.20.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-03-18 03:29:04 +00:00
dependabot[bot] 405e0f02d4 Bump mkdocs-material from 9.5.13 to 9.5.14
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.13 to 9.5.14.
- [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.13...9.5.14)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-18 03:28:58 +00:00
dependabot[bot] 9bb9f7453c Bump the types group with 1 update
Bumps the types group with 1 update: [types-python-dateutil](https://github.com/python/typeshed).


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

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-18 03:27:55 +00:00
Matthias 409729f9d2 Temporarily support 'finish' in the order, too 2024-03-17 17:00:18 +01:00
Matthias 838d9cd4d0 Improve stop fills order handling on gate
closes #9940
2024-03-17 15:17:04 +01:00
Matthias 3ed53218a0 Fix rate display rounding problem on telegram messages 2024-03-17 13:55:25 +01:00
Matthias c06f892109 don't use :.8g formatting - it's unreliable 2024-03-17 13:55:25 +01:00
Matthias ca2d322eb8 chore: Split strip_trailing_zeros function in formatters 2024-03-17 13:55:25 +01:00
Matthias d07ec50549 Remove unused line 2024-03-17 13:55:25 +01:00
Matthias 600519c36a Merge pull request #9961 from hippocritical/develop
tiny bugfix for lookahead-analysis
2024-03-17 13:55:22 +01:00
hippocritical 01c0fd0420 If enable_protections are enabled, disable all
- some work on all pairs, and we don't check protections either so ... just disable them completely
- added info in the docs

Changed pairs-check to if no definition is in the config (but it s maybe in the strategy) it will just force-set it to the proper amount of len(config['pairs']
2024-03-17 09:53:45 +01:00
Matthias 9a72003c74 Merge pull request #9878 from TheJoeSchr/chore/extract-timeframe-to-xyz-from-exchange-utils
chore: extract `timeframe_to_*` functions into `exchange_utils_timeframe.py`
2024-03-15 20:01:43 +01:00
Matthias 45bb9ee9a0 Reduce stacktrace length for "no config" errors 2024-03-15 18:20:01 +01:00
Matthias 60a62d933c Fix some failing mocks 2024-03-15 07:12:07 +01:00
Matthias 4281642f54 Fix flake failure due to flake update 2024-03-15 06:51:11 +01:00
Matthias 5e0aa9108c Update tests correspondlingy 2024-03-15 06:49:49 +01:00
Matthias dbcb07275a Refactor datahandlers into their own package 2024-03-15 06:49:42 +01:00
Matthias 4aff24f6df Fix imports that import directly from file
instead from the exporting package
2024-03-15 06:40:42 +01:00
Matthias 3037cef4d2 Update freqtrade image to python 3.12 2024-03-14 19:48:23 +01:00
Matthias fcc6d0fea8 Don't run notify-complete for non-freqtrade repos 2024-03-14 18:24:36 +01:00
Matthias c284da404e Fully test freqAI against python 3.12 2024-03-14 18:24:36 +01:00
Matthias 93a5f2906a Merge pull request #9945 from GeorgiKarapetrov/trivial/doc-nit
Nitpicking documentation:
2024-03-14 13:59:06 +01:00
Matthias 91e1b068e8 Merge pull request #9923 from freqtrade/bump/torch
Bump torch
2024-03-14 06:42:55 +01:00
Georgi D. Karapetrov ec23fec3e6 fix typo 2024-03-13 10:45:00 +02:00
Georgi D. Karapetrov e958552bcc Explain precision filter better 2024-03-13 10:43:24 +02:00
Matthias 85c145c777 Don't close the test trade ... 2024-03-13 07:07:42 +01:00
Matthias 28bdbbffca Patch torch logging on 3.12 2024-03-12 19:59:46 +01:00
Matthias e560f9963c Don't run Torch models on macos. 2024-03-12 19:57:56 +01:00
Matthias 4e02d31cdc Mock torch on mac ARM 2024-03-12 19:57:56 +01:00
Matthias 0cf46d8605 Revert "Remove conditional checks for python 3.12"
This reverts commit e5fb2eb2d1874d7652a280ff098e4f63d7b8f528.
2024-03-12 19:57:55 +01:00
Matthias 08cff9890f Remove conditional checks for python 3.12 2024-03-12 19:57:55 +01:00
Matthias 2b5b518ccf Bump rl requirements for 3.12 2024-03-12 19:57:55 +01:00
Georgi D. Karapetrov ce6dd466fa fix typo 2024-03-12 18:10:21 +02:00
Matthias f578453656 Merge pull request #9934 from freqtrade/dependabot/pip/develop/pyarrow-15.0.1
Bump pyarrow from 15.0.0 to 15.0.1
2024-03-12 13:34:53 +01:00
Matthias 72015686b6 Update pyarrow wheels to 15.0.1 2024-03-12 11:06:22 +01:00
Georgi D. Karapetrov a4541fda2f Nitpicking documentation:
- Clarify order_types.stoploss_on_exchange
- Detail available_capital ammendments
- Clarify BNB fee reduction when BNB runs out on Binance
- Explain PrecisionFilter
2024-03-12 11:50:00 +02:00
Stefano Ariestasia 14c27f2cbe fix test 2024-03-12 16:55:33 +09:00
Stefano Ariestasia c5201a6476 fix precommit 2024-03-12 16:31:53 +09:00
Stefano Ariestasia 8000f94295 changing the partial exit logic to be simpler and easier to understand 2024-03-12 16:28:40 +09:00
Matthias a68fbb7f0c Merge pull request #9943 from freqtrade/update/pre-commit-hooks
Update pre-commit hooks
2024-03-12 06:40:28 +01:00
xmatthias 3c34126e96 chore: update pre-commit hooks 2024-03-12 03:03:24 +00:00
Matthias c3e7569820 Merge pull request #9929 from freqtrade/dependabot/pip/develop/ruff-0.3.2
Bump ruff from 0.3.0 to 0.3.2
2024-03-11 19:16:52 +01:00
Matthias 7888f9265f Merge pull request #9942 from freqtrade/dependabot/pip/develop/ccxt-4.2.67
Bump ccxt from 4.2.66 to 4.2.67
2024-03-11 19:10:09 +01:00
dependabot[bot] e336d870a6 Bump ccxt from 4.2.66 to 4.2.67
Bumps [ccxt](https://github.com/ccxt/ccxt) from 4.2.66 to 4.2.67.
- [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.2.66...4.2.67)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-11 17:12:59 +00:00
Matthias 225ef6b8ca Update to latest ruff format 2024-03-11 17:50:47 +01:00
Matthias 76459f08aa Update contributing with new ruff syntax 2024-03-11 17:49:40 +01:00
Matthias 7dae93f6b5 Merge pull request #9932 from freqtrade/dependabot/pip/develop/sqlalchemy-2.0.28
Bump sqlalchemy from 2.0.27 to 2.0.28
2024-03-11 16:20:09 +01:00
Stefano Ariestasia 048cad04a8 remove unrelated old changes 2024-03-11 22:07:58 +09:00
Stefano Ariestasia bae4973da5 simplify full exit using adjust trade pos 2024-03-11 22:01:09 +09:00
Stefano Ariestasia 1d0c0d6ab7 Merge branch 'freqtrade:develop' into bt-metrics 2024-03-11 21:56:54 +09:00
Matthias 65a09b0d89 Merge pull request #9928 from freqtrade/dependabot/pip/develop/uvicorn-0.28.0
Bump uvicorn from 0.27.1 to 0.28.0
2024-03-11 13:24:12 +01:00
Matthias 6c81807b76 Bump sqlalchemy in pre-commit 2024-03-11 13:23:48 +01:00
dependabot[bot] b6fb1dc9e0 Bump sqlalchemy from 2.0.27 to 2.0.28
Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 2.0.27 to 2.0.28.
- [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-03-11 11:22:15 +00:00
dependabot[bot] 2a8c6a6d0e Bump uvicorn from 0.27.1 to 0.28.0
Bumps [uvicorn](https://github.com/encode/uvicorn) from 0.27.1 to 0.28.0.
- [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.27.1...0.28.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-11 11:21:40 +00:00
Matthias ca4fb98775 Merge pull request #9931 from freqtrade/dependabot/pip/develop/ccxt-4.2.66
Bump ccxt from 4.2.58 to 4.2.66
2024-03-11 12:21:05 +01:00
dependabot[bot] 8d22fbf39c Bump ruff from 0.3.0 to 0.3.2
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.3.0 to 0.3.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/v0.3.0...v0.3.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-03-11 10:12:12 +00:00
Matthias 1cc0207b9b Merge pull request #9930 from freqtrade/dependabot/pip/develop/mkdocs-material-9.5.13
Bump mkdocs-material from 9.5.12 to 9.5.13
2024-03-11 11:10:54 +01:00
Matthias 4f96d6c7f7 Merge pull request #9927 from freqtrade/dependabot/pip/develop/pytest-46239fcbf0
Bump the pytest group with 2 updates
2024-03-11 11:10:38 +01:00
Matthias 9c84d3549c Merge pull request #9926 from freqtrade/dependabot/pip/develop/types-6e9832e4ce
Bump the types group with 2 updates
2024-03-11 10:03:53 +01:00
Matthias f3e8dd896d Merge pull request #9939 from freqtrade/dependabot/github_actions/develop/pypa/gh-action-pypi-publish-1.8.14
Bump pypa/gh-action-pypi-publish from 1.8.12 to 1.8.14
2024-03-11 09:30:25 +01:00
Matthias 6a5869fa43 Merge pull request #9938 from freqtrade/dependabot/pip/develop/packaging-24.0
Bump packaging from 23.2 to 24.0
2024-03-11 09:07:41 +01:00
dependabot[bot] 018d10b346 Bump ccxt from 4.2.58 to 4.2.66
Bumps [ccxt](https://github.com/ccxt/ccxt) from 4.2.58 to 4.2.66.
- [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.2.58...4.2.66)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-11 07:33:37 +00:00
Matthias 5b188b5e1d Merge pull request #9937 from freqtrade/dependabot/pip/develop/python-telegram-bot-21.0.1
Bump python-telegram-bot from 20.8 to 21.0.1
2024-03-11 08:32:18 +01:00
dependabot[bot] 33556f3c2c Bump mkdocs-material from 9.5.12 to 9.5.13
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.12 to 9.5.13.
- [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.12...9.5.13)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-11 07:11:44 +00:00
Matthias e37d29b607 Merge pull request #9935 from freqtrade/dependabot/pip/develop/pymdown-extensions-10.7.1
Bump pymdown-extensions from 10.7 to 10.7.1
2024-03-11 08:10:27 +01:00
Matthias 7171dadc52 Merge pull request #9936 from freqtrade/dependabot/pip/develop/nbconvert-7.16.2
Bump nbconvert from 7.16.1 to 7.16.2
2024-03-11 08:10:10 +01:00
dependabot[bot] 1c91675c58 Bump the pytest group with 2 updates
Bumps the pytest group with 2 updates: [pytest](https://github.com/pytest-dev/pytest) and [pytest-asyncio](https://github.com/pytest-dev/pytest-asyncio).


Updates `pytest` from 8.1.0 to 8.1.1
- [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.1.0...8.1.1)

Updates `pytest-asyncio` from 0.23.5 to 0.23.5.post1
- [Release notes](https://github.com/pytest-dev/pytest-asyncio/releases)
- [Commits](https://github.com/pytest-dev/pytest-asyncio/compare/v0.23.5...v0.23.5.post1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-11 06:14:04 +00:00
Matthias 31b400406e Merge pull request #9933 from freqtrade/dependabot/pip/develop/mypy-1.9.0
Bump mypy from 1.8.0 to 1.9.0
2024-03-11 07:12:52 +01:00
Matthias c78480c494 Bump types in pre-commit file 2024-03-11 06:28:11 +01:00
dependabot[bot] 23d226d372 Bump pypa/gh-action-pypi-publish from 1.8.12 to 1.8.14
Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.8.12 to 1.8.14.
- [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases)
- [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/v1.8.12...v1.8.14)

---
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-03-11 03:46:55 +00:00
dependabot[bot] 9b44d1d8cb Bump packaging from 23.2 to 24.0
Bumps [packaging](https://github.com/pypa/packaging) from 23.2 to 24.0.
- [Release notes](https://github.com/pypa/packaging/releases)
- [Changelog](https://github.com/pypa/packaging/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pypa/packaging/compare/23.2...24.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-11 03:40:29 +00:00
dependabot[bot] ab6a5d75bc Bump python-telegram-bot from 20.8 to 21.0.1
Bumps [python-telegram-bot](https://github.com/python-telegram-bot/python-telegram-bot) from 20.8 to 21.0.1.
- [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/v20.8...v21.0.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-11 03:40:15 +00:00
dependabot[bot] e1fdb8dec9 Bump nbconvert from 7.16.1 to 7.16.2
Bumps [nbconvert](https://github.com/jupyter/nbconvert) from 7.16.1 to 7.16.2.
- [Release notes](https://github.com/jupyter/nbconvert/releases)
- [Changelog](https://github.com/jupyter/nbconvert/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jupyter/nbconvert/compare/v7.16.1...v7.16.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-11 03:40:06 +00:00
dependabot[bot] 6d2f454d8c Bump pymdown-extensions from 10.7 to 10.7.1
Bumps [pymdown-extensions](https://github.com/facelessuser/pymdown-extensions) from 10.7 to 10.7.1.
- [Release notes](https://github.com/facelessuser/pymdown-extensions/releases)
- [Commits](https://github.com/facelessuser/pymdown-extensions/compare/10.7...10.7.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-11 03:40:01 +00:00
dependabot[bot] 02dc895c41 Bump pyarrow from 15.0.0 to 15.0.1
Bumps [pyarrow](https://github.com/apache/arrow) from 15.0.0 to 15.0.1.
- [Commits](https://github.com/apache/arrow/compare/go/v15.0.0...go/v15.0.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-11 03:39:56 +00:00
dependabot[bot] 80560a389c Bump mypy from 1.8.0 to 1.9.0
Bumps [mypy](https://github.com/python/mypy) from 1.8.0 to 1.9.0.
- [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md)
- [Commits](https://github.com/python/mypy/compare/v1.8.0...1.9.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-11 03:39:48 +00:00
dependabot[bot] 60b9d9448a 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.31.0.20240218 to 2.31.0.20240311
- [Commits](https://github.com/python/typeshed/commits)

Updates `types-python-dateutil` from 2.8.19.20240106 to 2.8.19.20240311
- [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-03-11 03:37:19 +00:00
Matthias 518b6eb565 use dt_ts to simplify exchange date math 2024-03-10 19:31:43 +01:00
Matthias 1010ce96fa Merge pull request #9922 from xmatthias/ci/native_m1
Ci - Run on native macos ARM runner
2024-03-10 13:08:03 +01:00
Matthias 204849aa85 Merge pull request #9919 from freqtrade/bump/catboost
Bump catboost to 1.2.3
2024-03-10 11:45:13 +01:00
Matthias 4e94178169 exclude python 3.9 on Macos 14 2024-03-09 20:10:40 +01:00
Matthias 0bd50a6e24 Don't disable tensorboard on mac ARM 2024-03-09 19:45:11 +01:00
Matthias cb1f49e81c Don't run torch models on 3.12 yet 2024-03-09 19:25:17 +01:00
Matthias edc74ae2e4 Split macos Installation into 2 separate actions 2024-03-09 18:23:35 +01:00
Matthias c5f2a69d9c Allow running catboost tests on 3.12 2024-03-09 18:16:22 +01:00
Matthias 971a81e15d Bump catboost to 1.2.3, remove 3.12 restriction 2024-03-09 18:15:47 +01:00
Matthias cc3b2000eb Avoid fully patching torch on M1 macs 2024-03-09 17:55:22 +01:00
Matthias 86db888386 Install libomp from brew for macos
closes #9874
2024-03-09 17:41:10 +01:00
Matthias 7cceddb3df Improve wording on /health message 2024-03-09 17:35:29 +01:00
Matthias 29f90cbd04 Run CI on macos-14 (M1) 2024-03-09 17:34:06 +01:00
Matthias 71b4e79874 Merge pull request #9914 from cuinix/develop
fix some typos in docs
2024-03-09 16:12:35 +01:00
Matthias 1b608a162e Add type-hint for result dictionary 2024-03-09 16:10:33 +01:00
Matthias f8cbf138ee Add Initial bot start and current bot start to /health telegram msg 2024-03-09 15:47:01 +01:00
Matthias acbb485302 Add bot start and bot-startup to health endpoint 2024-03-09 15:01:13 +01:00
Matthias 98c2f81bb9 Merge pull request #9917 from syco/develop
update status table to show total amounts in stake currency
2024-03-08 07:12:10 +01:00
Matthias 2cfe993951 Fix condition for min-stake in position-adjust mode
closes #9915
2024-03-08 07:10:47 +01:00
Matthias 6f0f4f06ef Merge pull request #6908 from eSeR1805/feature_keyval_storage
Persistent storage of user-custom information
2024-03-08 07:00:17 +01:00
Matthias b690325f22 Remove typo in change-dir notebook
closes #9916
2024-03-08 06:39:39 +01:00
Alberto 865ebc3143 update status table to show total amounts in stake currency
Signed-off-by: Alberto <alberto.rinaudo+seiza@gmail.com>
2024-03-07 17:05:13 +00:00
cuinix 3e6e534e76 fix some typos in docs
Signed-off-by: cuinix <915115094@qq.com>
2024-03-07 13:57:25 +08:00
Matthias cab38fb8c3 Merge pull request #9911 from freqtrade/update/pre-commit-hooks
Update pre-commit hooks
2024-03-05 06:29:28 +01:00
xmatthias 510863f939 chore: update pre-commit hooks 2024-03-05 03:03:38 +00:00
Matthias bcd631d3a8 Merge pull request #9903 from freqtrade/dependabot/pip/develop/pydantic-2.6.3
Bump pydantic from 2.6.2 to 2.6.3
2024-03-04 09:55:33 +01:00
dependabot[bot] 99c8be4c30 Bump pydantic from 2.6.2 to 2.6.3
Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.6.2 to 2.6.3.
- [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.6.2...v2.6.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-04 07:59:10 +00:00
Matthias 1b92f2522c Merge pull request #9907 from freqtrade/dependabot/pip/develop/ccxt-4.2.58
Bump ccxt from 4.2.51 to 4.2.58
2024-03-04 08:57:13 +01:00
Matthias 00e77c7f3f Merge pull request #9908 from freqtrade/dependabot/pip/develop/ruff-0.3.0
Bump ruff from 0.2.2 to 0.3.0
2024-03-04 08:55:44 +01:00
Matthias 1680728acb Merge pull request #9906 from freqtrade/dependabot/pip/develop/python-rapidjson-1.16
Bump python-rapidjson from 1.14 to 1.16
2024-03-04 08:54:42 +01:00
dependabot[bot] 9ad60643f5 Bump ruff from 0.2.2 to 0.3.0
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.2.2 to 0.3.0.
- [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/v0.2.2...v0.3.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-04 05:46:23 +00:00
Matthias 2ac7500cbf Merge pull request #9905 from freqtrade/dependabot/pip/develop/cachetools-5.3.3
Bump cachetools from 5.3.2 to 5.3.3
2024-03-04 06:45:21 +01:00
Matthias a2f7d93d13 Merge pull request #9900 from freqtrade/dependabot/pip/develop/time-machine-2.14.0
Bump time-machine from 2.13.0 to 2.14.0
2024-03-04 06:44:57 +01:00
Matthias 6e052d9e0c Merge pull request #9901 from freqtrade/dependabot/pip/develop/mkdocs-material-9.5.12
Bump mkdocs-material from 9.5.11 to 9.5.12
2024-03-04 06:44:33 +01:00
Matthias a4aa20c1f2 Merge pull request #9902 from freqtrade/dependabot/pip/develop/rich-13.7.1
Bump rich from 13.7.0 to 13.7.1
2024-03-04 06:44:13 +01:00
Matthias ac6f7dca1f Merge pull request #9904 from freqtrade/dependabot/pip/develop/python-dateutil-2.9.0.post0
Bump python-dateutil from 2.8.2 to 2.9.0.post0
2024-03-04 06:43:55 +01:00
Matthias 91ff6df512 Merge pull request #9899 from freqtrade/dependabot/pip/develop/pytest-606dc6de2a
Bump the pytest group with 1 update
2024-03-04 06:43:35 +01:00
Matthias 154064602e Merge pull request #9898 from freqtrade/dependabot/github_actions/develop/pypa/gh-action-pypi-publish-1.8.12
Bump pypa/gh-action-pypi-publish from 1.8.11 to 1.8.12
2024-03-04 06:43:07 +01:00
Matthias 426bc4c97b Merge pull request #9897 from freqtrade/fix/xgboosttensorboard
fix: try plotting as much info in xgboost tensorboard as possible
2024-03-04 06:38:22 +01:00
dependabot[bot] 8c0ba2a69a Bump ccxt from 4.2.51 to 4.2.58
Bumps [ccxt](https://github.com/ccxt/ccxt) from 4.2.51 to 4.2.58.
- [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.2.51...4.2.58)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-04 03:23:27 +00:00
dependabot[bot] d6ae63ac48 Bump python-rapidjson from 1.14 to 1.16
Bumps [python-rapidjson](https://github.com/python-rapidjson/python-rapidjson) from 1.14 to 1.16.
- [Changelog](https://github.com/python-rapidjson/python-rapidjson/blob/master/CHANGES.rst)
- [Commits](https://github.com/python-rapidjson/python-rapidjson/compare/v1.14...v1.16)

---
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-03-04 03:23:16 +00:00
dependabot[bot] ec17b5523c Bump cachetools from 5.3.2 to 5.3.3
Bumps [cachetools](https://github.com/tkem/cachetools) from 5.3.2 to 5.3.3.
- [Changelog](https://github.com/tkem/cachetools/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/tkem/cachetools/compare/v5.3.2...v5.3.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-04 03:23:10 +00:00
dependabot[bot] b8c16fb889 Bump python-dateutil from 2.8.2 to 2.9.0.post0
Bumps [python-dateutil](https://github.com/dateutil/dateutil) from 2.8.2 to 2.9.0.post0.
- [Release notes](https://github.com/dateutil/dateutil/releases)
- [Changelog](https://github.com/dateutil/dateutil/blob/master/NEWS)
- [Commits](https://github.com/dateutil/dateutil/compare/2.8.2...2.9.0.post0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-04 03:23:02 +00:00
dependabot[bot] c3f9b16c84 Bump rich from 13.7.0 to 13.7.1
Bumps [rich](https://github.com/Textualize/rich) from 13.7.0 to 13.7.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.7.0...v13.7.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-03-04 03:22:46 +00:00
dependabot[bot] 062376f573 Bump mkdocs-material from 9.5.11 to 9.5.12
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.11 to 9.5.12.
- [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.11...9.5.12)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-04 03:22:41 +00:00
dependabot[bot] 21709204eb Bump time-machine from 2.13.0 to 2.14.0
Bumps [time-machine](https://github.com/adamchainz/time-machine) from 2.13.0 to 2.14.0.
- [Changelog](https://github.com/adamchainz/time-machine/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/adamchainz/time-machine/compare/2.13.0...2.14.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-03-04 03:22:37 +00:00
dependabot[bot] 7ca3032d51 Bump the pytest group with 1 update
Bumps the pytest group with 1 update: [pytest](https://github.com/pytest-dev/pytest).


Updates `pytest` from 8.0.2 to 8.1.0
- [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.0.2...8.1.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-04 03:22:32 +00:00
dependabot[bot] 07bf19a990 Bump pypa/gh-action-pypi-publish from 1.8.11 to 1.8.12
Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.8.11 to 1.8.12.
- [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases)
- [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/v1.8.11...v1.8.12)

---
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-03-04 03:17:13 +00:00
robcaulk a948796ef7 fix: manually add train eval since xgboost does not expose this information by default 2024-03-03 15:47:19 +01:00
Matthias 255ea88638 Add to telegram documentation 2024-03-03 15:24:26 +01:00
Matthias 6bdb651573 Merge pull request #9890 from freqtrade/feat/dl-trades_futures
Support for --dl-trades for futures
2024-03-03 15:00:02 +01:00
Matthias ed8469f23a use trading_mode to determine trades file location 2024-03-03 13:12:42 +01:00
Matthias 265a7123da Add explicit test for telegram functionality of list-custom-data 2024-03-03 13:10:50 +01:00
Matthias ceb461a252 Switch sequence of information documentation 2024-03-03 12:58:10 +01:00
Matthias c1ae110080 Improve documentation 2024-03-03 12:56:42 +01:00
Matthias 30b4f27152 Cleanup some nitpicks 2024-03-03 12:50:28 +01:00
Matthias 1176c16b93 REmove unnecessary assignment 2024-03-03 12:41:51 +01:00
robcaulk 093a093bd5 fix: use data metric_name directly 2024-03-03 12:38:51 +01:00
robcaulk b8a1089592 fix: try plotting as much info in xgboost tensorboard as possible 2024-03-03 12:23:18 +01:00
Matthias fcb16098d8 Reduce Error level when converting trades 2024-03-02 15:40:43 +01:00
Matthias 7ed7ed4081 Accept trading-mode for trades-to-ohlcv command 2024-03-02 15:38:36 +01:00
Matthias 99da6f70c2 Fix failing test due to new approach for convert-trades 2024-03-02 13:27:57 +01:00
Matthias 09d763b604 convert-trades should do proper pair expansion to support regex in pairlists 2024-03-02 13:23:03 +01:00
Matthias 5e7868a28d Remove block from download-trades for futures 2024-03-02 13:22:41 +01:00
Matthias 66e43f2fe8 Adjust tests for new arguments 2024-03-02 13:17:45 +01:00
Matthias 43103f51e5 Update functions that use datahandler trade methods 2024-03-02 13:10:57 +01:00
Matthias b6040e270f Update data handlers to accept trading_mode
for trade data related functions
2024-03-02 13:08:58 +01:00
Matthias f8cc2a6e74 Fix typo in doc header 2024-03-02 12:01:01 +01:00
Matthias 75c84bfe65 Only show a subset of list-exchanges output in the docs
(it's potentially missleading, and changes all the time - so was probably outdated).
2024-03-02 08:23:38 +01:00
Matthias bdd63aa1d6 FIx futures trades pair download directory 2024-03-01 20:17:43 +01:00
Matthias 5dee60921f Fix test for convert_trades_to_ohlcv 2024-03-01 19:42:33 +01:00
Matthias 46e616f997 Remove defaults for converter - they're always provided and necessary. 2024-03-01 19:32:35 +01:00
Matthias 82f191f7b0 Merge pull request #9861 from freqtrade/feat/sort_volatility
Add sorting to volatility and RangeStability pairlists
2024-03-01 06:52:34 +01:00
Matthias cdfeae9f90 Update merge to "left" to avoid creating non-existing mark candles 2024-02-29 07:31:22 +01:00
Matthias e988995d71 Handle NaN funding fees
closes #9831
2024-02-29 07:22:47 +01:00
Matthias 1fc69ef114 Merge pull request #9876 from freqtrade/new_release
New release 2024.2
2024-02-29 06:38:44 +01:00
Matthias 2384ba74a3 Merge branch 'develop' into feat/sort_volatility 2024-02-27 20:50:41 +01:00
Matthias d1028b8ca2 Merge pull request #9871 from freqtrade/dependabot/pip/develop/pandas-2.2.1
Bump pandas from 2.1.4 to 2.2.1
2024-02-27 20:47:31 +01:00
Joe Schr b55105ec82 chore: adjust timeframe_to_* imports accordingly 2024-02-27 10:42:29 +01:00
Joe Schr 4a8c8f296a chore: extract timeframe_to_* functions into exchange_utils_timeframe.py 2024-02-27 10:23:07 +01:00
Matthias 883f27d99e Version bump to 2024.3-dev 2024-02-27 07:01:16 +01:00
Matthias 0a8719c617 Bump Version to 2024.2 2024-02-27 06:54:21 +01:00
Matthias e1fdc0c79c Merge branch 'stable' into new_release 2024-02-27 06:54:07 +01:00
Matthias c0e9726f49 don't use "1M" - but be explicit in the intend 2024-02-27 06:20:08 +01:00
Matthias 0021e2c205 fillna needs explicit type. 2024-02-27 06:19:54 +01:00
Matthias ce2f4f89c4 update pandas deprecation to_hdf 2024-02-27 06:19:38 +01:00
Matthias 5912d87b65 Pandas - update view to astype 2024-02-27 06:19:27 +01:00
Matthias b1015172c7 Update test for fixed pandas behavior 2024-02-27 06:19:06 +01:00
dependabot[bot] bd7edfba97 Bump pandas from 2.1.4 to 2.2.1
Bumps [pandas](https://github.com/pandas-dev/pandas) from 2.1.4 to 2.2.1.
- [Release notes](https://github.com/pandas-dev/pandas/releases)
- [Commits](https://github.com/pandas-dev/pandas/compare/v2.1.4...v2.2.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-26 19:18:14 +01:00
Matthias d6b01a6ffe Assert for exact equality 2024-02-26 19:17:50 +01:00
Matthias 01266ed7eb Align test results ... 2024-02-26 19:02:11 +01:00
Matthias 5402d276d6 Add header to warning box, reorder Boxes 2024-02-26 18:03:39 +01:00
Matthias 9d2bfa6594 Merge pull request #9872 from freqtrade/dependabot/pip/develop/ccxt-4.2.51
Bump ccxt from 4.2.47 to 4.2.51
2024-02-26 13:26:22 +01:00
dependabot[bot] d5c01f7480 Bump ccxt from 4.2.47 to 4.2.51
Bumps [ccxt](https://github.com/ccxt/ccxt) from 4.2.47 to 4.2.51.
- [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.2.47...4.2.51)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-26 07:47:19 +00:00
Matthias e61a4ffc98 Merge pull request #9869 from freqtrade/dependabot/pip/develop/fastapi-0.110.0
Bump fastapi from 0.109.2 to 0.110.0
2024-02-26 08:46:03 +01:00
Matthias 1b8255a464 Merge pull request #9873 from freqtrade/dependabot/pip/develop/cryptography-42.0.5
Bump cryptography from 42.0.4 to 42.0.5
2024-02-26 08:45:14 +01:00
Matthias d9f4c62f15 Add warning about excessive use of position_adjustment 2024-02-26 07:07:48 +01:00
Matthias 540ba4c769 Merge pull request #9870 from freqtrade/dependabot/pip/develop/nbconvert-7.16.1
Bump nbconvert from 7.16.0 to 7.16.1
2024-02-26 06:43:27 +01:00
dependabot[bot] 080d9b55f8 Bump fastapi from 0.109.2 to 0.110.0
Bumps [fastapi](https://github.com/tiangolo/fastapi) from 0.109.2 to 0.110.0.
- [Release notes](https://github.com/tiangolo/fastapi/releases)
- [Commits](https://github.com/tiangolo/fastapi/compare/0.109.2...0.110.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-02-26 05:29:59 +00:00
Matthias 74bdb94d07 Merge pull request #9867 from freqtrade/dependabot/pip/develop/pydantic-2.6.2
Bump pydantic from 2.6.1 to 2.6.2
2024-02-26 06:28:52 +01:00
Matthias 5854c63d05 Merge pull request #9866 from freqtrade/dependabot/pip/develop/orjson-3.9.15
Bump orjson from 3.9.14 to 3.9.15
2024-02-26 06:28:27 +01:00
Matthias 3957506db0 Merge pull request #9868 from freqtrade/dependabot/pip/develop/mkdocs-material-9.5.11
Bump mkdocs-material from 9.5.9 to 9.5.11
2024-02-26 06:27:55 +01:00
Matthias 1ac8210313 Merge pull request #9865 from freqtrade/dependabot/pip/develop/pytest-1fcd73377f
Bump the pytest group with 1 update
2024-02-26 06:27:34 +01:00
dependabot[bot] 4ff888488b Bump cryptography from 42.0.4 to 42.0.5
Bumps [cryptography](https://github.com/pyca/cryptography) from 42.0.4 to 42.0.5.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/42.0.4...42.0.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-26 03:55:48 +00:00
dependabot[bot] e2a486f85e Bump nbconvert from 7.16.0 to 7.16.1
Bumps [nbconvert](https://github.com/jupyter/nbconvert) from 7.16.0 to 7.16.1.
- [Release notes](https://github.com/jupyter/nbconvert/releases)
- [Changelog](https://github.com/jupyter/nbconvert/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jupyter/nbconvert/compare/v7.16.0...v7.16.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-26 03:55:27 +00:00
dependabot[bot] 313de7b8a0 Bump mkdocs-material from 9.5.9 to 9.5.11
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.9 to 9.5.11.
- [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.9...9.5.11)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-26 03:55:12 +00:00
dependabot[bot] c2998f3d75 Bump pydantic from 2.6.1 to 2.6.2
Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.6.1 to 2.6.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.6.1...v2.6.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-02-26 03:55:06 +00:00
dependabot[bot] 9805cd768b Bump orjson from 3.9.14 to 3.9.15
Bumps [orjson](https://github.com/ijl/orjson) from 3.9.14 to 3.9.15.
- [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.9.14...3.9.15)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-26 03:54:59 +00:00
dependabot[bot] c06e4ee89c Bump the pytest group with 1 update
Bumps the pytest group with 1 update: [pytest](https://github.com/pytest-dev/pytest).


Updates `pytest` from 8.0.1 to 8.0.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.0.1...8.0.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-02-26 03:54:50 +00:00
Matthias d80ed7e33c Bump Docker Python version from 3.11.7 to 3.11.8 2024-02-25 15:17:42 +01:00
Matthias e2d3774b07 Clearer wallets variable/parameter wording 2024-02-25 09:07:53 +01:00
Matthias aad327b1fe Update comment around funding fees 2024-02-25 09:02:58 +01:00
Matthias 3e0755b8ad Enhance funding-fee call test to account for different timezones 2024-02-25 09:02:17 +01:00
Matthias f164b89351 Ensure pytz is updated regularily by pinning it 2024-02-25 08:57:05 +01:00
Matthias e80ad309f1 Improve type safety, refactor volatilityfilter 2024-02-24 15:04:54 +01:00
Matthias 817ad64402 Add docs for rangeStability sorting 2024-02-24 15:00:28 +01:00
Matthias 67152ad48a Improve and parametrize pairlist tests 2024-02-24 14:56:42 +01:00
Matthias e82d9e2f55 Test volatilityfilter with more pairs 2024-02-24 14:45:15 +01:00
Matthias b972ee78ec Enhance rangeStability test 2024-02-24 14:41:06 +01:00
Matthias 2704f6e758 Improve test 2024-02-24 14:05:25 +01:00
Matthias 9dd5967275 Add sorting capabilities to rangeStabilityFilter 2024-02-24 14:03:50 +01:00
Matthias 6a313aa9e3 Improve help wording 2024-02-24 14:03:26 +01:00
Matthias 81de29a1e3 Improve conditions for removal of pairs 2024-02-24 14:00:50 +01:00
Matthias 3677953d90 Properly cache volatility-average 2024-02-24 13:54:52 +01:00
Matthias 7af46628f8 Simplify rangeStability Filter 2024-02-24 13:50:54 +01:00
Matthias 88a2995b4c Fix wrong typehint 2024-02-24 13:39:46 +01:00
Matthias 866ff55d84 document sort_direction mode 2024-02-24 13:34:42 +01:00
Matthias 91ba4f6424 Add test for volatilityFilter sorting 2024-02-24 13:31:26 +01:00
Matthias 31e2543134 Enhance generate_test_data with parametrizable random seed 2024-02-24 13:30:42 +01:00
Matthias eaf70428c1 Improve volatility tests 2024-02-24 13:21:49 +01:00
Matthias 38ca58c728 Add verification for volatilityfilter 2024-02-24 13:12:52 +01:00
Matthias 0bf73cc64b Voliatilityfilter - sorting 2024-02-24 13:11:43 +01:00
Matthias 7ddaa09a23 Refactor VolatilityFilter 2024-02-24 08:26:07 +01:00
Matthias c663016b47 Improve some type safety 2024-02-24 08:06:06 +01:00
Matthias 6307e16304 Properly format notification date 2024-02-23 06:45:28 +01:00
Matthias 9ac7149c47 Add check to verify that "since" is properly respected. 2024-02-22 22:14:29 +01:00
Matthias 0acde28902 Remove pointless comment 2024-02-22 19:28:02 +01:00
Matthias f4ad1e8020 chore: Remove wrong typehint 2024-02-22 19:02:11 +01:00
Matthias c7fff45bef Fix test leakage 2024-02-22 19:01:33 +01:00
Matthias e30fa3a445 Remove duplicate pairlistmanager init 2024-02-22 18:17:58 +01:00
Matthias db83b0cdb0 Update typehint for candle_cache 2024-02-22 07:09:47 +01:00
Matthias 4cfd5d004e Improve ohlcv pair cache with since_ms
avoids some rare bugs in more complex configurations.
2024-02-22 06:38:28 +01:00
Matthias 3680e199ce Fix range-stability filter downloading too little data 2024-02-22 06:29:08 +01:00
Matthias 4e87169a0c Use set to reduce iterations 2024-02-22 06:28:54 +01:00
Matthias 621e497e7b Merge pull request #9854 from freqtrade/dependabot/pip/cryptography-42.0.4
Bump cryptography from 42.0.3 to 42.0.4
2024-02-22 06:28:08 +01:00
dependabot[bot] c013c76e64 Bump cryptography from 42.0.3 to 42.0.4
Bumps [cryptography](https://github.com/pyca/cryptography) from 42.0.3 to 42.0.4.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/42.0.3...42.0.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-21 20:49:18 +00:00
Matthias 4bbb3174b2 re-enable use_database after bt test 2024-02-21 06:52:22 +01:00
Matthias c511d65d2e Add backtesting test 2024-02-20 20:25:12 +01:00
Matthias e8ca9ce39b Add testconfirming correct functioning 2024-02-20 20:00:45 +01:00
Matthias f1af00dd39 Merge branch 'develop' into feature_keyval_storage 2024-02-20 19:21:44 +01:00
Matthias bfb0b535b7 Merge pull request #9847 from freqtrade/fix/bybit_unified
Bump ccxt to 4.2.47
2024-02-20 08:26:55 +01:00
Matthias 0199e7d3d8 Add type-hint to exchange_has dict 2024-02-20 06:30:10 +01:00
Matthias b3ba2cee17 Bump ccxt to 4.2.47 2024-02-20 06:21:09 +01:00
Matthias 411f606476 Fix some tests due to new method 2024-02-20 06:21:03 +01:00
Matthias f53c019d2a Update "exchange_has" validation with new fallbacks 2024-02-20 06:21:03 +01:00
Matthias 3497f7946e Add test for fetch_order_emulated 2024-02-20 06:21:03 +01:00
Matthias 65af7750e6 Add fetch_order_emulated to support exchanges without proper fetch_order method 2024-02-20 06:21:03 +01:00
Matthias 103c993591 Merge pull request #9843 from freqtrade/dependabot/pip/develop/tensorboard-2.16.2
Bump tensorboard from 2.15.2 to 2.16.2
2024-02-20 06:11:11 +01:00
Matthias 969128a74f Merge pull request #9846 from freqtrade/update/pre-commit-hooks
Update pre-commit hooks
2024-02-20 06:10:45 +01:00
xmatthias f6e2030bf2 chore: update pre-commit hooks 2024-02-20 03:03:46 +00:00
Matthias 00bde70f73 Fix / improve styling in test class 2024-02-19 19:14:44 +01:00
Matthias 61e2170fc1 Merge pull request #9832 from freqtrade/dependabot/pip/develop/types-8a8455b0ec
Bump the types group with 1 update
2024-02-19 11:52:44 +01:00
Matthias 434b8a423c bump types-requests pre-commit 2024-02-19 09:33:09 +01:00
Matthias 0b149cbd6a Merge pull request #9840 from freqtrade/dependabot/pip/develop/ruff-0.2.2
Bump ruff from 0.2.1 to 0.2.2
2024-02-19 09:32:08 +01:00
Matthias 2323215aa7 Merge pull request #9838 from freqtrade/dependabot/pip/develop/sqlalchemy-2.0.27
Bump sqlalchemy from 2.0.26 to 2.0.27
2024-02-19 08:49:53 +01:00
dependabot[bot] 82876570a3 Bump ruff from 0.2.1 to 0.2.2
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.2.1 to 0.2.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/v0.2.1...v0.2.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-02-19 07:14:01 +00:00
Matthias c329e71fa8 Merge pull request #9836 from freqtrade/dependabot/pip/develop/pre-commit-3.6.2
Bump pre-commit from 3.6.1 to 3.6.2
2024-02-19 08:13:14 +01:00
Matthias a200b5524b Update sqlalchemy in pre-commit 2024-02-19 07:18:56 +01:00
Matthias baf6725a9a Merge pull request #9839 from freqtrade/dependabot/pip/develop/scikit-learn-1.4.1.post1
Bump scikit-learn from 1.4.0 to 1.4.1.post1
2024-02-19 07:18:07 +01:00
Matthias d655a0a638 Merge pull request #9842 from freqtrade/dependabot/pip/develop/orjson-3.9.14
Bump orjson from 3.9.13 to 3.9.14
2024-02-19 07:16:29 +01:00
Matthias 39941a7ac0 Improve formatting in drawdown calc 2024-02-19 07:09:23 +01:00
Matthias 66f4839101 Further increase test coverate of max_drawdown 2024-02-19 07:02:42 +01:00
dependabot[bot] 6aa1bbf574 Bump sqlalchemy from 2.0.26 to 2.0.27
Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 2.0.26 to 2.0.27.
- [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-02-19 05:31:00 +00:00
dependabot[bot] 381576b8f1 Bump pre-commit from 3.6.1 to 3.6.2
Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 3.6.1 to 3.6.2.
- [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.6.1...v3.6.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-19 05:30:18 +00:00
Matthias 82814cb5fa Merge pull request #9837 from freqtrade/dependabot/pip/develop/plotly-5.19.0
Bump plotly from 5.18.0 to 5.19.0
2024-02-19 06:30:11 +01:00
Matthias 093b44928e Merge pull request #9835 from freqtrade/dependabot/pip/develop/cryptography-42.0.3
Bump cryptography from 42.0.2 to 42.0.3
2024-02-19 06:29:42 +01:00
Matthias fa632311f3 Merge pull request #9834 from freqtrade/dependabot/pip/develop/urllib3-2.2.1
Bump urllib3 from 2.2.0 to 2.2.1
2024-02-19 06:28:35 +01:00
Matthias 5e86ee9de6 Merge pull request #9833 from freqtrade/dependabot/pip/develop/pytest-4c2ece7286
Bump the pytest group with 1 update
2024-02-19 06:28:12 +01:00
dependabot[bot] 549b9f62fd Bump tensorboard from 2.15.2 to 2.16.2
Bumps [tensorboard](https://github.com/tensorflow/tensorboard) from 2.15.2 to 2.16.2.
- [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.15.2...2.16.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-19 03:13:12 +00:00
dependabot[bot] edb5431a77 Bump orjson from 3.9.13 to 3.9.14
Bumps [orjson](https://github.com/ijl/orjson) from 3.9.13 to 3.9.14.
- [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.9.13...3.9.14)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-19 03:13:07 +00:00
dependabot[bot] 4241db2fe2 Bump scikit-learn from 1.4.0 to 1.4.1.post1
Bumps [scikit-learn](https://github.com/scikit-learn/scikit-learn) from 1.4.0 to 1.4.1.post1.
- [Release notes](https://github.com/scikit-learn/scikit-learn/releases)
- [Commits](https://github.com/scikit-learn/scikit-learn/compare/1.4.0...1.4.1.post1)

---
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-02-19 03:12:37 +00:00
dependabot[bot] c966f83147 Bump plotly from 5.18.0 to 5.19.0
Bumps [plotly](https://github.com/plotly/plotly.py) from 5.18.0 to 5.19.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.18.0...v5.19.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-02-19 03:12:17 +00:00
dependabot[bot] f361824b15 Bump cryptography from 42.0.2 to 42.0.3
Bumps [cryptography](https://github.com/pyca/cryptography) from 42.0.2 to 42.0.3.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/42.0.2...42.0.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-19 03:12:05 +00:00
dependabot[bot] 8675f86d14 Bump urllib3 from 2.2.0 to 2.2.1
Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.2.0 to 2.2.1.
- [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.0...2.2.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-19 03:11:57 +00:00
dependabot[bot] 0979d0b6e4 Bump the pytest group with 1 update
Bumps the pytest group with 1 update: [pytest](https://github.com/pytest-dev/pytest).


Updates `pytest` from 8.0.0 to 8.0.1
- [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.0.0...8.0.1)

---
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-02-19 03:11:53 +00:00
dependabot[bot] 4c3879cb57 Bump the types group with 1 update
Bumps the types group with 1 update: [types-requests](https://github.com/python/typeshed).


Updates `types-requests` from 2.31.0.20240125 to 2.31.0.20240218
- [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-02-19 03:11:21 +00:00
Matthias 69a0f4c465 Fix bybit spot live tests 2024-02-18 16:09:59 +01:00
Matthias 61e09ac719 Update telegram help with new wording 2024-02-18 16:07:03 +01:00
Matthias 583b2fc690 Fail if unified account is detected. 2024-02-18 11:50:42 +01:00
Matthias e06b70eb05 Add log message for Bybit accout type 2024-02-18 11:50:42 +01:00
Matthias 51a6a7b632 Merge pull request #9821 from freqtrade/feat/volumepairlist_caching
improve volumepairlist "advanced filter mode" caching
2024-02-18 11:46:31 +01:00
Matthias 3250f42257 Improve validate_exchange
returns now both required and optional dependencies
2024-02-18 11:21:34 +01:00
Matthias a029d1f08e Merge pull request #9823 from tar-xz/develop
docs: Remove unnecessary bracket in sql_cheatsheet.md
2024-02-18 10:54:55 +01:00
William Wong a5d1ae3191 docs: Update sql_cheatsheet.md 2024-02-18 03:21:50 +08:00
Matthias ebd439cdd1 Remove unused import 2024-02-17 16:41:10 +01:00
Matthias 78d8a4df2e Use "ohlcv_with_cache" for further pairlists 2024-02-17 16:29:52 +01:00
Matthias 4bcf2c423a Don't tick on ttl cache 2024-02-17 16:27:43 +01:00
Matthias 7b36a0fc42 Add explicit test for ohlcv_with_cache 2024-02-17 16:26:53 +01:00
Matthias bcfe7ef547 Refactor ohlcv caching to exchange class 2024-02-17 16:26:44 +01:00
Matthias 8033faa2f2 Update pairlist cache behavior in VolumePairList 2024-02-17 15:14:11 +01:00
Matthias 190ba272de Merge pull request #9818 from CaffeinatedTech/develop
Added escaping to enter and exit tags on telegram performance messages.
2024-02-17 13:36:52 +01:00
Matthias 3f3760c0ae Use Markdown V1 - update tests 2024-02-17 12:02:26 +01:00
CaffeinatedTech 4761bf2427 Change enter_tag, exit_reason, mix_tag performance messages from HTML to Markdown to fix some string encoding issues. 2024-02-17 09:12:49 +10:00
Matthias c0da1b6922 Fix edge-case when calculating cagr
edge-case with leveraged trades - yielding a negative final balance.
closes #9820
2024-02-16 20:04:49 +01:00
CaffeinatedTech a0b7df70d6 Added escaping to enter and exit tags on telegram performance messages. 2024-02-16 13:36:16 +10:00
Matthias 60b12c1d9e Double newlines between functions ... 2024-02-15 20:45:16 +01:00
Matthias fd48991fb0 Fix duplicate parentheses 2024-02-15 20:44:16 +01:00
Matthias 1696aa3915 Adjust tests for new fields 2024-02-15 20:39:06 +01:00
Matthias fb54c9ffe4 Add open_fill_date stuff to json schema 2024-02-15 20:35:34 +01:00
Matthias 0f85ef0997 Simplify trade_model serializations 2024-02-15 19:52:50 +01:00
Matthias c6d1c1a980 Add dt_ts_none helper 2024-02-15 19:50:56 +01:00
Matthias c1d7184849 Adjust for ccxt exception hierarchy change
caused by https://github.com/ccxt/ccxt/pull/21035
2024-02-15 19:20:08 +01:00
Matthias a22181d721 Enable caching for "filter only" Volumepairlist 2024-02-15 18:20:19 +01:00
Matthias 7f7e9ec875 Add additional test case for VolumePairlist in range mode 2024-02-15 18:20:19 +01:00
Matthias 6c9b9e91e8 enhance volumpairlist range test 2024-02-15 18:20:19 +01:00
Matthias d01e9cf299 Improve log message 2024-02-15 18:20:11 +01:00
Matthias 86da9cb659 Simplify some pairlist conditions 2024-02-15 18:20:11 +01:00
Matthias d2b0dc2171 Merge pull request #9814 from freqtrade/frog-docs-remotepairlist-1
Clarify processing_mode for RemotePairlist docs
2024-02-15 18:10:18 +01:00
Robert Davey 57fd0e379a Clarify processing_mode for RemotePairlist
No default value is specified in the docs for the processing_mode, making it unclear that the default behaviour is to filter out pairs, rather than append.
2024-02-15 15:57:49 +00:00
Matthias 280737447c Don't load empty dataframes - skip these
closes #9811
2024-02-14 07:26:23 +01:00
Matthias 3d6079ae19 Add debug output showing the pair to be converted
part of #9811
2024-02-13 17:47:43 +01:00
Matthias ab062d7bb1 Add test to run in backtest mode 2024-02-13 07:16:20 +01:00
Matthias d5b21f2a32 Fix bug in backtest mode 2024-02-13 07:16:09 +01:00
Matthias 9be7759e42 Add initial test for custom_data 2024-02-13 07:10:11 +01:00
Matthias 304f52ab79 Fix some minor custom-data bugs 2024-02-13 07:06:32 +01:00
Matthias 6a6e3aacf3 Fix broken deletion 2024-02-12 20:51:44 +01:00
Matthias 1c81a21bb6 Merge branch 'develop' into feature_keyval_storage 2024-02-12 20:39:37 +01:00
Matthias 67b910835e Simplify access to custom_data - users will usually only care about the value, not about the metadata. 2024-02-12 20:39:29 +01:00
Matthias 8364a704d6 Fix a few sql gotchas 2024-02-12 20:31:44 +01:00
Matthias 790c7e386a re-adjust logic for custom_data in rpc module 2024-02-12 20:27:56 +01:00
Matthias 8dda28351e Simplify custom_data stuff 2024-02-12 20:25:26 +01:00
Matthias b7904b8e80 Combine custom_data classes to one file 2024-02-12 20:14:37 +01:00
Matthias 7fd70b82fa custom_data: Simplify and fix a few things 2024-02-12 20:05:17 +01:00
Matthias 9699011cd9 Remove pointless wrapper 2024-02-12 18:28:29 +01:00
Matthias 83b22dedd5 Fix non-reset of use_db 2024-02-12 18:25:02 +01:00
Matthias c67e451fe1 Remove unused imports 2024-02-12 18:24:26 +01:00
Matthias 8593094619 Ensure custom data access goes through the accessor functions 2024-02-12 18:22:49 +01:00
Matthias d49da76382 Slighlty improve docs 2024-02-12 18:17:48 +01:00
Matthias 2c652a2ce6 Merge pull request #9803 from freqtrade/dependabot/pip/develop/sqlalchemy-2.0.26
Bump sqlalchemy from 2.0.25 to 2.0.26
2024-02-12 14:17:19 +01:00
Matthias c99009d47e Merge branch 'develop' into dependabot/pip/develop/sqlalchemy-2.0.26 2024-02-12 10:52:10 +01:00
Matthias 05ab13592b Merge pull request #9796 from freqtrade/dependabot/pip/develop/pytest-859c960bd7
Bump the pytest group with 2 updates
2024-02-12 08:19:57 +01:00
Matthias a02b1d0306 Merge pull request #9804 from freqtrade/dependabot/pip/develop/tensorboard-2.15.2
Bump tensorboard from 2.15.1 to 2.15.2
2024-02-12 08:17:47 +01:00
Matthias ffe43be3fa Merge pull request #9802 from freqtrade/dependabot/pip/develop/python-telegram-bot-20.8
Bump python-telegram-bot from 20.7 to 20.8
2024-02-12 08:17:22 +01:00
Matthias 94a52a776c Merge pull request #9801 from freqtrade/dependabot/pip/develop/mkdocs-material-9.5.9
Bump mkdocs-material from 9.5.8 to 9.5.9
2024-02-12 06:47:12 +01:00
Matthias f2a7312010 Update sqlalchemy pre-commit 2024-02-12 06:34:45 +01:00
Matthias 72290365db telegram: ensure msg update test uses the correct edit_message_text call 2024-02-12 06:31:39 +01:00
Matthias fd737af69d use query.edit_message_text to simplify telegram class 2024-02-12 06:29:42 +01:00
dependabot[bot] 0740a13393 Bump sqlalchemy from 2.0.25 to 2.0.26
Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 2.0.25 to 2.0.26.
- [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-02-12 05:18:09 +00:00
Matthias 1f3047dce4 Merge pull request #9800 from freqtrade/dependabot/pip/develop/tqdm-4.66.2
Bump tqdm from 4.66.1 to 4.66.2
2024-02-12 06:17:42 +01:00
Matthias ea0f4f6b86 Merge pull request #9799 from freqtrade/dependabot/pip/develop/ccxt-4.2.42
Bump ccxt from 4.2.39 to 4.2.42
2024-02-12 06:17:18 +01:00
dependabot[bot] 0ba27ddee6 Bump the pytest group with 2 updates
Bumps the pytest group with 2 updates: [pytest](https://github.com/pytest-dev/pytest) and [pytest-asyncio](https://github.com/pytest-dev/pytest-asyncio).


Updates `pytest` from 7.4.4 to 8.0.0
- [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/7.4.4...8.0.0)

Updates `pytest-asyncio` from 0.23.4 to 0.23.5
- [Release notes](https://github.com/pytest-dev/pytest-asyncio/releases)
- [Commits](https://github.com/pytest-dev/pytest-asyncio/compare/v0.23.4...v0.23.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-12 05:04:43 +00:00
Matthias f19aed5349 Merge pull request #9798 from freqtrade/dependabot/pip/develop/pre-commit-3.6.1
Bump pre-commit from 3.6.0 to 3.6.1
2024-02-12 06:03:30 +01:00
Matthias ca72b494bb Merge pull request #9797 from freqtrade/dependabot/pip/develop/uvicorn-0.27.1
Bump uvicorn from 0.27.0.post1 to 0.27.1
2024-02-12 06:03:06 +01:00
dependabot[bot] 2c1ea2d256 Bump tensorboard from 2.15.1 to 2.15.2
Bumps [tensorboard](https://github.com/tensorflow/tensorboard) from 2.15.1 to 2.15.2.
- [Release notes](https://github.com/tensorflow/tensorboard/releases)
- [Changelog](https://github.com/tensorflow/tensorboard/blob/2.15.2/RELEASE.md)
- [Commits](https://github.com/tensorflow/tensorboard/compare/2.15.1...2.15.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-12 03:30:19 +00:00
dependabot[bot] 8ca905b45f Bump python-telegram-bot from 20.7 to 20.8
Bumps [python-telegram-bot](https://github.com/python-telegram-bot/python-telegram-bot) from 20.7 to 20.8.
- [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/v20.7...v20.8)

---
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-02-12 03:30:00 +00:00
dependabot[bot] 903ae33679 Bump mkdocs-material from 9.5.8 to 9.5.9
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.8 to 9.5.9.
- [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.8...9.5.9)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-12 03:29:52 +00:00
dependabot[bot] 7d74a4c193 Bump tqdm from 4.66.1 to 4.66.2
Bumps [tqdm](https://github.com/tqdm/tqdm) from 4.66.1 to 4.66.2.
- [Release notes](https://github.com/tqdm/tqdm/releases)
- [Commits](https://github.com/tqdm/tqdm/compare/v4.66.1...v4.66.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-12 03:29:45 +00:00
dependabot[bot] 7e8e36c6be Bump ccxt from 4.2.39 to 4.2.42
Bumps [ccxt](https://github.com/ccxt/ccxt) from 4.2.39 to 4.2.42.
- [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.2.39...4.2.42)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-12 03:29:40 +00:00
dependabot[bot] d5aec91a41 Bump pre-commit from 3.6.0 to 3.6.1
Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 3.6.0 to 3.6.1.
- [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.6.0...v3.6.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-12 03:29:27 +00:00
dependabot[bot] f10c8f9c3b Bump uvicorn from 0.27.0.post1 to 0.27.1
Bumps [uvicorn](https://github.com/encode/uvicorn) from 0.27.0.post1 to 0.27.1.
- [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.27.0.post1...0.27.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-12 03:29:17 +00:00
Matthias 3aa2d0c30a Slightly improve memory-usage of kraken-convert 2024-02-10 09:37:59 +01:00
Matthias a83b5abb51 Allow wildcards for pairs filter 2024-02-10 08:48:52 +01:00
Matthias f89147fd44 Allow limiting kraken pairs to --pairs selection 2024-02-10 08:44:16 +01:00
Matthias adf63a45cf Align new-config documentation examples
closes #9790
2024-02-09 21:38:15 +01:00
Matthias 5bb4fe16b1 Merge pull request #9789 from freqtrade/dependabot/pip/develop/nbconvert-7.16.0
Bump nbconvert from 7.14.2 to 7.16.0
2024-02-08 20:35:08 +01:00
Matthias 444d939f88 Merge pull request #9786 from freqtrade/dependabot/pip/develop/mkdocs-material-9.5.8
Bump mkdocs-material from 9.5.7 to 9.5.8
2024-02-08 20:34:58 +01:00
Matthias f6f069511e Merge pull request #9787 from freqtrade/dependabot/pip/develop/ruff-0.2.1
Bump ruff from 0.2.0 to 0.2.1
2024-02-08 20:34:46 +01:00
Matthias 02742f36cd Merge pull request #9788 from freqtrade/dependabot/pip/develop/numpy-1.26.4
Bump numpy from 1.26.3 to 1.26.4
2024-02-08 20:34:34 +01:00
dependabot[bot] ce6b62f865 Bump numpy from 1.26.3 to 1.26.4
Bumps [numpy](https://github.com/numpy/numpy) from 1.26.3 to 1.26.4.
- [Release notes](https://github.com/numpy/numpy/releases)
- [Changelog](https://github.com/numpy/numpy/blob/main/doc/RELEASE_WALKTHROUGH.rst)
- [Commits](https://github.com/numpy/numpy/compare/v1.26.3...v1.26.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-08 18:26:56 +00:00
Matthias 1d07ae4f3a Merge pull request #9785 from freqtrade/dependabot/pip/develop/ccxt-4.2.39
Bump ccxt from 4.2.35 to 4.2.39
2024-02-08 19:26:09 +01:00
Matthias ba9e8d2bc5 Merge pull request #9783 from freqtrade/dependabot/github_actions/develop/pre-commit/action-3.0.1
Bump pre-commit/action from 3.0.0 to 3.0.1
2024-02-08 19:25:46 +01:00
dependabot[bot] 01a00ad048 Bump nbconvert from 7.14.2 to 7.16.0
Bumps [nbconvert](https://github.com/jupyter/nbconvert) from 7.14.2 to 7.16.0.
- [Release notes](https://github.com/jupyter/nbconvert/releases)
- [Changelog](https://github.com/jupyter/nbconvert/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jupyter/nbconvert/compare/v7.14.2...v7.16.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-08 17:06:26 +00:00
dependabot[bot] 9f449dd347 Bump ruff from 0.2.0 to 0.2.1
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.2.0 to 0.2.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/v0.2.0...v0.2.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-08 17:06:05 +00:00
dependabot[bot] 68fd7d7ffd Bump mkdocs-material from 9.5.7 to 9.5.8
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.7 to 9.5.8.
- [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.7...9.5.8)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-08 17:05:44 +00:00
dependabot[bot] a0c246fa9a Bump ccxt from 4.2.35 to 4.2.39
Bumps [ccxt](https://github.com/ccxt/ccxt) from 4.2.35 to 4.2.39.
- [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.2.35...4.2.39)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-08 17:05:39 +00:00
dependabot[bot] 7223a6c504 Bump pre-commit/action from 3.0.0 to 3.0.1
Bumps [pre-commit/action](https://github.com/pre-commit/action) from 3.0.0 to 3.0.1.
- [Release notes](https://github.com/pre-commit/action/releases)
- [Commits](https://github.com/pre-commit/action/compare/v3.0.0...v3.0.1)

---
updated-dependencies:
- dependency-name: pre-commit/action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-08 17:04:19 +00:00
Matthias bf1f08cd21 chore: add dependency groups for regular updates,
reschedule to run at 3am UTC (hope this works now ...)
2024-02-08 18:03:52 +01:00
Matthias 3e5a572fc6 Allow int as trade-id parameter
closes #9780
2024-02-08 07:16:56 +01:00
Matthias 386925f06b Merge pull request #9776 from stash86/bt-metrics2
fix strategy-updater docs
2024-02-08 06:44:01 +01:00
Matthias 626c904103 Fix some issues with types 2024-02-07 19:28:06 +01:00
Matthias 2393a9fecf Fix some minor test failures 2024-02-07 19:06:41 +01:00
Matthias 8f9f4b40cd Update model to new sqlalchemy version 2024-02-07 07:25:02 +01:00
Matthias 4700782f60 Merge branch 'develop' into feature_keyval_storage 2024-02-07 07:13:41 +01:00
Stefano Ariestasia 43bab85b85 fix strategy-updater docs 2024-02-07 11:21:32 +09:00
Matthias 9e89567eeb Merge pull request #9704 from ShaneOss/patch-2
fix: logical error in the conditional checks for model classes
2024-02-06 19:53:04 +01:00
Matthias d2e9d36dea chore: Update ordering of requirements in setup.py 2024-02-06 18:15:17 +01:00
Matthias e50fac34a5 Bump technical to 1.4.3 2024-02-06 07:03:30 +01:00
Matthias 3fd8b2f1c3 Merge pull request #9774 from freqtrade/update/pre-commit-hooks
Update pre-commit hooks
2024-02-06 06:20:33 +01:00
xmatthias 8d02504072 chore: update pre-commit hooks 2024-02-06 03:03:17 +00:00
Matthias 4aef5676d7 update tool.ruff configuration to match 2.0 version 2024-02-05 18:10:11 +01:00
Matthias 73e2e034ae Remove unused argument 2024-02-05 18:03:28 +01:00
Matthias b0da978999 Merge pull request #9767 from freqtrade/dependabot/pip/develop/ruff-0.2.0
Bump ruff from 0.1.15 to 0.2.0
2024-02-05 18:03:25 +01:00
Matthias 1eb27c8b87 Merge pull request #9773 from freqtrade/dependabot/pip/develop/pydantic-2.6.1
Bump pydantic from 2.5.3 to 2.6.1
2024-02-05 16:55:01 +01:00
dependabot[bot] b4f030fd55 Bump pydantic from 2.5.3 to 2.6.1
Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.5.3 to 2.6.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.5.3...v2.6.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-05 14:18:28 +00:00
Matthias e03ea80f79 Merge pull request #9769 from freqtrade/dependabot/pip/develop/orjson-3.9.13
Bump orjson from 3.9.12 to 3.9.13
2024-02-05 15:18:18 +01:00
Matthias d505f98d12 Merge pull request #9768 from freqtrade/dependabot/pip/develop/fastapi-0.109.2
Bump fastapi from 0.109.0 to 0.109.2
2024-02-05 15:17:38 +01:00
dependabot[bot] b9245aef2d Bump orjson from 3.9.12 to 3.9.13
Bumps [orjson](https://github.com/ijl/orjson) from 3.9.12 to 3.9.13.
- [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.9.12...3.9.13)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-05 08:21:44 +00:00
dependabot[bot] 646aca7a36 Bump fastapi from 0.109.0 to 0.109.2
Bumps [fastapi](https://github.com/tiangolo/fastapi) from 0.109.0 to 0.109.2.
- [Release notes](https://github.com/tiangolo/fastapi/releases)
- [Commits](https://github.com/tiangolo/fastapi/compare/0.109.0...0.109.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-02-05 08:20:20 +00:00
Matthias 76fbe6ed1f Merge pull request #9770 from freqtrade/dependabot/pip/develop/cryptography-42.0.2
Bump cryptography from 42.0.1 to 42.0.2
2024-02-05 09:19:15 +01:00
Matthias e526311c2c Merge pull request #9771 from freqtrade/dependabot/github_actions/develop/peter-evans/create-pull-request-6
Bump peter-evans/create-pull-request from 5 to 6
2024-02-05 09:18:35 +01:00
dependabot[bot] a675d2b026 Bump cryptography from 42.0.1 to 42.0.2
Bumps [cryptography](https://github.com/pyca/cryptography) from 42.0.1 to 42.0.2.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/42.0.1...42.0.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-05 06:02:59 +00:00
Matthias fcfde31985 Merge pull request #9766 from freqtrade/dependabot/pip/develop/mkdocs-material-9.5.7
Bump mkdocs-material from 9.5.6 to 9.5.7
2024-02-05 07:02:30 +01:00
Matthias 5c672ce487 Merge pull request #9763 from freqtrade/dependabot/pip/develop/aiohttp-3.9.3
Bump aiohttp from 3.9.2 to 3.9.3
2024-02-05 07:02:04 +01:00
Matthias 5ef261d7a7 Merge pull request #9764 from freqtrade/dependabot/pip/develop/ccxt-4.2.35
Bump ccxt from 4.2.25 to 4.2.35
2024-02-05 07:01:43 +01:00
Matthias 36cd3c70e2 Merge pull request #9765 from freqtrade/dependabot/pip/develop/urllib3-2.2.0
Bump urllib3 from 2.1.0 to 2.2.0
2024-02-05 07:01:20 +01:00
Matthias 6c08f0a86b Merge pull request #9761 from freqtrade/dependabot/pip/develop/uvicorn-0.27.0.post1
Bump uvicorn from 0.27.0 to 0.27.0.post1
2024-02-05 07:01:07 +01:00
dependabot[bot] 667a8cc59f Bump peter-evans/create-pull-request from 5 to 6
Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 5 to 6.
- [Release notes](https://github.com/peter-evans/create-pull-request/releases)
- [Commits](https://github.com/peter-evans/create-pull-request/compare/v5...v6)

---
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-02-05 03:59:04 +00:00
dependabot[bot] e773276dde Bump ruff from 0.1.15 to 0.2.0
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.1.15 to 0.2.0.
- [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/v0.1.15...v0.2.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-05 03:51:36 +00:00
dependabot[bot] 87e813a3dd Bump mkdocs-material from 9.5.6 to 9.5.7
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.5.6 to 9.5.7.
- [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.6...9.5.7)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-05 03:51:20 +00:00
dependabot[bot] 5114be42cd Bump urllib3 from 2.1.0 to 2.2.0
Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.1.0 to 2.2.0.
- [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.1.0...2.2.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-05 03:51:09 +00:00
dependabot[bot] 440382ae69 Bump ccxt from 4.2.25 to 4.2.35
Bumps [ccxt](https://github.com/ccxt/ccxt) from 4.2.25 to 4.2.35.
- [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.2.25...4.2.35)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-05 03:51:00 +00:00
dependabot[bot] ab7364c62d Bump aiohttp from 3.9.2 to 3.9.3
Bumps [aiohttp](https://github.com/aio-libs/aiohttp) from 3.9.2 to 3.9.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.9.2...v3.9.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-02-05 03:50:36 +00:00
dependabot[bot] 6961182680 Bump uvicorn from 0.27.0 to 0.27.0.post1
Bumps [uvicorn](https://github.com/encode/uvicorn) from 0.27.0 to 0.27.0.post1.
- [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.27.0...0.27.0.post1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-02-05 03:50:09 +00:00
Matthias cb2aaa7bbb Fix wording fully. 2024-02-03 15:00:48 +01:00
Matthias 35e1421d59 Reduce whitespace 2024-02-03 13:42:25 +01:00
Matthias 0f9e846587 Update Tests data to work with new cached levtiers 2024-02-03 13:32:53 +01:00
Matthias 2989b42760 Update cached binance leverage tiers 2024-02-03 13:16:32 +01:00
Matthias 3711fa5098 Improve formatting of leverage
closes #9759
2024-02-03 13:14:50 +01:00
Matthias c5948693a3 enable sub-minute backtest detail timeframes
closes #9635
2024-02-03 13:14:46 +01:00
Matthias 8469484998 chore: Split stoploss tests from freqtradebot
stoploss on exchange tests are quiet extensive, and deserve their own test file.
2024-02-02 07:25:53 +01:00
Matthias 57df7d9eca chore: convert test dir to package 2024-02-02 07:23:19 +01:00
Matthias 80f6fbbae9 chore: Move bot-related tests to test subdir 2024-02-02 07:07:58 +01:00
Matthias 0d14b7a800 perf: only create detail timedelta object once for detail backtests 2024-02-02 07:03:44 +01:00
Matthias ad121c19b0 Allow <1m timeframes for utility modes 2024-02-02 06:57:12 +01:00
Matthias 67c3bad977 Fix misspelled comment 2024-02-02 06:43:41 +01:00
Matthias 9bfd34a4f3 Merge pull request #9267 from Axel-CH/feature/update_sl_order_mgt
Update stoploss order management
2024-02-02 06:43:02 +01:00
Matthias f7472ca74c Merge pull request #9750 from freqtrade/feat/order_tags
Add tags per order
2024-02-01 07:11:06 +01:00
Matthias 44bc1b9aa1 Merge pull request #9743 from freqtrade/dependabot/pip/develop/lightgbm-4.3.0
Bump lightgbm from 4.2.0 to 4.3.0
2024-02-01 06:44:02 +01:00
Stefano Ariestasia 008c2feff9 Merge branch 'freqtrade:develop' into bt-metrics 2024-02-01 10:43:11 +09:00
Matthias 0bc5c876c2 Fix GITHUB_REF variable interpolation in ci.yml 2024-01-30 20:47:48 +01:00
Matthias 13ee90725e Fix syntax error 2024-01-30 20:19:24 +01:00
Matthias 7082a8073c add debugging for GITHUB_REF 2024-01-30 19:42:32 +01:00
Matthias cd484bd483 Update extract-branch-name to new syntax using GITHUB_OUTPUT 2024-01-30 19:40:16 +01:00
Matthias d1a96af5e8 Add ft_order_Tag to backtest test 2024-01-30 07:28:36 +01:00
Matthias 6ba8966090 chore: Add minified parameter docstring to to_json method 2024-01-30 07:25:46 +01:00
Matthias 78a1c7247a keep ft_order_tag for backtest responses 2024-01-30 07:25:15 +01:00
Matthias 0fa0f49b75 Add adjustment order tagging in strategy callbacks docs 2024-01-30 07:24:36 +01:00
Matthias 6806fab1b5 Fix migration not triggering 2024-01-30 07:24:36 +01:00
Matthias 79b8496f38 Fix backtesting not setting entry_tag out of position adjustments 2024-01-30 07:24:36 +01:00
Matthias d664e76834 Add some tests 2024-01-30 07:24:36 +01:00
Matthias 398b93beef Fix rpc test 2024-01-30 07:24:36 +01:00
Matthias 2d704a77b5 Improve formatting 2024-01-30 07:24:36 +01:00
Matthias 830a004dfd Move response handling to interface wrappermethod 2024-01-30 07:24:36 +01:00
Matthias 535ff387ff add order_tag handlig to running bot 2024-01-30 07:24:36 +01:00
Matthias 95e51bf816 allow adjust_trade_position to return tuples in backtesting 2024-01-30 07:24:36 +01:00
Matthias e8288a34c9 add ft_order_tag to backtesting 2024-01-30 07:24:36 +01:00
Matthias 39ffee381b Improve type hint 2024-01-30 07:24:36 +01:00
Matthias ccd4c715ca add order-tag to serialize / unserialize methods 2024-01-30 07:24:36 +01:00
Matthias d82bfc9fad Add order_tag to orders model 2024-01-30 07:24:36 +01:00
Matthias c59ae30e31 chore: Add minified parameter docs to to_json method 2024-01-30 07:24:26 +01:00
Matthias cfc6d3b32f Apply "Dependencies" label to pull requests 2024-01-30 07:04:53 +01:00
Matthias e58b7ea8e5 Merge pull request #9747 from freqtrade/update/pre-commit-hooks
Update pre-commit hooks
2024-01-30 07:03:51 +01:00
Matthias d219983f5b Bump ruff version 2024-01-30 07:03:07 +01:00
Matthias 6c27b27d4f Bump version to 2024.2-dev 2024-01-30 07:03:07 +01:00
Matthias fcedc7de0d Merge pull request #9659 from stash86/bt-metrics2
Add MarketCapPairList based on CoinGecko
2024-01-30 07:00:07 +01:00
xmatthias 36d4f10a60 chore: update pre-commit hooks 2024-01-30 03:03:17 +00:00
Matthias 470a239e82 Formatting ... 2024-01-29 20:10:50 +01:00
Matthias 01baea8aab Test exceptions / errors 2024-01-29 20:08:25 +01:00
Matthias 4a580fc72f Add test validating cache 2024-01-29 20:01:21 +01:00
Matthias d691ffe9db Test more diff. scenario 2024-01-29 19:47:08 +01:00
Matthias eb7fbb0096 Improve doc wording 2024-01-29 19:39:02 +01:00
Stefano Ariestasia 63aac1a2c9 update docs 2024-01-29 19:50:19 +09:00
Stefano 17af69435f remove mode, add max_rank, modify test 2024-01-29 17:57:14 +09:00
dependabot[bot] b02e15b162 Bump lightgbm from 4.2.0 to 4.3.0
Bumps [lightgbm](https://github.com/microsoft/LightGBM) from 4.2.0 to 4.3.0.
- [Release notes](https://github.com/microsoft/LightGBM/releases)
- [Commits](https://github.com/microsoft/LightGBM/compare/v4.2.0...v4.3.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-01-29 03:27:55 +00:00
Matthias 23ac9e145a Fix generator test 2024-01-27 16:25:00 +01:00
Matthias 63d8239011 Merge branch 'develop' into bt-metrics2 2024-01-27 16:24:05 +01:00
Matthias b72078e76e use get_pair_base_currency 2024-01-27 16:07:55 +01:00
Matthias 076ca75200 Slightly refactor pairlist test 2024-01-27 14:52:05 +01:00
Matthias 132e143b9a Minor comment fix 2024-01-27 14:49:50 +01:00
Stefano f8dc161ab4 add 2 more final tests 2024-01-27 19:33:12 +09:00
Stefano e0f7b62b54 fix initial tests 2024-01-27 19:12:50 +09:00
Stefano Ariestasia c854cef313 fix precommit 2024-01-27 18:12:19 +09:00
Stefano Ariestasia 006639820b add simple test 2024-01-27 18:10:10 +09:00
Stefano Ariestasia bf17236e9b fix typo in the docs 2024-01-27 16:25:54 +09:00
Stefano Ariestasia 60812983e3 fix error 2024-01-21 13:33:38 +09:00
Stefano Ariestasia bbec51685d remove can_filter, and use log_once 2024-01-21 13:28:50 +09:00
Shane ad0f88796b fix: logical error
Fix logical error in the conditional checks for model classes. The `elif` statement that looks for "lightgbm.sklearn" or "xgb" in the model class string is now broken into two separate conditions because the old condition would always evaluate to `True` due to the non-empty string "xgb".
2024-01-20 10:42:37 +11:00
Matthias e76888882d Fix typehint 2024-01-19 18:59:52 +01:00
Matthias 3ab226a096 Remove unused import 2024-01-19 18:24:03 +01:00
Matthias 58058f0197 Fix migration test 2024-01-19 18:20:23 +01:00
Matthias 6eaf42fe33 Default order_date to dt_now if it's not set via ccxt and wasn't previously set. 2024-01-19 18:17:53 +01:00
Matthias 88ba82d4fd Fix more tests 2024-01-19 18:17:31 +01:00
Matthias acbea4e26f Fix some tests after update_stoploss_date removal 2024-01-19 18:16:36 +01:00
Matthias b9a43b8e24 Don't store 'stoploss_last_updated' explicitly
it can easily be derived from the very last stoploss order.
2024-01-19 07:12:49 +01:00
Matthias 59b3486574 Update migrations 2024-01-19 06:49:49 +01:00
Matthias 9f3c6f2dcc Fix some tests and comments 2024-01-19 06:48:32 +01:00
Stefano de91261f73 fix pre-commit 2024-01-09 15:03:10 +09:00
Stefano Ariestasia e8fcac491a use number_assets instead of limit 2024-01-09 14:32:27 +09:00
Stefano Ariestasia 95af462e80 fix pre-commit 2024-01-09 14:21:07 +09:00
Stefano Ariestasia 3b54e1e746 add Try block to catch the failure on using FullTradesFilter on non-dry/live run 2024-01-09 14:17:35 +09:00
Stefano Ariestasia d3506c249d update docs 2024-01-09 14:08:15 +09:00
Stefano Ariestasia bc8c5e743f rename to PairList 2024-01-08 16:38:30 +09:00
Stefano Ariestasia ef8712f4d9 tidy up 2024-01-08 16:04:30 +09:00
Stefano Ariestasia fdf9cc1b76 tidy up codes 2024-01-08 16:02:31 +09:00
Stefano Ariestasia 442ca86d15 add market check 2024-01-08 15:33:43 +09:00
Stefano Ariestasia d578c910bc remove unused lines 2024-01-08 15:09:42 +09:00
Stefano Ariestasia 5bb4824aae add log print to see the time 2024-01-08 00:58:39 +09:00
Stefano Ariestasia f29fcb31e0 debug 2024-01-08 00:54:21 +09:00
Stefano Ariestasia 8055140e66 simplify code 2024-01-08 00:53:37 +09:00
Stefano Ariestasia 96a6c00acc add mode 2024-01-08 00:42:33 +09:00
Stefano Ariestasia 09cb6cb02d remove debug 2024-01-08 00:21:58 +09:00
Stefano Ariestasia 1f0a2ab242 forgot () 2024-01-08 00:21:07 +09:00
Stefano Ariestasia 35467619c4 add logger 2024-01-08 00:20:09 +09:00
Stefano Ariestasia bb684dd613 empty pairlist 2024-01-08 00:16:49 +09:00
Stefano Ariestasia adf07dd3e1 remove cache for noe 2024-01-08 00:16:16 +09:00
Stefano Ariestasia ef528fa69c option B 2024-01-08 00:14:55 +09:00
Stefano Ariestasia e10ab8c543 test gen_pairlist 2024-01-08 00:04:58 +09:00
Stefano Ariestasia a5862dcc4a add log message 2024-01-07 23:33:34 +09:00
Stefano Ariestasia 1cd08c96a1 filter the pairlist 2024-01-07 23:30:11 +09:00
Stefano Ariestasia 2eb3b73a93 cut the list to top x 2024-01-07 23:22:58 +09:00
Stefano Ariestasia 70cc2942d7 wrong arg name 2024-01-07 23:10:18 +09:00
Stefano Ariestasia 1e49200596 early stage of marketcapfilter 2024-01-07 20:16:44 +09:00
Matthias dc9c4da95e Improve integration test stability 2024-01-02 19:22:46 +01:00
Matthias 14660f54f8 Remove duplicate call to update_trade_state 2024-01-02 19:22:16 +01:00
Matthias e199083287 Fix test ... 2024-01-02 17:10:33 +01:00
Matthias 206809d2e7 Update emergency sell test 2024-01-02 17:05:21 +01:00
Matthias 128807ff3b Merge remote-tracking branch 'origin/develop' into pr/Axel-CH/9267 2024-01-02 17:04:07 +01:00
Matthias 501e256c58 Fix further stoploss test 2024-01-02 16:25:42 +01:00
Matthias 7ba9aa9acd Merge remote-tracking branch 'origin/develop' into pr/Axel-CH/9267 2024-01-02 12:11:44 +01:00
Matthias f0073078e9 Fix stop order test 2024-01-02 11:27:42 +01:00
Matthias a39b329e3b Fix line-length 2023-12-23 15:30:24 +01:00
Matthias 68f9402384 Fix further test 2023-12-23 15:19:56 +01:00
Matthias 600e311b3e Fix test test_handle_stoploss_on_exchange_custom_stop 2023-12-23 15:19:56 +01:00
Matthias 33bd433c22 Don't run against all orders, only consider open sl orders. 2023-12-23 15:19:56 +01:00
Matthias 1db4732648 Fix some more tests 2023-12-23 15:19:56 +01:00
Matthias ae3f62cf9b Fix RPC tests 2023-12-23 15:19:56 +01:00
Matthias cbfebd397c Use a trade for test that actually has an open stop order 2023-12-23 15:19:41 +01:00
Matthias c6ffe82a7a Update more tests 2023-12-23 15:19:41 +01:00
Matthias 6427144934 Fix stoploss test 2023-12-23 15:19:41 +01:00
Matthias c35b308ada Fix some tests 2023-12-23 15:19:41 +01:00
Matthias 28e2bfaf1c Fix types of "has" calls 2023-12-23 15:19:41 +01:00
Matthias 15058d3ce6 Add type hints to manage_trade_orders,
fix content ...
2023-12-23 15:19:41 +01:00
Matthias 931704abcc Merge branch 'develop' into feature/update_sl_order_mgt 2023-12-23 15:19:15 +01:00
Matthias 8234879b58 stoploss_order_id removal tests 2023-12-22 17:38:14 +01:00
Matthias b33a9059ab Fix some more tests 2023-12-22 17:35:02 +01:00
Matthias 13780d5963 Remove further usage 2023-12-22 17:22:33 +01:00
Matthias 279082b202 Merge branch 'develop' into pr/Axel-CH/9267 2023-12-22 17:21:05 +01:00
Matthias 41e40e6214 Update some initial tests 2023-11-28 06:48:27 +01:00
Matthias c2b32769a1 Remove further occurance in bot file 2023-11-28 06:48:20 +01:00
Matthias 42d75b0754 Merge branch 'develop' into pr/Axel-CH/9267 2023-11-28 06:45:37 +01:00
Axel-CH e8be011e2b update manage_trade_stoploss_orders: remove unrelevant TODO 2023-10-06 17:01:12 -04:00
Axel-CH aaa82e1fa9 remove all occurence of stoploss_order_id in trade_model, update api schemas, update rpc_delete 2023-10-06 16:34:03 -04:00
Axel-CH 2bb68ca53d remove stoploss_order_id from LocalTrade class 2023-10-06 16:08:40 -04:00
Axel-CH df8f1b9328 update manage_trade_stoploss_orders description 2023-10-06 15:58:51 -04:00
Axel-CH 9d82de15d4 first updated proposition of handle_stoploss_on_exchange, add sl_orders helper 2023-10-06 15:56:08 -04:00
Axel-CH ea828ccb4a remove legacy sl management code from create_stoploss_order 2023-10-06 10:40:23 -04:00
Axel-CH 2565d509a6 remove legacy sl management code from handle_insufficient_funds 2023-10-06 10:38:14 -04:00
Axel-CH d5a0759051 add open_sl_orders helper, use it in cancel_stoploss_on_exchange 2023-10-06 10:29:57 -04:00
Axel-CH 9214af6901 update cancel_stoploss_on_exchange to cancel all sl orders of trade 2023-10-05 22:24:17 -04:00
Axel-CH cac777cb21 add property has_open_sl_orders to trade model 2023-10-04 13:09:44 -04:00
eSeR1805 f755df2568 Merge branch 'develop' into feature_keyval_storage
Update rpc/telegram to use MAX_MESSAGE_LENGTH.
2022-08-11 11:26:45 +03:00
eSeR1805 c420304b33 Delete custom data before the trade. 2022-06-19 20:03:56 +03:00
eSeR1805 8494bea64f Handle max message length. 2022-06-19 19:59:14 +03:00
eSeR1805 c8ba8106e6 Update telegram reporting. 2022-06-19 17:24:13 +03:00
eSeR1805 ce9d9d7e60 Finish renaming persistant storage infrastructure. 2022-06-19 16:02:24 +03:00
eSeR1805 365527508b Rename files. 2022-06-19 15:59:21 +03:00
eSeR1805 9fdb8b07ac Rename persistant storage infrastructure. 2022-06-19 15:56:50 +03:00
eSeR1805 0009b987e4 Merge branch 'develop' into feature_keyval_storage 2022-06-19 13:32:25 +03:00
eSeR1805 24b6ce450b Further cleanup. 2022-06-14 13:27:50 +03:00
eSeR1805 3ad8111d11 Remove stray debug messages. 2022-06-14 13:26:45 +03:00
eSeR1805 4c6074062c Merge branch 'develop' into feature_keyval_storage 2022-06-13 20:05:45 +03:00
eSeR1805 4f799cc9db Add /list_kvals command for TG and underlying RPC. 2022-06-13 20:04:14 +03:00
eSeR1805 c719860a16 get_kval() -> get_kvals(). Update docs also. 2022-06-13 20:03:22 +03:00
eSeR1805 f3dee5ec4f Update handling for query_kv when no Key is supplied. 2022-06-13 20:02:06 +03:00
eSeR1805 be169a23f4 Add a new session for KeyValues. 2022-06-13 20:00:21 +03:00
eSeR1805 1551510c6f Merge branch 'develop' into feature_keyval_storage 2022-06-09 11:35:35 +03:00
eSeR1805 9f1a7209d6 Merge branch 'develop' into feature_keyval_storage 2022-06-05 12:18:30 +03:00
eSeR1805 abda02572b Fix KeyValue __repr__. 2022-06-05 12:18:07 +03:00
eSeR1805 de01aaf290 Add documentation details. 2022-05-31 16:17:31 +03:00
eSeR1805 096e98a68c Remove stray debug message. 2022-05-31 16:16:57 +03:00
eSeR1805 854bd9af2b Merge branch 'develop' into feature_keyval_storage 2022-05-31 12:28:58 +03:00
eSeR1805 10917a280a Add initial structure and wrapping. 2022-05-31 12:26:07 +03:00
178 changed files with 11058 additions and 6817 deletions
+9
View File
@@ -10,8 +10,17 @@ updates:
directory: "/"
schedule:
interval: weekly
time: "03:00"
timezone: "Etc/UTC"
open-pull-requests-limit: 15
target-branch: develop
groups:
types:
patterns:
- "types-*"
pytest:
patterns:
- "pytest*"
- package-ecosystem: "github-actions"
directory: "/"
@@ -0,0 +1,47 @@
name: Binance Leverage tiers update
on:
schedule:
- cron: "0 3 * * 4"
# on demand
workflow_dispatch:
permissions:
contents: read
jobs:
auto-update:
runs-on: ubuntu-latest
environment:
name: develop
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install ccxt
run: pip install ccxt
- name: Run leverage tier update
env:
CI_WEB_PROXY: ${{ secrets.CI_WEB_PROXY }}
FREQTRADE__EXCHANGE__KEY: ${{ secrets.BINANCE_EXCHANGE_KEY }}
FREQTRADE__EXCHANGE__SECRET: ${{ secrets.BINANCE_EXCHANGE_SECRET }}
run: python build_helpers/binance_update_lev_tiers.py
- uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.REPO_SCOPED_TOKEN }}
add-paths: freqtrade/exchange/binance_leverage_tiers.json
labels: |
Tech maintenance
Dependencies
branch: update/binance-leverage-tiers
title: Update Binance Leverage Tiers
commit-message: "chore: update pre-commit hooks"
committer: Freqtrade Bot <noreply@github.com>
body: Update binance leverage tiers.
delete-branch: true
+44 -16
View File
@@ -11,7 +11,7 @@ on:
types: [published]
pull_request:
schedule:
- cron: '0 5 * * 4'
- cron: '0 3 * * 4'
concurrency:
group: "${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}"
@@ -19,7 +19,7 @@ concurrency:
permissions:
repository-projects: read
jobs:
build_linux:
build-linux:
runs-on: ${{ matrix.os }}
strategy:
@@ -60,11 +60,16 @@ jobs:
export TA_LIBRARY_PATH=${HOME}/dependencies/lib
export TA_INCLUDE_PATH=${HOME}/dependencies/include
pip install -r requirements-dev.txt
pip install -e ft_client/
pip install -e .
- name: Check for version alignment
run: |
python build_helpers/freqtrade_client_version_align.py
- name: Tests
run: |
pytest --random-order --cov=freqtrade --cov-config=.coveragerc
pytest --random-order --cov=freqtrade --cov=freqtrade_client --cov-config=.coveragerc
- name: Coveralls
if: (runner.os == 'Linux' && matrix.python-version == '3.10' && matrix.os == 'ubuntu-22.04')
@@ -124,8 +129,11 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ "macos-latest", "macos-13" ]
os: [ "macos-latest", "macos-13", "macos-14" ]
python-version: ["3.9", "3.10", "3.11", "3.12"]
exclude:
- os: "macos-14"
python-version: "3.9"
steps:
- uses: actions/checkout@v4
@@ -154,7 +162,7 @@ jobs:
run: |
cd build_helpers && ./install_ta-lib.sh ${HOME}/dependencies/; cd ..
- name: Installation - macOS
- name: Installation - macOS (Brew)
run: |
# brew update
# TODO: Should be the brew upgrade
@@ -177,11 +185,15 @@ jobs:
rm /usr/local/bin/python3.12-config || true
brew install hdf5 c-blosc libomp
- name: Installation (python)
run: |
python -m pip install --upgrade pip wheel
export LD_LIBRARY_PATH=${HOME}/dependencies/lib:$LD_LIBRARY_PATH
export TA_LIBRARY_PATH=${HOME}/dependencies/lib
export TA_INCLUDE_PATH=${HOME}/dependencies/include
pip install -r requirements-dev.txt
pip install -e ft_client/
pip install -e .
- name: Tests
@@ -325,7 +337,7 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: "3.10"
- uses: pre-commit/action@v3.0.0
- uses: pre-commit/action@v3.0.1
docs-check:
runs-on: ubuntu-22.04
@@ -356,7 +368,7 @@ jobs:
webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
build_linux_online:
build-linux-online:
# Run pytest with "live" checks
runs-on: ubuntu-22.04
steps:
@@ -392,29 +404,30 @@ jobs:
export TA_LIBRARY_PATH=${HOME}/dependencies/lib
export TA_INCLUDE_PATH=${HOME}/dependencies/include
pip install -r requirements-dev.txt
pip install -e ft_client/
pip install -e .
- name: Tests incl. ccxt compatibility tests
env:
CI_WEB_PROXY: http://152.67.78.211:13128
run: |
pytest --random-order --longrun --durations 20 -n auto --dist loadscope
pytest --random-order --longrun --durations 20 -n auto
# Notify only once - when CI completes (and after deploy) in case it's successfull
notify-complete:
needs: [
build_linux,
build-linux,
build-macos,
build-windows,
docs-check,
mypy-version-check,
pre-commit,
build_linux_online
build-linux-online
]
runs-on: ubuntu-22.04
# Discord notification can't handle schedule events
if: (github.event_name != 'schedule')
if: github.event_name != 'schedule' && github.repository == 'freqtrade/freqtrade'
permissions:
repository-projects: read
steps:
@@ -437,7 +450,7 @@ jobs:
build:
name: "Build"
needs: [ build_linux, build-macos, build-windows, docs-check, mypy-version-check, pre-commit ]
needs: [ build-linux, build-macos, build-windows, docs-check, mypy-version-check, pre-commit ]
runs-on: ubuntu-22.04
steps:
@@ -461,6 +474,19 @@ jobs:
dist
retention-days: 10
- name: Build Client distribution
run: |
pip install -U build
python -m build --sdist --wheel ft_client
- name: Upload artifacts 📦
uses: actions/upload-artifact@v4
with:
name: freqtrade-client-build
path: |
ft_client/dist
retention-days: 10
deploy-pypi:
name: "Deploy to PyPI"
needs: [ build ]
@@ -478,20 +504,22 @@ jobs:
- name: Download artifact 📦
uses: actions/download-artifact@v4
with:
name: freqtrade-build
pattern: freqtrade*-build
path: dist
merge-multiple: true
- name: Publish to PyPI (Test)
uses: pypa/gh-action-pypi-publish@v1.8.11
uses: pypa/gh-action-pypi-publish@v1.8.14
with:
repository-url: https://test.pypi.org/legacy/
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@v1.8.11
uses: pypa/gh-action-pypi-publish@v1.8.14
deploy-docker:
needs: [ build_linux, build-macos, build-windows, docs-check, mypy-version-check, pre-commit ]
needs: [ build-linux, build-macos, build-windows, docs-check, mypy-version-check, pre-commit ]
runs-on: ubuntu-22.04
if: (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'release') && github.repository == 'freqtrade/freqtrade'
@@ -9,9 +9,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Docker Hub Description
uses: peter-evans/dockerhub-description@v4
env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKER_USERNAME }}
DOCKERHUB_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
DOCKERHUB_REPOSITORY: freqtradeorg/freqtrade
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
repository: freqtradeorg/freqtrade
+23 -23
View File
@@ -1,7 +1,6 @@
name: Pre-commit auto-update
on:
# every day at midnight
schedule:
- cron: "0 3 * * 2"
# on demand
@@ -14,31 +13,32 @@ jobs:
auto-update:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install pre-commit
run: pip install pre-commit
- name: Install pre-commit
run: pip install pre-commit
- name: Run auto-update
run: pre-commit autoupdate
- name: Run auto-update
run: pre-commit autoupdate
- name: Run pre-commit
run: pre-commit run --all-files
- name: Run pre-commit
run: pre-commit run --all-files
- uses: peter-evans/create-pull-request@v5
with:
token: ${{ secrets.REPO_SCOPED_TOKEN }}
add-paths: .pre-commit-config.yaml
labels: |
Tech maintenance
branch: update/pre-commit-hooks
title: Update pre-commit hooks
commit-message: "chore: update pre-commit hooks"
committer: Freqtrade Bot <noreply@github.com>
body: Update versions of pre-commit hooks to latest version.
delete-branch: true
- uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.REPO_SCOPED_TOKEN }}
add-paths: .pre-commit-config.yaml
labels: |
Tech maintenance
Dependencies
branch: update/pre-commit-hooks
title: Update pre-commit hooks
commit-message: "chore: update pre-commit hooks"
committer: Freqtrade Bot <noreply@github.com>
body: Update versions of pre-commit hooks to latest version.
delete-branch: true
+5 -5
View File
@@ -9,17 +9,17 @@ repos:
# stages: [push]
- repo: https://github.com/pre-commit/mirrors-mypy
rev: "v1.8.0"
rev: "v1.9.0"
hooks:
- id: mypy
exclude: build_helpers
additional_dependencies:
- types-cachetools==5.3.0.7
- types-filelock==3.2.7
- types-requests==2.31.0.20240125
- types-requests==2.31.0.20240311
- types-tabulate==0.9.0.20240106
- types-python-dateutil==2.8.19.20240106
- SQLAlchemy==2.0.25
- types-python-dateutil==2.9.0.20240316
- SQLAlchemy==2.0.29
# stages: [push]
- repo: https://github.com/pycqa/isort
@@ -31,7 +31,7 @@ repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
# Ruff version.
rev: 'v0.1.14'
rev: 'v0.3.4'
hooks:
- id: ruff
+1 -1
View File
@@ -48,7 +48,7 @@ pytest tests/test_<file_name>.py::test_<method_name>
#### Run Ruff
```bash
ruff .
ruff check .
```
We receive a lot of code that fails the `ruff` checks.
+1 -1
View File
@@ -1,4 +1,4 @@
FROM python:3.11.7-slim-bookworm as base
FROM python:3.12.2-slim-bookworm as base
# Setup env
ENV LANG C.UTF-8
+26
View File
@@ -0,0 +1,26 @@
#!/usr/bin/env python3
import json
import os
from pathlib import Path
import ccxt
key = os.environ.get('FREQTRADE__EXCHANGE__KEY')
secret = os.environ.get('FREQTRADE__EXCHANGE__SECRET')
proxy = os.environ.get('CI_WEB_PROXY')
exchange = ccxt.binance({
'apiKey': key,
'secret': secret,
'httpsProxy': proxy,
'options': {'defaultType': 'swap'}
})
_ = exchange.load_markets()
lev_tiers = exchange.fetch_leverage_tiers()
# Assumes this is running in the root of the repository.
file = Path('freqtrade/exchange/binance_leverage_tiers.json')
json.dump(dict(sorted(lev_tiers.items())), file.open('w'), indent=2)
+18
View File
@@ -0,0 +1,18 @@
#!/usr/bin/env python3
from freqtrade_client import __version__ as client_version
from freqtrade import __version__ as ft_version
def main():
if ft_version != client_version:
print(f"Versions do not match: \n"
f"ft: {ft_version} \n"
f"client: {client_version}")
exit(1)
print(f"Versions match: ft: {ft_version}, client: {client_version}")
exit(0)
if __name__ == '__main__':
main()
+1 -1
View File
@@ -1,4 +1,4 @@
FROM python:3.11.7-slim-bookworm as base
FROM python:3.11.8-slim-bookworm as base
# Setup env
ENV LANG C.UTF-8
+2 -2
View File
@@ -109,12 +109,12 @@ automatically accessible by including them on the indicator-list, and these incl
- **open_date :** trade open datetime
- **close_date :** trade close datetime
- **min_rate :** minimum price seen throughout the position
- **max_rate :** maxiumum price seen throughout the position
- **max_rate :** maximum price seen throughout the position
- **open :** signal candle open price
- **close :** signal candle close price
- **high :** signal candle high price
- **low :** signal candle low price
- **volume :** signal candle volumne
- **volume :** signal candle volume
- **profit_ratio :** trade profit ratio
- **profit_abs :** absolute profit return of the trade
Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

+35 -35
View File
@@ -252,34 +252,34 @@ The most important in the backtesting is to understand the result.
A backtesting result will look like that:
```
========================================================= BACKTESTING REPORT =========================================================
| Pair | Entries | Avg Profit % | Cum Profit % | Tot Profit BTC | Tot Profit % | Avg Duration | Wins Draws Loss Win% |
|:---------|--------:|---------------:|---------------:|-----------------:|---------------:|:-------------|-------------------------:|
| ADA/BTC | 35 | -0.11 | -3.88 | -0.00019428 | -1.94 | 4:35:00 | 14 0 21 40.0 |
| ARK/BTC | 11 | -0.41 | -4.52 | -0.00022647 | -2.26 | 2:03:00 | 3 0 8 27.3 |
| BTS/BTC | 32 | 0.31 | 9.78 | 0.00048938 | 4.89 | 5:05:00 | 18 0 14 56.2 |
| DASH/BTC | 13 | -0.08 | -1.07 | -0.00005343 | -0.53 | 4:39:00 | 6 0 7 46.2 |
| ENG/BTC | 18 | 1.36 | 24.54 | 0.00122807 | 12.27 | 2:50:00 | 8 0 10 44.4 |
| EOS/BTC | 36 | 0.08 | 3.06 | 0.00015304 | 1.53 | 3:34:00 | 16 0 20 44.4 |
| ETC/BTC | 26 | 0.37 | 9.51 | 0.00047576 | 4.75 | 6:14:00 | 11 0 15 42.3 |
| ETH/BTC | 33 | 0.30 | 9.96 | 0.00049856 | 4.98 | 7:31:00 | 16 0 17 48.5 |
| IOTA/BTC | 32 | 0.03 | 1.09 | 0.00005444 | 0.54 | 3:12:00 | 14 0 18 43.8 |
| LSK/BTC | 15 | 1.75 | 26.26 | 0.00131413 | 13.13 | 2:58:00 | 6 0 9 40.0 |
| LTC/BTC | 32 | -0.04 | -1.38 | -0.00006886 | -0.69 | 4:49:00 | 11 0 21 34.4 |
| NANO/BTC | 17 | 1.26 | 21.39 | 0.00107058 | 10.70 | 1:55:00 | 10 0 7 58.5 |
| NEO/BTC | 23 | 0.82 | 18.97 | 0.00094936 | 9.48 | 2:59:00 | 10 0 13 43.5 |
| REQ/BTC | 9 | 1.17 | 10.54 | 0.00052734 | 5.27 | 3:47:00 | 4 0 5 44.4 |
| XLM/BTC | 16 | 1.22 | 19.54 | 0.00097800 | 9.77 | 3:15:00 | 7 0 9 43.8 |
| XMR/BTC | 23 | -0.18 | -4.13 | -0.00020696 | -2.07 | 5:30:00 | 12 0 11 52.2 |
| XRP/BTC | 35 | 0.66 | 22.96 | 0.00114897 | 11.48 | 3:49:00 | 12 0 23 34.3 |
| ZEC/BTC | 22 | -0.46 | -10.18 | -0.00050971 | -5.09 | 2:22:00 | 7 0 15 31.8 |
| TOTAL | 429 | 0.36 | 152.41 | 0.00762792 | 76.20 | 4:12:00 | 186 0 243 43.4 |
====================================================== LEFT OPEN TRADES REPORT ======================================================
| Pair | Entries | Avg Profit % | Cum Profit % | Tot Profit BTC | Tot Profit % | Avg Duration | Win Draw Loss Win% |
|:---------|---------:|---------------:|---------------:|-----------------:|---------------:|:---------------|--------------------:|
| ADA/BTC | 1 | 0.89 | 0.89 | 0.00004434 | 0.44 | 6:00:00 | 1 0 0 100 |
| LTC/BTC | 1 | 0.68 | 0.68 | 0.00003421 | 0.34 | 2:00:00 | 1 0 0 100 |
| TOTAL | 2 | 0.78 | 1.57 | 0.00007855 | 0.78 | 4:00:00 | 2 0 0 100 |
================================================ BACKTESTING REPORT =================================================
| Pair | Entries | Avg Profit % | Tot Profit BTC | Tot Profit % | Avg Duration | Wins Draws Loss Win% |
|:---------|--------:|---------------:|-----------------:|---------------:|:-------------|-------------------------:|
| ADA/BTC | 35 | -0.11 | -0.00019428 | -1.94 | 4:35:00 | 14 0 21 40.0 |
| ARK/BTC | 11 | -0.41 | -0.00022647 | -2.26 | 2:03:00 | 3 0 8 27.3 |
| BTS/BTC | 32 | 0.31 | 0.00048938 | 4.89 | 5:05:00 | 18 0 14 56.2 |
| DASH/BTC | 13 | -0.08 | -0.00005343 | -0.53 | 4:39:00 | 6 0 7 46.2 |
| ENG/BTC | 18 | 1.36 | 0.00122807 | 12.27 | 2:50:00 | 8 0 10 44.4 |
| EOS/BTC | 36 | 0.08 | 0.00015304 | 1.53 | 3:34:00 | 16 0 20 44.4 |
| ETC/BTC | 26 | 0.37 | 0.00047576 | 4.75 | 6:14:00 | 11 0 15 42.3 |
| ETH/BTC | 33 | 0.30 | 0.00049856 | 4.98 | 7:31:00 | 16 0 17 48.5 |
| IOTA/BTC | 32 | 0.03 | 0.00005444 | 0.54 | 3:12:00 | 14 0 18 43.8 |
| LSK/BTC | 15 | 1.75 | 0.00131413 | 13.13 | 2:58:00 | 6 0 9 40.0 |
| LTC/BTC | 32 | -0.04 | -0.00006886 | -0.69 | 4:49:00 | 11 0 21 34.4 |
| NANO/BTC | 17 | 1.26 | 0.00107058 | 10.70 | 1:55:00 | 10 0 7 58.5 |
| NEO/BTC | 23 | 0.82 | 0.00094936 | 9.48 | 2:59:00 | 10 0 13 43.5 |
| REQ/BTC | 9 | 1.17 | 0.00052734 | 5.27 | 3:47:00 | 4 0 5 44.4 |
| XLM/BTC | 16 | 1.22 | 0.00097800 | 9.77 | 3:15:00 | 7 0 9 43.8 |
| XMR/BTC | 23 | -0.18 | -0.00020696 | -2.07 | 5:30:00 | 12 0 11 52.2 |
| XRP/BTC | 35 | 0.66 | 0.00114897 | 11.48 | 3:49:00 | 12 0 23 34.3 |
| ZEC/BTC | 22 | -0.46 | -0.00050971 | -5.09 | 2:22:00 | 7 0 15 31.8 |
| TOTAL | 429 | 0.36 | 0.00762792 | 76.20 | 4:12:00 | 186 0 243 43.4 |
============================================= LEFT OPEN TRADES REPORT =============================================
| Pair | Entries | Avg Profit % | Tot Profit BTC | Tot Profit % | Avg Duration | Win Draw Loss Win% |
|:---------|---------:|---------------:|-----------------:|---------------:|:---------------|--------------------:|
| ADA/BTC | 1 | 0.89 | 0.00004434 | 0.44 | 6:00:00 | 1 0 0 100 |
| LTC/BTC | 1 | 0.68 | 0.00003421 | 0.34 | 2:00:00 | 1 0 0 100 |
| TOTAL | 2 | 0.78 | 0.00007855 | 0.78 | 4:00:00 | 2 0 0 100 |
==================== EXIT REASON STATS ====================
| Exit Reason | Exits | Wins | Draws | Losses |
|:-------------------|--------:|------:|-------:|--------:|
@@ -358,7 +358,7 @@ here:
The bot has made `429` trades for an average duration of `4:12:00`, with a performance of `76.20%` (profit), that means it has
earned a total of `0.00762792 BTC` starting with a capital of 0.01 BTC.
The column `Avg Profit %` shows the average profit for all trades made while the column `Cum Profit %` sums up all the profits/losses.
The column `Avg Profit %` shows the average profit for all trades made.
The column `Tot Profit %` shows instead the total profit % in relation to the starting balance.
In the above results, we have a starting balance of 0.01 BTC and an absolute profit of 0.00762792 BTC - so the `Tot Profit %` will be `(0.00762792 / 0.01) * 100 ~= 76.2%`.
@@ -464,7 +464,7 @@ It contains some useful key metrics about performance of your strategy on backte
- `Profit factor`: profit / loss.
- `Avg. stake amount`: Average stake amount, either `stake_amount` or the average when using dynamic stake amount.
- `Total trade volume`: Volume generated on the exchange to reach the above profit.
- `Best Pair` / `Worst Pair`: Best and worst performing pair, and it's corresponding `Cum Profit %`.
- `Best Pair` / `Worst Pair`: Best and worst performing pair, and it's corresponding `Tot Profit %`.
- `Best Trade` / `Worst Trade`: Biggest single winning trade and biggest single losing trade.
- `Best day` / `Worst day`: Best and worst day based on daily profit.
- `Days win/draw/lose`: Winning / Losing days (draws are usually days without closed trade).
@@ -629,11 +629,11 @@ There will be an additional table comparing win/losses of the different strategi
Detailed output for all strategies one after the other will be available, so make sure to scroll up to see the details per strategy.
```
=========================================================== STRATEGY SUMMARY ===========================================================================
| Strategy | Entries | Avg Profit % | Cum Profit % | Tot Profit BTC | Tot Profit % | Avg Duration | Wins | Draws | Losses | Drawdown % |
|:------------|---------:|---------------:|---------------:|-----------------:|---------------:|:---------------|------:|-------:|-------:|-----------:|
| Strategy1 | 429 | 0.36 | 152.41 | 0.00762792 | 76.20 | 4:12:00 | 186 | 0 | 243 | 45.2 |
| Strategy2 | 1487 | -0.13 | -197.58 | -0.00988917 | -98.79 | 4:43:00 | 662 | 0 | 825 | 241.68 |
================================================== STRATEGY SUMMARY ===================================================================
| Strategy | Entries | Avg Profit % | Tot Profit BTC | Tot Profit % | Avg Duration | Wins | Draws | Losses | Drawdown % |
|:------------|---------:|---------------:|-----------------:|---------------:|:---------------|------:|-------:|-------:|-----------:|
| Strategy1 | 429 | 0.36 | 0.00762792 | 76.20 | 4:12:00 | 186 | 0 | 243 | 45.2 |
| Strategy2 | 1487 | -0.13 | -0.00988917 | -98.79 | 4:43:00 | 662 | 0 | 825 | 241.68 |
```
## Next step
+8 -5
View File
@@ -33,7 +33,6 @@ For spot pairs, naming will be `base/quote` (e.g. `ETH/USDT`).
For futures pairs, naming will be `base/quote:settle` (e.g. `ETH/USDT:USDT`).
## Bot execution logic
Starting freqtrade in dry-run or live mode (using `freqtrade trade`) will start the bot and start the bot iteration loop.
@@ -50,10 +49,12 @@ By default, the bot loop runs every few seconds (`internals.process_throttle_sec
* Call `populate_indicators()`
* Call `populate_entry_trend()`
* Call `populate_exit_trend()`
* Check timeouts for open orders.
* Calls `check_entry_timeout()` strategy callback for open entry orders.
* Calls `check_exit_timeout()` strategy callback for open exit orders.
* Calls `adjust_entry_price()` strategy callback for open entry orders.
* Update trades open order state from exchange.
* Call `order_filled()` strategy callback for filled orders.
* Check timeouts for open orders.
* Calls `check_entry_timeout()` strategy callback for open entry orders.
* Calls `check_exit_timeout()` strategy callback for open exit orders.
* Calls `adjust_entry_price()` strategy callback for open entry orders.
* Verifies existing positions and eventually places exit orders.
* Considers stoploss, ROI and exit-signal, `custom_exit()` and `custom_stoploss()`.
* Determine exit-price based on `exit_pricing` configuration setting or by using the `custom_exit_price()` callback.
@@ -86,8 +87,10 @@ This loop will be repeated again and again until the bot is stopped.
* In Margin and Futures mode, `leverage()` strategy callback is called to determine the desired leverage.
* Determine stake size by calling the `custom_stake_amount()` callback.
* Check position adjustments for open trades if enabled and call `adjust_trade_position()` to determine if an additional order is requested.
* Call `order_filled()` strategy callback for filled entry orders.
* Call `custom_stoploss()` and `custom_exit()` to find custom exit points.
* For exits based on exit-signal, custom-exit and partial exits: Call `custom_exit_price()` to determine exit price (Prices are moved to be within the closing candle).
* Call `order_filled()` strategy callback for filled exit orders.
* Generate backtest report output
!!! Note
+16 -4
View File
@@ -14,7 +14,7 @@ You can specify a different configuration file used by the bot with the `-c/--co
If you used the [Quick start](docker_quickstart.md#docker-quick-start) method for installing
the bot, the installation script should have already created the default configuration file (`config.json`) for you.
If the default configuration file is not created we recommend to use `freqtrade new-config --config config.json` to generate a basic configuration file.
If the default configuration file is not created we recommend to use `freqtrade new-config --config user_data/config.json` to generate a basic configuration file.
The Freqtrade configuration file is to be written in JSON format.
@@ -49,6 +49,13 @@ FREQTRADE__EXCHANGE__SECRET=<yourExchangeSecret>
!!! Note
Environment variables detected are logged at startup - so if you can't find why a value is not what you think it should be based on the configuration, make sure it's not loaded from an environment variable.
!!! Tip "Validate combined result"
You can use the [show-config subcommand](utils.md#show-config) to see the final, combined configuration.
??? Warning "Loading sequence"
Environment variables are loaded after the initial configuration. As such, you cannot provide the path to the configuration through environment variables. Please use `--config path/to/config.json` for that.
This also applies to user_dir to some degree. while the user directory can be set through environment variables - the configuration will **not** be loaded from that location.
### Multiple configuration files
Multiple configuration files can be specified and used by the bot or the bot can read its configuration parameters from the process standard input stream.
@@ -56,6 +63,9 @@ Multiple configuration files can be specified and used by the bot or the bot can
You can specify additional configuration files in `add_config_files`. Files specified in this parameter will be loaded and merged with the initial config file. The files are resolved relative to the initial configuration file.
This is similar to using multiple `--config` parameters, but simpler in usage as you don't have to specify all files for all commands.
!!! Tip "Validate combined result"
You can use the [show-config subcommand](utils.md#show-config) to see the final, combined configuration.
!!! Tip "Use multiple configuration files to keep secrets secret"
You can use a 2nd configuration file containing your secrets. That way you can share your "primary" configuration file, while still keeping your API keys for yourself.
The 2nd file should only specify what you intend to override.
@@ -326,6 +336,8 @@ You'd set `available_capital=5000` - granting each bot an initial capital of 500
The bot will then split this starting balance equally into `max_open_trades` buckets.
Profitable trades will result in increased stake-sizes for this bot - without affecting the stake-sizes of the other bot.
Adjusting `available_capital` requires reloading the configuration to take effect. Adjusting the `available_capital` adds the difference between the previous `available_capital` and the new `available_capital`. Decreasing the available capital when trades are open doesn't exit the trades. The difference is returned to the wallet when the trades conclude. The outcome of this differs depending on the price movement between the adjustment and exiting the trades.
!!! Warning "Incompatible with `tradable_balance_ratio`"
Setting this option will replace any configuration of `tradable_balance_ratio`.
@@ -503,13 +515,13 @@ Configuration:
Please carefully read the section [Market order pricing](#market-order-pricing) section when using market orders.
!!! Note "Stoploss on exchange"
`stoploss_on_exchange_interval` is not mandatory. Do not change its value if you are
`order_types.stoploss_on_exchange_interval` is not mandatory. Do not change its value if you are
unsure of what you are doing. For more information about how stoploss works please
refer to [the stoploss documentation](stoploss.md).
If `stoploss_on_exchange` is enabled and the stoploss is cancelled manually on the exchange, then the bot will create a new stoploss order.
If `order_types.stoploss_on_exchange` is enabled and the stoploss is cancelled manually on the exchange, then the bot will create a new stoploss order.
!!! Warning "Warning: stoploss_on_exchange failures"
!!! Warning "Warning: order_types.stoploss_on_exchange failures"
If stoploss on exchange creation fails for some reason, then an "emergency exit" is initiated. By default, this will exit the trade using a market order. The order-type for the emergency-exit can be changed by setting the `emergency_exit` value in the `order_types` dictionary - however, this is not advised.
### Understand order_time_in_force
+3 -1
View File
@@ -129,6 +129,8 @@ Below is an outline of exception inheritance hierarchy:
+ FreqtradeException
|
+---+ OperationalException
| |
| +---+ ConfigurationError
|
+---+ DependencyException
| |
@@ -376,7 +378,7 @@ from pathlib import Path
exchange = ccxt.binance({
'apiKey': '<apikey>',
'secret': '<secret>'
'secret': '<secret>',
'options': {'defaultType': 'swap'}
})
_ = exchange.load_markets()
+2
View File
@@ -68,6 +68,8 @@ Binance supports [time_in_force](configuration.md#understand-order_time_in_force
For Binance, it is suggested to add `"BNB/<STAKE>"` to your blacklist to avoid issues, unless you are willing to maintain enough extra `BNB` on the account or unless you're willing to disable using `BNB` for fees.
Binance accounts may use `BNB` for fees, and if a trade happens to be on `BNB`, further trades may consume this position and make the initial BNB trade unsellable as the expected amount is not there anymore.
If not enough `BNB` is available to cover transaction fees, then fees will not be covered by `BNB` and no fee reduction will occur. Freqtrade will never buy BNB to cover for fees. BNB needs to be bought and monitored manually to this end.
### Binance sites
Binance has been split into 2, and users must use the correct ccxt exchange ID for their exchange, otherwise API keys are not recognized.
+3
View File
@@ -32,6 +32,9 @@ FreqAI is configured through the typical [Freqtrade config file](configuration.m
A full example config is available in `config_examples/config_freqai.example.json`.
!!! Note
The `identifier` is commonly overlooked by newcomers, however, this value plays an important role in your configuration. This value is a unique ID that you choose to describe one of your runs. Keeping it the same allows you to maintain crash resilience as well as faster backtesting. As soon as you want to try a new run (new features, new model, etc.), you should change this value (or delete the `user_data/models/unique-id` folder. More details available in the [parameter table](freqai-parameter-table.md#feature-parameters).
## Building a FreqAI strategy
The FreqAI strategy requires including the following lines of code in the standard [Freqtrade strategy](strategy-customization.md):
+1 -1
View File
@@ -75,7 +75,7 @@ Mandatory parameters are marked as **Required** and have to be set in one of the
| `rl_config` | A dictionary containing the control parameters for a Reinforcement Learning model. <br> **Datatype:** Dictionary.
| `train_cycles` | Training time steps will be set based on the `train_cycles * number of training data points. <br> **Datatype:** Integer.
| `max_trade_duration_candles`| Guides the agent training to keep trades below desired length. Example usage shown in `prediction_models/ReinforcementLearner.py` within the customizable `calculate_reward()` function. <br> **Datatype:** int.
| `model_type` | Model string from stable_baselines3 or SBcontrib. Available strings include: `'TRPO', 'ARS', 'RecurrentPPO', 'MaskablePPO', 'PPO', 'A2C', 'DQN'`. User should ensure that `model_training_parameters` match those available to the corresponding stable_baselines3 model by visiting their documentaiton. [PPO doc](https://stable-baselines3.readthedocs.io/en/master/modules/ppo.html) (external website) <br> **Datatype:** string.
| `model_type` | Model string from stable_baselines3 or SBcontrib. Available strings include: `'TRPO', 'ARS', 'RecurrentPPO', 'MaskablePPO', 'PPO', 'A2C', 'DQN'`. User should ensure that `model_training_parameters` match those available to the corresponding stable_baselines3 model by visiting their documentation. [PPO doc](https://stable-baselines3.readthedocs.io/en/master/modules/ppo.html) (external website) <br> **Datatype:** string.
| `policy_type` | One of the available policy types from stable_baselines3 <br> **Datatype:** string.
| `max_training_drawdown_pct` | The maximum drawdown that the agent is allowed to experience during training. <br> **Datatype:** float. <br> Default: 0.8
| `cpu_count` | Number of threads/cpus to dedicate to the Reinforcement Learning training process (depending on if `ReinforcementLearning_multiproc` is selected or not). Recommended to leave this untouched, by default, this value is set to the total number of physical cores minus 1. <br> **Datatype:** int.
+1 -1
View File
@@ -142,7 +142,7 @@ Parameter details can be found [here](freqai-parameter-table.md), but in general
As you begin to modify the strategy and the prediction model, you will quickly realize some important differences between the Reinforcement Learner and the Regressors/Classifiers. Firstly, the strategy does not set a target value (no labels!). Instead, you set the `calculate_reward()` function inside the `MyRLEnv` class (see below). A default `calculate_reward()` is provided inside `prediction_models/ReinforcementLearner.py` to demonstrate the necessary building blocks for creating rewards, but this is *not* designed for production. Users *must* create their own custom reinforcement learning model class or use a pre-built one from outside the Freqtrade source code and save it to `user_data/freqaimodels`. It is inside the `calculate_reward()` where creative theories about the market can be expressed. For example, you can reward your agent when it makes a winning trade, and penalize the agent when it makes a losing trade. Or perhaps, you wish to reward the agent for entering trades, and penalize the agent for sitting in trades too long. Below we show examples of how these rewards are all calculated:
!!! note "Hint"
The best reward functions are ones that are continuously differentiable, and well scaled. In other words, adding a single large negative penalty to a rare event is not a good idea, and the neural net will not be able to learn that function. Instead, it is better to add a small negative penalty to a common event. This will help the agent learn faster. Not only this, but you can help improve the continuity of your rewards/penalties by having them scale with severity according to some linear/exponential functions. In other words, you'd slowly scale the penalty as the duration of the trade increases. This is better than a single large penalty occuring at a single point in time.
The best reward functions are ones that are continuously differentiable, and well scaled. In other words, adding a single large negative penalty to a rare event is not a good idea, and the neural net will not be able to learn that function. Instead, it is better to add a small negative penalty to a common event. This will help the agent learn faster. Not only this, but you can help improve the continuity of your rewards/penalties by having them scale with severity according to some linear/exponential functions. In other words, you'd slowly scale the penalty as the duration of the trade increases. This is better than a single large penalty occurring at a single point in time.
```python
from freqtrade.freqai.prediction_models.ReinforcementLearner import ReinforcementLearner
+35 -4
View File
@@ -6,7 +6,7 @@ In your configuration, you can use Static Pairlist (defined by the [`StaticPairL
Additionally, [`AgeFilter`](#agefilter), [`PrecisionFilter`](#precisionfilter), [`PriceFilter`](#pricefilter), [`ShuffleFilter`](#shufflefilter), [`SpreadFilter`](#spreadfilter) and [`VolatilityFilter`](#volatilityfilter) act as Pairlist Filters, removing certain pairs and/or moving their positions in the pairlist.
If multiple Pairlist Handlers are used, they are chained and a combination of all Pairlist Handlers forms the resulting pairlist the bot uses for trading and backtesting. Pairlist Handlers are executed in the sequence they are configured. You should always configure either `StaticPairList` or `VolumePairList` as the starting Pairlist Handler.
If multiple Pairlist Handlers are used, they are chained and a combination of all Pairlist Handlers forms the resulting pairlist the bot uses for trading and backtesting. Pairlist Handlers are executed in the sequence they are configured. You can define either `StaticPairList`, `VolumePairList`, `ProducerPairList`, `RemotePairList` or `MarketCapPairList` as the starting Pairlist Handler.
Inactive markets are always removed from the resulting pairlist. Explicitly blacklisted pairs (those in the `pair_blacklist` configuration setting) are also always removed from the resulting pairlist.
@@ -24,6 +24,7 @@ You may also use something like `.*DOWN/BTC` or `.*UP/BTC` to exclude leveraged
* [`VolumePairList`](#volume-pair-list)
* [`ProducerPairList`](#producerpairlist)
* [`RemotePairList`](#remotepairlist)
* [`MarketCapPairList`](#marketcappairlist)
* [`AgeFilter`](#agefilter)
* [`FullTradesFilter`](#fulltradesfilter)
* [`OffsetFilter`](#offsetfilter)
@@ -67,7 +68,7 @@ When used in the leading position of the chain of Pairlist Handlers, the `pair_w
The `refresh_period` setting allows to define the period (in seconds), at which the pairlist will be refreshed. Defaults to 1800s (30 minutes).
The pairlist cache (`refresh_period`) on `VolumePairList` is only applicable to generating pairlists.
Filtering instances (not the first position in the list) will not apply any cache and will always use up-to-date data.
Filtering instances (not the first position in the list) will not apply any cache (beyond caching candles for the duration of the candle in advanced mode) and will always use up-to-date data.
`VolumePairList` is per default based on the ticker data from exchange, as reported by the ccxt library:
@@ -80,12 +81,14 @@ Filtering instances (not the first position in the list) will not apply any cach
"number_assets": 20,
"sort_key": "quoteVolume",
"min_value": 0,
"max_value": 8000000,
"refresh_period": 1800
}
],
```
You can define a minimum volume with `min_value` - which will filter out pairs with a volume lower than the specified value in the specified timerange.
In addition to that, you can also define a maximum volume with `max_value` - which will filter out pairs with a volume higher than the specified value in the specified timerange.
##### VolumePairList Advanced mode
@@ -200,7 +203,7 @@ The RemotePairList is defined in the pairlists section of the configuration sett
The optional `mode` option specifies if the pairlist should be used as a `blacklist` or as a `whitelist`. The default value is "whitelist".
The optional `processing_mode` option in the RemotePairList configuration determines how the retrieved pairlist is processed. It can have two values: "filter" or "append".
The optional `processing_mode` option in the RemotePairList configuration determines how the retrieved pairlist is processed. It can have two values: "filter" or "append". The default value is "filter".
In "filter" mode, the retrieved pairlist is used as a filter. Only the pairs present in both the original pairlist and the retrieved pairlist are included in the final pairlist. Other pairs are filtered out.
@@ -264,6 +267,25 @@ The optional `bearer_token` will be included in the requests Authorization Heade
!!! Note
In case of a server error the last received pairlist will be kept if `keep_pairlist_on_failure` is set to true, when set to false a empty pairlist is returned.
#### MarketCapPairList
`MarketCapPairList` employs sorting/filtering of pairs by their marketcap rank based of CoinGecko. It will only recognize coins up to the coin placed at rank 250. The returned pairlist will be sorted based of their marketcap ranks.
```json
"pairlists": [
{
"method": "MarketCapPairList",
"number_assets": 20,
"max_rank": 50,
"refresh_period": 86400
}
]
```
`number_assets` defines the maximum number of pairs returned by the pairlist. `max_rank` will determine the maximum rank used in creating/filtering the pairlist. It's expected that some coins within the top `max_rank` marketcap will not be included in the resulting pairlist since not all pairs will have active trading pairs in your preferred market/stake/exchange combination.
`refresh_period` setting defines the period (in seconds) at which the marketcap rank data will be refreshed. Defaults to 86,400s (1 day). The pairlist cache (`refresh_period`) is applicable on both generating pairlists (first position in the list) and filtering instances (not the first position in the list).
#### AgeFilter
Removes pairs that have been listed on the exchange for less than `min_days_listed` days (defaults to `10`) or more than `max_days_listed` days (defaults `None` mean infinity).
@@ -349,6 +371,11 @@ As this Filter uses past performance of the bot, it'll have some startup-period
Filters low-value coins which would not allow setting stoplosses.
Namely, pairs are blacklisted if a variance of one percent or more in the stop price would be caused by precision rounding on the exchange, i.e. `rounded(stop_price) <= rounded(stop_price * 0.99)`. The idea is to avoid coins with a value VERY close to their lower trading boundary, not allowing setting of proper stoploss.
!!! Tip "PerformanceFilter is pointless for futures trading"
The above does not apply to shorts. And for longs, in theory the trade will be liquidated first.
!!! Warning "Backtesting"
`PrecisionFilter` does not support backtesting mode using multiple strategies.
@@ -430,6 +457,8 @@ If the trading range over the last 10 days is <1% or >99%, remove the pair from
]
```
Adding `"sort_direction": "asc"` or `"sort_direction": "desc"` enables sorting for this pairlist.
!!! Tip
This Filter can be used to automatically remove stable coin pairs, which have a very low trading range, and are therefore extremely difficult to trade with profit.
Additionally, it can also be used to automatically remove pairs with extreme high/low variance over a given amount of time.
@@ -440,7 +469,7 @@ Volatility is the degree of historical variation of a pairs over time, it is mea
This filter removes pairs if the average volatility over a `lookback_days` days is below `min_volatility` or above `max_volatility`. Since this is a filter that requires additional data, the results are cached for `refresh_period`.
This filter can be used to narrow down your pairs to a certain volatility or avoid very volatile pairs.
This filter can be used to narrow down your pairs to a certain volatility or avoid very volatile pairs.
In the below example:
If the volatility over the last 10 days is not in the range of 0.05-0.50, remove the pair from the whitelist. The filter is applied every 24h.
@@ -457,6 +486,8 @@ If the volatility over the last 10 days is not in the range of 0.05-0.50, remove
]
```
Adding `"sort_direction": "asc"` or `"sort_direction": "desc"` enables sorting mode for this pairlist.
### Full example of Pairlist Handlers
The below example blacklists `BNB/BTC`, uses `VolumePairList` with `20` assets, sorting pairs by `quoteVolume` and applies [`PrecisionFilter`](#precisionfilter) and [`PriceFilter`](#pricefilter), filtering all assets where 1 price unit is > 1%. Then the [`SpreadFilter`](#spreadfilter) and [`VolatilityFilter`](#volatilityfilter) is applied and pairs are finally shuffled with the random seed set to some predefined value.
+1 -1
View File
@@ -23,6 +23,7 @@ It also supports the lookahead-analysis of freqai strategies.
- `--max-open-trades` is forced to be at least equal to the number of pairs.
- `--dry-run-wallet` is forced to be basically infinite (1 billion).
- `--stake-amount` is forced to be a static 10000 (10k).
- `--enable-protections` is forced to be off.
Those are set to avoid users accidentally generating false positives.
@@ -40,7 +41,6 @@ usage: freqtrade lookahead-analysis [-h] [-v] [--logfile FILE] [-V] [-c PATH]
[--max-open-trades INT]
[--stake-amount STAKE_AMOUNT]
[--fee FLOAT] [-p PAIRS [PAIRS ...]]
[--enable-protections]
[--dry-run-wallet DRY_RUN_WALLET]
[--timeframe-detail TIMEFRAME_DETAIL]
[--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]]
+3 -3
View File
@@ -1,6 +1,6 @@
markdown==3.5.2
markdown==3.6
mkdocs==1.5.3
mkdocs-material==9.5.6
mkdocs-material==9.5.15
mdx_truly_sane_lists==1.3
pymdown-extensions==10.7
pymdown-extensions==10.7.1
jinja2==3.1.3
+25 -5
View File
@@ -95,11 +95,13 @@ Make sure that the following 2 lines are available in your docker-compose file:
### Consuming the API
You can consume the API by using the script `scripts/rest_client.py`.
The client script only requires the `requests` module, so Freqtrade does not need to be installed on the system.
You can consume the API by using `freqtrade-client` (also available as `scripts/rest_client.py`).
This command can be installed independent of the bot by using `pip install freqtrade-client`.
This module is designed to be lightweight, and only depends on the `requests` and `python-rapidjson` modules, skipping all heavy dependencies freqtrade otherwise needs.
``` bash
python3 scripts/rest_client.py <command> [optional parameters]
freqtrade-client <command> [optional parameters]
```
By default, the script assumes `127.0.0.1` (localhost) and port `8080` to be used, however you can specify a configuration file to override this behaviour.
@@ -120,9 +122,27 @@ By default, the script assumes `127.0.0.1` (localhost) and port `8080` to be use
```
``` bash
python3 scripts/rest_client.py --config rest_config.json <command> [optional parameters]
freqtrade-client --config rest_config.json <command> [optional parameters]
```
??? Note "Programmatic use"
The `freqtrade-client` package (installable independent of freqtrade) can be used in your own scripts to interact with the freqtrade API.
to do so, please use the following:
``` python
from freqtrade_client import FtRestClient
client = FtRestClient(server_url, username, password)
# Get the status of the bot
ping = client.ping()
print(ping)
# ...
```
For a full list of available commands, please refer to the list below.
### Available endpoints
| Command | Description |
@@ -176,7 +196,7 @@ python3 scripts/rest_client.py --config rest_config.json <command> [optional par
Possible commands can be listed from the rest-client script using the `help` command.
``` bash
python3 scripts/rest_client.py help
freqtrade-client help
```
``` output
+1 -1
View File
@@ -109,7 +109,7 @@ Freqtrade does not depend or install any additional database driver. Please refe
The following systems have been tested and are known to work with freqtrade:
* sqlite (default)
* PostgreSQL)
* PostgreSQL
* MariaDB
!!! Warning
+112 -17
View File
@@ -11,34 +11,129 @@ The call sequence of the methods described here is covered under [bot execution
!!! Tip
Start off with a strategy template containing all available callback methods by running `freqtrade new-strategy --strategy MyAwesomeStrategy --template advanced`
## Storing information
## Storing information (Persistent)
Storing information can be accomplished by creating a new dictionary within the strategy class.
Freqtrade allows storing/retrieving user custom information associated with a specific trade in the database.
The name of the variable can be chosen at will, but should be prefixed with `custom_` to avoid naming collisions with predefined strategy variables.
Using a trade object, information can be stored using `trade.set_custom_data(key='my_key', value=my_value)` and retrieved using `trade.get_custom_data(key='my_key')`. Each data entry is associated with a trade and a user supplied key (of type `string`). This means that this can only be used in callbacks that also provide a trade object.
For the data to be able to be stored within the database, freqtrade must serialized the data. This is done by converting the data to a JSON formatted string.
Freqtrade will attempt to reverse this action on retrieval, so from a strategy perspective, this should not be relevant.
```python
from freqtrade.persistence import Trade
from datetime import timedelta
class AwesomeStrategy(IStrategy):
# Create custom dictionary
custom_info = {}
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# Check if the entry already exists
if not metadata["pair"] in self.custom_info:
# Create empty entry for this pair
self.custom_info[metadata["pair"]] = {}
def bot_loop_start(self, **kwargs) -> None:
for trade in Trade.get_open_order_trades():
fills = trade.select_filled_orders(trade.entry_side)
if trade.pair == 'ETH/USDT':
trade_entry_type = trade.get_custom_data(key='entry_type')
if trade_entry_type is None:
trade_entry_type = 'breakout' if 'entry_1' in trade.enter_tag else 'dip'
elif fills > 1:
trade_entry_type = 'buy_up'
trade.set_custom_data(key='entry_type', value=trade_entry_type)
return super().bot_loop_start(**kwargs)
if "crosstime" in self.custom_info[metadata["pair"]]:
self.custom_info[metadata["pair"]]["crosstime"] += 1
else:
self.custom_info[metadata["pair"]]["crosstime"] = 1
def adjust_entry_price(self, trade: Trade, order: Optional[Order], pair: str,
current_time: datetime, proposed_rate: float, current_order_rate: float,
entry_tag: Optional[str], side: str, **kwargs) -> float:
# 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
and order.filled == 0.0
):
dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe)
current_candle = dataframe.iloc[-1].squeeze()
# store information about entry adjustment
existing_count = trade.get_custom_data('num_entry_adjustments', default=0)
if not existing_count:
existing_count = 1
else:
existing_count += 1
trade.set_custom_data(key='num_entry_adjustments', value=existing_count)
# adjust order price
return current_candle['sma_200']
# default: maintain existing order
return current_order_rate
def custom_exit(self, pair: str, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, **kwargs):
entry_adjustment_count = trade.get_custom_data(key='num_entry_adjustments')
trade_entry_type = trade.get_custom_data(key='entry_type')
if entry_adjustment_count is None:
if current_profit > 0.01 and (current_time - timedelta(minutes=100) > trade.open_date_utc):
return True, 'exit_1'
else
if entry_adjustment_count > 0 and if current_profit > 0.05:
return True, 'exit_2'
if trade_entry_type == 'breakout' and current_profit > 0.1:
return True, 'exit_3
return False, None
```
!!! Warning
The data is not persisted after a bot-restart (or config-reload). Also, the amount of data should be kept smallish (no DataFrames and such), otherwise the bot will start to consume a lot of memory and eventually run out of memory and crash.
The above is a simple example - there are simpler ways to retrieve trade data like entry-adjustments.
!!! Note
If the data is pair-specific, make sure to use pair as one of the keys in the dictionary.
It is recommended that simple data types are used `[bool, int, float, str]` to ensure no issues when serializing the data that needs to be stored.
Storing big junks of data may lead to unintended side-effects, like a database becoming big (and as a consequence, also slow).
!!! Warning "Non-serializable data"
If supplied data cannot be serialized a warning is logged and the entry for the specified `key` will contain `None` as data.
??? Note "All attributes"
custom-data has the following accessors through the Trade object (assumed as `trade` below):
* `trade.get_custom_data(key='something', default=0)` - Returns the actual value given in the type provided.
* `trade.get_custom_data_entry(key='something')` - Returns the entry - including metadata. The value is accessible via `.value` property.
* `trade.set_custom_data(key='something', value={'some': 'value'})` - set or update the corresponding key for this trade. Value must be serializable - and we recommend to keep the stored data relatively small.
"value" can be any type (both in setting and receiving) - but must be json serializable.
## Storing information (Non-Persistent)
!!! Warning "Deprecated"
This method of storing information is deprecated and we do advise against using non-persistent storage.
Please use [Persistent Storage](#storing-information-persistent) instead.
It's content has therefore been collapsed.
??? Abstract "Storing information"
Storing information can be accomplished by creating a new dictionary within the strategy class.
The name of the variable can be chosen at will, but should be prefixed with `custom_` to avoid naming collisions with predefined strategy variables.
```python
class AwesomeStrategy(IStrategy):
# Create custom dictionary
custom_info = {}
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# Check if the entry already exists
if not metadata["pair"] in self.custom_info:
# Create empty entry for this pair
self.custom_info[metadata["pair"]] = {}
if "crosstime" in self.custom_info[metadata["pair"]]:
self.custom_info[metadata["pair"]]["crosstime"] += 1
else:
self.custom_info[metadata["pair"]]["crosstime"] = 1
```
!!! Warning
The data is not persisted after a bot-restart (or config-reload). Also, the amount of data should be kept smallish (no DataFrames and such), otherwise the bot will start to consume a lot of memory and eventually run out of memory and crash.
!!! Note
If the data is pair-specific, make sure to use pair as one of the keys in the dictionary.
## Dataframe access
+44 -5
View File
@@ -19,6 +19,7 @@ Currently available callbacks:
* [`adjust_trade_position()`](#adjust-trade-position)
* [`adjust_entry_price()`](#adjust-entry-price)
* [`leverage()`](#leverage-callback)
* [`order_filled()`](#order-filled-callback)
!!! Tip "Callback calling sequence"
You can find the callback calling sequence in [bot-basics](bot-basics.md#bot-execution-logic)
@@ -767,6 +768,7 @@ This callback is **not** called when there is an open order (either buy or sell)
`adjust_trade_position()` is called very frequently for the duration of a trade, so you must keep your implementation as performant as possible.
Position adjustments will always be applied in the direction of the trade, so a positive value will always increase your position (negative values will decrease your position), no matter if it's a long or short trade.
Adjustment orders can be assigned with a tag by returning a 2 element Tuple, with the first element being the adjustment amount, and the 2nd element the tag (e.g. `return 250, 'increase_favorable_conditions'`).
Modifications to leverage are not possible, and the stake-amount returned is assumed to be before applying leverage.
@@ -782,7 +784,7 @@ Additional entries are ignored once you have reached the maximum amount of extra
### Decrease position
The strategy is expected to return a negative stake_amount (in stake currency) for a partial exit.
Returning the full owned stake at that point (based on the current price) (`-(trade.amount / trade.leverage) * current_exit_rate`) results in a full exit.
Returning the full owned stake at that point (`-trade.stake_amount`) results in a full exit.
Returning a value more than the above (so remaining stake_amount would become negative) will result in the bot ignoring the signal.
!!! Note "About stake size"
@@ -790,7 +792,7 @@ Returning a value more than the above (so remaining stake_amount would become ne
If you wish to buy additional orders with DCA, then make sure to leave enough funds in the wallet for that.
Using 'unlimited' stake amount with DCA orders requires you to also implement the `custom_stake_amount()` callback to avoid allocating all funds to the initial order.
!!! Warning
!!! Warning "Stoploss calculation"
Stoploss is still calculated from the initial opening price, not averaged price.
Regular stoploss rules still apply (cannot move down).
@@ -800,6 +802,11 @@ Returning a value more than the above (so remaining stake_amount would become ne
During backtesting this callback is called for each candle in `timeframe` or `timeframe_detail`, so run-time performance will be affected.
This can also cause deviating results between live and backtesting, since backtesting can adjust the trade only once per candle, whereas live could adjust the trade multiple times per candle.
!!! Warning "Performance with many position adjustments"
Position adjustments can be a good approach to increase a strategy's output - but it can also have drawbacks if using this feature extensively.
Each of the orders will be attached to the trade object for the duration of the trade - hence increasing memory usage.
Trades with long duration and 10s or even 100ds of position adjustments are therefore not recommended, and should be closed at regular intervals to not affect performance.
``` python
from freqtrade.persistence import Trade
@@ -833,7 +840,8 @@ class DigDeeperStrategy(IStrategy):
min_stake: Optional[float], max_stake: float,
current_entry_rate: float, current_exit_rate: float,
current_entry_profit: float, current_exit_profit: float,
**kwargs) -> Optional[float]:
**kwargs
) -> Union[Optional[float], Tuple[Optional[float], Optional[str]]]:
"""
Custom trade adjustment logic, returning the stake amount that a trade should be
increased or decreased.
@@ -859,11 +867,12 @@ class DigDeeperStrategy(IStrategy):
:return float: Stake amount to adjust your trade,
Positive values to increase position, Negative values to decrease position.
Return None for no action.
Optionally, return a tuple with a 2nd element with an order reason
"""
if current_profit > 0.05 and trade.nr_of_successful_exits == 0:
# Take half of the profit at +5%
return -(trade.stake_amount / 2)
return -(trade.stake_amount / 2), 'half_profit_5%'
if current_profit > -0.05:
return None
@@ -891,7 +900,7 @@ class DigDeeperStrategy(IStrategy):
stake_amount = filled_entries[0].stake_amount
# This then calculates current safety order size
stake_amount = stake_amount * (1 + (count_of_entries * 0.25))
return stake_amount
return stake_amount, '1/3rd_increase'
except Exception as exception:
return None
@@ -1014,3 +1023,33 @@ class AwesomeStrategy(IStrategy):
All profit calculations include leverage. Stoploss / ROI also include leverage in their calculation.
Defining a stoploss of 10% at 10x leverage would trigger the stoploss with a 1% move to the downside.
## Order filled Callback
The `order_filled()` callback may be used to perform specific actions based on the current trade state after an order is filled.
It will be called independent of the order type (entry, exit, stoploss or position adjustment).
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
class AwesomeStrategy(IStrategy):
def order_filled(self, pair: str, trade: Trade, order: Order, current_time: datetime, **kwargs) -> None:
"""
Called right after an order fills.
Will be called for all order types (entry, exit, stoploss, position adjustment).
:param pair: Pair for trade
:param trade: trade object.
:param order: Order object.
: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.
"""
# Obtain pair dataframe (just to show how to access it)
dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe)
last_candle = dataframe.iloc[-1].squeeze()
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'])
return None
```
+1 -1
View File
@@ -19,7 +19,7 @@ from pathlib import Path
project_root = "somedir/freqtrade"
i=0
try:
os.chdirdir(project_root)
os.chdir(project_root)
assert Path('LICENSE').is_file()
except:
while i<4 and (not Path('LICENSE').is_file()):
+2 -1
View File
@@ -59,7 +59,7 @@ For the Freqtrade configuration, you can then use the the full value (including
"chat_id": "-1001332619709"
```
!!! Warning "Using telegram groups"
When using telegram groups, you're giving every member of the telegram group access to your freqtrade bot and to all commands possible via telegram. Please make sure that you can trust everyone in the telegram group to avoid unpleasent surprises.
When using telegram groups, you're giving every member of the telegram group access to your freqtrade bot and to all commands possible via telegram. Please make sure that you can trust everyone in the telegram group to avoid unpleasant surprises.
## Control telegram noise
@@ -181,6 +181,7 @@ official commands. You can ask at any moment for help with `/help`.
| `/locks` | Show currently locked pairs.
| `/unlock <pair or lock_id>` | Remove the lock for this pair (or for this lock id).
| `/marketdir [long | short | even | none]` | Updates the user managed variable that represents the current market direction. If no direction is provided, the currently set direction will be displayed.
| `/list_custom_data <trade_id> [key]` | List custom_data for Trade ID & Key combination. If no Key is supplied it will list all key-value pairs found for that Trade ID.
| **Modify Trade states** |
| `/forceexit <trade_id> | /fx <tradeid>` | Instantly exits the given trade (Ignoring `minimum_roi`).
| `/forceexit all | /fx all` | Instantly exits all open trades (Ignoring `minimum_roi`).
+1 -1
View File
@@ -6,7 +6,7 @@ To update your freqtrade installation, please use one of the below methods, corr
Breaking changes / changed behavior will be documented in the changelog that is posted alongside every release.
For the develop branch, please follow PR's to avoid being surprised by changes.
## docker
## Docker
!!! Note "Legacy installations using the `master` image"
We're switching from master to stable for the release Images - please adjust your docker-file and replace `freqtradeorg/freqtrade:master` with `freqtradeorg/freqtrade:stable`
+79 -194
View File
@@ -54,7 +54,7 @@ optional arguments:
### Create config examples
```
$ freqtrade new-config --config config_binance.json
$ freqtrade new-config --config user_data/config_binance.json
? Do you want to enable Dry-run (simulated trades)? Yes
? Please insert your stake currency: BTC
@@ -66,6 +66,53 @@ $ freqtrade new-config --config config_binance.json
? Do you want to enable Telegram? No
```
## Show config
Show configuration file (with sensitive values redacted by default).
Especially useful with [split configuration files](configuration.md#multiple-configuration-files) or [environment variables](configuration.md#environment-variables), where this command will show the merged configuration.
![Show config output](assets/show-config-output.png)
```
usage: freqtrade show-config [-h] [--userdir PATH] [-c PATH]
[--show-sensitive]
options:
-h, --help show this help message and exit
--userdir PATH, --user-data-dir PATH
Path to userdata directory.
-c PATH, --config PATH
Specify configuration file (default:
`userdir/config.json` or `config.json` whichever
exists). Multiple --config options may be used. Can be
set to `-` to read config from stdin.
--show-sensitive Show secrets in the output.
```
``` output
Your combined configuration is:
{
"exit_pricing": {
"price_side": "other",
"use_order_book": true,
"order_book_top": 1
},
"stake_currency": "USDT",
"exchange": {
"name": "binance",
"key": "REDACTED",
"secret": "REDACTED",
"ccxt_config": {},
"ccxt_async_config": {},
}
// ...
}
```
!!! Warning "Sharing information provided by this command"
We try to remove all known sensitive information from the default output (without `--show-sensitive`).
Yet, please do double-check for sensitive values in your output to make sure you're not accidentally exposing some private info.
## Create new strategy
Creates a new strategy from a template similar to SampleStrategy.
@@ -219,207 +266,49 @@ optional arguments:
-a, --all Print all exchanges known to the ccxt library.
```
* Example: see exchanges available for the bot:
Example: see exchanges available for the bot:
```
$ freqtrade list-exchanges
Exchanges available for Freqtrade:
Exchange name Valid reason
--------------- ------- --------------------------------------------
aax True
ascendex True missing opt: fetchMyTrades
bequant True
bibox True
bigone True
binance True
binanceus True
bitbank True missing opt: fetchTickers
bitcoincom True
bitfinex True
bitforex True missing opt: fetchMyTrades, fetchTickers
bitget True
bithumb True missing opt: fetchMyTrades
bitkk True missing opt: fetchMyTrades
bitmart True
bitmax True missing opt: fetchMyTrades
bitpanda True
bitvavo True
bitz True missing opt: fetchMyTrades
btcalpha True missing opt: fetchTicker, fetchTickers
btcmarkets True missing opt: fetchTickers
buda True missing opt: fetchMyTrades, fetchTickers
bw True missing opt: fetchMyTrades, fetchL2OrderBook
bybit True
bytetrade True
cdax True
cex True missing opt: fetchMyTrades
coinbaseprime True missing opt: fetchTickers
coinbasepro True missing opt: fetchTickers
coinex True
crex24 True
deribit True
digifinex True
equos True missing opt: fetchTicker, fetchTickers
eterbase True
fcoin True missing opt: fetchMyTrades, fetchTickers
fcoinjp True missing opt: fetchMyTrades, fetchTickers
gateio True
gemini True
gopax True
hbtc True
hitbtc True
huobijp True
huobipro True
idex True
kraken True
kucoin True
lbank True missing opt: fetchMyTrades
mercado True missing opt: fetchTickers
ndax True missing opt: fetchTickers
novadax True
okcoin True
okex True
probit True
qtrade True
stex True
timex True
upbit True missing opt: fetchMyTrades
vcc True
zb True missing opt: fetchMyTrades
Exchange name Supported Markets Reason
------------------ ----------- ---------------------- ------------------------------------------------------------------------
binance Official spot, isolated futures
bitmart Official spot
bybit spot, isolated futures
gate Official spot, isolated futures
htx Official spot
huobi spot
kraken Official spot
okx Official spot, isolated futures
```
!!! info ""
Output reduced for clarity - supported and available exchanges may change over time.
!!! Note "missing opt exchanges"
Values with "missing opt:" might need special configuration (e.g. using orderbook if `fetchTickers` is missing) - but should in theory work (although we cannot guarantee they will).
* Example: see all exchanges supported by the ccxt library (including 'bad' ones, i.e. those that are known to not work with Freqtrade):
Example: see all exchanges supported by the ccxt library (including 'bad' ones, i.e. those that are known to not work with Freqtrade)
```
$ freqtrade list-exchanges -a
All exchanges supported by the ccxt library:
Exchange name Valid reason
------------------ ------- ---------------------------------------------------------------------------------------
aax True
aofex False missing: fetchOrder
ascendex True missing opt: fetchMyTrades
bequant True
bibox True
bigone True
binance True
binanceus True
bit2c False missing: fetchOrder, fetchOHLCV
bitbank True missing opt: fetchTickers
bitbay False missing: fetchOrder
bitcoincom True
bitfinex True
bitfinex2 False missing: fetchOrder
bitflyer False missing: fetchOrder, fetchOHLCV
bitforex True missing opt: fetchMyTrades, fetchTickers
bitget True
bithumb True missing opt: fetchMyTrades
bitkk True missing opt: fetchMyTrades
bitmart True
bitmax True missing opt: fetchMyTrades
bitmex False Various reasons.
bitpanda True
bitso False missing: fetchOHLCV
bitstamp True missing opt: fetchTickers
bitstamp1 False missing: fetchOrder, fetchOHLCV
bitvavo True
bitz True missing opt: fetchMyTrades
bl3p False missing: fetchOrder, fetchOHLCV
bleutrade False missing: fetchOrder
braziliex False missing: fetchOHLCV
btcalpha True missing opt: fetchTicker, fetchTickers
btcbox False missing: fetchOHLCV
btcmarkets True missing opt: fetchTickers
btctradeua False missing: fetchOrder, fetchOHLCV
btcturk False missing: fetchOrder
buda True missing opt: fetchMyTrades, fetchTickers
bw True missing opt: fetchMyTrades, fetchL2OrderBook
bybit True
bytetrade True
cdax True
cex True missing opt: fetchMyTrades
chilebit False missing: fetchOrder, fetchOHLCV
coinbase False missing: fetchOrder, cancelOrder, createOrder, fetchOHLCV
coinbaseprime True missing opt: fetchTickers
coinbasepro True missing opt: fetchTickers
coincheck False missing: fetchOrder, fetchOHLCV
coinegg False missing: fetchOHLCV
coinex True
coinfalcon False missing: fetchOHLCV
coinfloor False missing: fetchOrder, fetchOHLCV
coingi False missing: fetchOrder, fetchOHLCV
coinmarketcap False missing: fetchOrder, cancelOrder, createOrder, fetchBalance, fetchOHLCV
coinmate False missing: fetchOHLCV
coinone False missing: fetchOHLCV
coinspot False missing: fetchOrder, cancelOrder, fetchOHLCV
crex24 True
currencycom False missing: fetchOrder
delta False missing: fetchOrder
deribit True
digifinex True
equos True missing opt: fetchTicker, fetchTickers
eterbase True
exmo False missing: fetchOrder
exx False missing: fetchOHLCV
fcoin True missing opt: fetchMyTrades, fetchTickers
fcoinjp True missing opt: fetchMyTrades, fetchTickers
flowbtc False missing: fetchOrder, fetchOHLCV
foxbit False missing: fetchOrder, fetchOHLCV
gateio True
gemini True
gopax True
hbtc True
hitbtc True
hollaex False missing: fetchOrder
huobijp True
huobipro True
idex True
independentreserve False missing: fetchOHLCV
indodax False missing: fetchOHLCV
itbit False missing: fetchOHLCV
kraken True
kucoin True
kuna False missing: fetchOHLCV
lakebtc False missing: fetchOrder, fetchOHLCV
latoken False missing: fetchOrder, fetchOHLCV
lbank True missing opt: fetchMyTrades
liquid False missing: fetchOHLCV
luno False missing: fetchOHLCV
lykke False missing: fetchOHLCV
mercado True missing opt: fetchTickers
mixcoins False missing: fetchOrder, fetchOHLCV
ndax True missing opt: fetchTickers
novadax True
oceanex False missing: fetchOHLCV
okcoin True
okex True
paymium False missing: fetchOrder, fetchOHLCV
phemex False Does not provide history.
poloniex False missing: fetchOrder
probit True
qtrade True
rightbtc False missing: fetchOrder
ripio False missing: fetchOHLCV
southxchange False missing: fetchOrder, fetchOHLCV
stex True
surbitcoin False missing: fetchOrder, fetchOHLCV
therock False missing: fetchOHLCV
tidebit False missing: fetchOrder
tidex False missing: fetchOHLCV
timex True
upbit True missing opt: fetchMyTrades
vbtc False missing: fetchOrder, fetchOHLCV
vcc True
wavesexchange False missing: fetchOrder
whitebit False missing: fetchOrder, cancelOrder, createOrder, fetchBalance
xbtce False missing: fetchOrder, fetchOHLCV
xena False missing: fetchOrder
yobit False missing: fetchOHLCV
zaif False missing: fetchOrder, fetchOHLCV
zb True missing opt: fetchMyTrades
Exchange name Valid Supported Markets Reason
------------------ ------- ----------- ---------------------- ---------------------------------------------------------------------------------
binance True Official spot, isolated futures
bitflyer False spot missing: fetchOrder. missing opt: fetchTickers.
bitmart True Official spot
bybit True spot, isolated futures
gate True Official spot, isolated futures
htx True Official spot
kraken True Official spot
okx True Official spot, isolated futures
```
!!! info ""
Reduced output - supported and available exchanges may change over time.
## List Timeframes
Use the `list-timeframes` subcommand to see the list of timeframes available for the exchange.
@@ -990,11 +879,7 @@ options:
-h, --help show this help message and exit
--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]
Provide a space-separated list of strategies to
backtest. Please note that timeframe needs to be set
either in config or via command line. When using this
together with `--export trades`, the strategy-name is
injected into the filename (so `backtest-data.json`
becomes `backtest-data-SampleStrategy.json`
be converted.
Common arguments:
-v, --verbose Verbose mode (-vv for more, -vvv to get all messages).
+1 -1
View File
@@ -65,7 +65,7 @@ You can set the POST body format to Form-Encoded (default), JSON-Encoded, or raw
The result would be a POST request with e.g. `{"text":"Status: running"}` body and `Content-Type: application/json` header which results `Status: running` message in the Mattermost channel.
When using the Form-Encoded or JSON-Encoded configuration you can configure any number of payload values, and both the key and value will be ouput in the POST request. However, when using the raw data format you can only configure one value and it **must** be named `"data"`. In this instance the data key will not be output in the POST request, only the value. For example:
When using the Form-Encoded or JSON-Encoded configuration you can configure any number of payload values, and both the key and value will be output in the POST request. However, when using the raw data format you can only configure one value and it **must** be named `"data"`. In this instance the data key will not be output in the POST request, only the value. For example:
```json
"webhook": {
+1 -1
View File
@@ -1,5 +1,5 @@
""" Freqtrade bot """
__version__ = '2024.1'
__version__ = '2024.3'
if 'dev' in __version__:
from pathlib import Path
+1 -1
View File
@@ -8,7 +8,7 @@ Note: Be careful with file-scoped imports in these subfiles.
"""
from freqtrade.commands.analyze_commands import start_analysis_entries_exits
from freqtrade.commands.arguments import Arguments
from freqtrade.commands.build_config_commands import start_new_config
from freqtrade.commands.build_config_commands import start_new_config, start_show_config
from freqtrade.commands.data_commands import (start_convert_data, start_convert_trades,
start_download_data, start_list_data)
from freqtrade.commands.db_commands import start_convert_db
+3 -3
View File
@@ -4,7 +4,7 @@ from typing import Any, Dict
from freqtrade.configuration import setup_utils_configuration
from freqtrade.enums import RunMode
from freqtrade.exceptions import OperationalException
from freqtrade.exceptions import ConfigurationError, OperationalException
logger = logging.getLogger(__name__)
@@ -34,9 +34,9 @@ def setup_analyze_configuration(args: Dict[str, Any], method: RunMode) -> Dict[s
btfile = Path(config['exportfilename'])
signals_file = f"{btfile.parent}/{btfile.stem}_signals.pkl"
else:
raise OperationalException(f"{config['exportfilename']} does not exist.")
raise ConfigurationError(f"{config['exportfilename']} does not exist.")
else:
raise OperationalException('exportfilename not in config.')
raise ConfigurationError('exportfilename not in config.')
if (not Path(signals_file).exists()):
raise OperationalException(
+14 -4
View File
@@ -62,6 +62,7 @@ ARGS_TEST_PAIRLIST = ["user_data_dir", "verbosity", "config", "quote_currencies"
ARGS_CREATE_USERDIR = ["user_data_dir", "reset"]
ARGS_BUILD_CONFIG = ["config"]
ARGS_SHOW_CONFIG = ["user_data_dir", "config", "show_sensitive"]
ARGS_BUILD_STRATEGY = ["user_data_dir", "strategy", "template"]
@@ -69,7 +70,8 @@ ARGS_CONVERT_DATA_TRADES = ["pairs", "format_from_trades", "format_to", "erase",
ARGS_CONVERT_DATA = ["pairs", "format_from", "format_to", "erase", "exchange"]
ARGS_CONVERT_DATA_OHLCV = ARGS_CONVERT_DATA + ["timeframes", "trading_mode", "candle_types"]
ARGS_CONVERT_TRADES = ["pairs", "timeframes", "exchange", "dataformat_ohlcv", "dataformat_trades"]
ARGS_CONVERT_TRADES = ["pairs", "timeframes", "exchange", "dataformat_ohlcv", "dataformat_trades",
"trading_mode"]
ARGS_LIST_DATA = ["exchange", "dataformat_ohlcv", "pairs", "trading_mode", "show_timerange"]
@@ -208,9 +210,9 @@ class Arguments:
start_list_strategies, start_list_timeframes,
start_lookahead_analysis, start_new_config,
start_new_strategy, start_plot_dataframe, start_plot_profit,
start_recursive_analysis, start_show_trades,
start_strategy_update, start_test_pairlist, start_trading,
start_webserver)
start_recursive_analysis, start_show_config,
start_show_trades, start_strategy_update,
start_test_pairlist, start_trading, start_webserver)
subparsers = self.parser.add_subparsers(dest='command',
# Use custom message when no subhandler is added
@@ -243,6 +245,14 @@ class Arguments:
build_config_cmd.set_defaults(func=start_new_config)
self._build_args(optionlist=ARGS_BUILD_CONFIG, parser=build_config_cmd)
# add show-config subcommand
show_config_cmd = subparsers.add_parser(
'show-config',
help="Show resolved config",
)
show_config_cmd.set_defaults(func=start_show_config)
self._build_args(optionlist=ARGS_SHOW_CONFIG, parser=show_config_cmd)
# add new-strategy subcommand
build_strategy_cmd = subparsers.add_parser(
'new-strategy',
@@ -5,9 +5,12 @@ from typing import Any, Dict, List
from questionary import Separator, prompt
from freqtrade.configuration import sanitize_config
from freqtrade.configuration.config_setup import setup_utils_configuration
from freqtrade.configuration.detect_environment import running_in_docker
from freqtrade.configuration.directory_operations import chown_user_directory
from freqtrade.constants import UNLIMITED_STAKE_AMOUNT
from freqtrade.enums import RunMode
from freqtrade.exceptions import OperationalException
from freqtrade.exchange import MAP_EXCHANGE_CHILDCLASS, available_exchanges
from freqtrade.util import render_template
@@ -264,3 +267,19 @@ def start_new_config(args: Dict[str, Any]) -> None:
"Please delete it or use a different configuration file name.")
selections = ask_user_config()
deploy_new_config(config_path, selections)
def start_show_config(args: Dict[str, Any]) -> None:
config = setup_utils_configuration(args, RunMode.UTIL_EXCHANGE, set_dry=False)
# TODO: Sanitize from sensitive info before printing
print("Your combined configuration is:")
config_sanitized = sanitize_config(
config['original_config'],
show_sensitive=args.get('show_sensitive', False)
)
from rich import print_json
print_json(data=config_sanitized)
+6
View File
@@ -716,4 +716,10 @@ AVAILABLE_CLI_OPTIONS = {
help='Specify startup candles to be checked (`199`, `499`, `999`, `1999`).',
nargs='+',
),
"show_sensitive": Arg(
'--show-sensitive',
help='Show secrets in the output.',
action='store_true',
default=False,
),
}
+16 -7
View File
@@ -8,9 +8,10 @@ from freqtrade.constants import DATETIME_PRINT_FORMAT, DL_DATA_TIMEFRAMES, Confi
from freqtrade.data.converter import (convert_ohlcv_format, convert_trades_format,
convert_trades_to_ohlcv)
from freqtrade.data.history import download_data_main
from freqtrade.enums import RunMode, TradingMode
from freqtrade.exceptions import OperationalException
from freqtrade.enums import CandleType, RunMode, TradingMode
from freqtrade.exceptions import ConfigurationError
from freqtrade.exchange import timeframe_to_minutes
from freqtrade.plugins.pairlist.pairlist_helpers import dynamic_expand_pairlist
from freqtrade.resolvers import ExchangeResolver
from freqtrade.util.migrations import migrate_data
@@ -20,11 +21,11 @@ logger = logging.getLogger(__name__)
def _check_data_config_download_sanity(config: Config) -> None:
if 'days' in config and 'timerange' in config:
raise OperationalException("--days and --timerange are mutually exclusive. "
"You can only specify one or the other.")
raise ConfigurationError("--days and --timerange are mutually exclusive. "
"You can only specify one or the other.")
if 'pairs' not in config:
raise OperationalException(
raise ConfigurationError(
"Downloading data requires a list of pairs. "
"Please check the documentation on how to configure this.")
@@ -62,13 +63,21 @@ def start_convert_trades(args: Dict[str, Any]) -> None:
for timeframe in config['timeframes']:
exchange.validate_timeframes(timeframe)
available_pairs = [
p for p in exchange.get_markets(
tradable_only=True, active_only=not config.get('include_inactive')
).keys()
]
expanded_pairs = dynamic_expand_pairlist(config, available_pairs)
# Convert downloaded trade data to different timeframes
convert_trades_to_ohlcv(
pairs=config.get('pairs', []), timeframes=config['timeframes'],
pairs=expanded_pairs, timeframes=config['timeframes'],
datadir=config['datadir'], timerange=timerange, erase=bool(config.get('erase')),
data_format_ohlcv=config['dataformat_ohlcv'],
data_format_trades=config['dataformat_trades'],
candle_type=config.get('candle_type_def', CandleType.SPOT)
)
@@ -98,7 +107,7 @@ def start_list_data(args: Dict[str, Any]) -> None:
from tabulate import tabulate
from freqtrade.data.history.idatahandler import get_datahandler
from freqtrade.data.history import get_datahandler
dhc = get_datahandler(config['datadir'], config['dataformat_ohlcv'])
paircombs = dhc.ohlcv_get_available_data(
+2 -2
View File
@@ -9,7 +9,7 @@ from freqtrade.configuration import setup_utils_configuration
from freqtrade.configuration.directory_operations import copy_sample_files, create_userdata_dir
from freqtrade.constants import USERPATH_STRATEGIES
from freqtrade.enums import RunMode
from freqtrade.exceptions import OperationalException
from freqtrade.exceptions import ConfigurationError, OperationalException
from freqtrade.util import render_template, render_template_with_fallback
@@ -89,7 +89,7 @@ def start_new_strategy(args: Dict[str, Any]) -> None:
deploy_new_strategy(args['strategy'], new_path, args['template'])
else:
raise OperationalException("`new-strategy` requires --strategy to be set.")
raise ConfigurationError("`new-strategy` requires --strategy to be set.")
def clean_ui_subdir(directory: Path):
+2 -2
View File
@@ -10,7 +10,7 @@ from tabulate import tabulate
from freqtrade.configuration import setup_utils_configuration
from freqtrade.enums import RunMode
from freqtrade.exceptions import OperationalException
from freqtrade.exceptions import ConfigurationError, OperationalException
from freqtrade.exchange import list_available_exchanges, market_is_active
from freqtrade.misc import parse_db_uri_for_logging, plural
from freqtrade.resolvers import ExchangeResolver, StrategyResolver
@@ -246,7 +246,7 @@ def start_show_trades(args: Dict[str, Any]) -> None:
config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE)
if 'db_url' not in config:
raise OperationalException("--db-url is required for this command.")
raise ConfigurationError("--db-url is required for this command.")
logger.info(f'Using DB: "{parse_db_uri_for_logging(config["db_url"])}"')
init_db(config['db_url'])
+2 -2
View File
@@ -4,7 +4,7 @@ from typing import Any, Dict
from freqtrade import constants
from freqtrade.configuration import setup_utils_configuration
from freqtrade.enums import RunMode
from freqtrade.exceptions import OperationalException
from freqtrade.exceptions import ConfigurationError, OperationalException
from freqtrade.util import fmt_coin
@@ -31,7 +31,7 @@ def setup_optimize_configuration(args: Dict[str, Any], method: RunMode) -> Dict[
and config['stake_amount'] > wallet_size):
wallet = fmt_coin(wallet_size, config['stake_currency'])
stake = fmt_coin(config['stake_amount'], config['stake_currency'])
raise OperationalException(
raise ConfigurationError(
f"Starting balance ({wallet}) is smaller than stake_amount {stake}. "
f"Wallet is calculated as `dry_run_wallet * tradable_balance_ratio`."
)
+2 -2
View File
@@ -2,12 +2,12 @@ from typing import Any, Dict
from freqtrade.configuration import setup_utils_configuration
from freqtrade.enums import RunMode
from freqtrade.exceptions import OperationalException
from freqtrade.exceptions import ConfigurationError
def validate_plot_args(args: Dict[str, Any]) -> None:
if not args.get('datadir') and not args.get('config'):
raise OperationalException(
raise ConfigurationError(
"You need to specify either `--datadir` or `--config` "
"for plot-profit and plot-dataframe.")
-5
View File
@@ -23,11 +23,6 @@ def start_trading(args: Dict[str, Any]) -> int:
signal.signal(signal.SIGTERM, term_handler)
worker = Worker(args)
worker.run()
except Exception as e:
logger.error(str(e))
logger.exception("Fatal exception!")
except (KeyboardInterrupt):
logger.info('SIGINT received, aborting ...')
finally:
if worker:
logger.info("worker found ... calling exit")
+1
View File
@@ -1,5 +1,6 @@
# flake8: noqa: F401
from freqtrade.configuration.config_secrets import sanitize_config
from freqtrade.configuration.config_setup import setup_utils_configuration
from freqtrade.configuration.config_validation import validate_config_consistency
from freqtrade.configuration.configuration import Configuration
+36
View File
@@ -0,0 +1,36 @@
from copy import deepcopy
from freqtrade.constants import Config
def sanitize_config(config: Config, *, show_sensitive: bool = False) -> Config:
"""
Remove sensitive information from the config.
:param config: Configuration
:param show_sensitive: Show sensitive information
:return: Configuration
"""
if show_sensitive:
return config
keys_to_remove = [
"exchange.key",
"exchange.secret",
"exchange.password",
"exchange.uid",
"telegram.token",
"telegram.chat_id",
"discord.webhook_url",
"api_server.password",
]
config = deepcopy(config)
for key in keys_to_remove:
if '.' in key:
nested_keys = key.split('.')
nested_config = config
for nested_key in nested_keys[:-1]:
nested_config = nested_config.get(nested_key, {})
nested_config[nested_keys[-1]] = 'REDACTED'
else:
config[key] = 'REDACTED'
return config
+4 -2
View File
@@ -10,7 +10,8 @@ from .configuration import Configuration
logger = logging.getLogger(__name__)
def setup_utils_configuration(args: Dict[str, Any], method: RunMode) -> Dict[str, Any]:
def setup_utils_configuration(
args: Dict[str, Any], method: RunMode, *, set_dry: bool = True) -> Dict[str, Any]:
"""
Prepare the configuration for utils subcommands
:param args: Cli args from Arguments()
@@ -21,7 +22,8 @@ def setup_utils_configuration(args: Dict[str, Any], method: RunMode) -> Dict[str
config = configuration.get_config()
# Ensure these modes are using Dry-run
config['dry_run'] = True
if set_dry:
config['dry_run'] = True
validate_config_consistency(config, preliminary=True)
return config
+26 -26
View File
@@ -9,7 +9,7 @@ from jsonschema.exceptions import ValidationError, best_match
from freqtrade import constants
from freqtrade.configuration.deprecated_settings import process_deprecated_setting
from freqtrade.enums import RunMode, TradingMode
from freqtrade.exceptions import OperationalException
from freqtrade.exceptions import ConfigurationError
logger = logging.getLogger(__name__)
@@ -73,7 +73,7 @@ def validate_config_consistency(conf: Dict[str, Any], *, preliminary: bool = Fal
Should be ran after loading both configuration and strategy,
since strategies can set certain configuration settings too.
:param conf: Config in JSON format
:return: Returns None if everything is ok, otherwise throw an OperationalException
:return: Returns None if everything is ok, otherwise throw an ConfigurationError
"""
# validating trailing stoploss
@@ -98,12 +98,12 @@ def validate_config_consistency(conf: Dict[str, Any], *, preliminary: bool = Fal
def _validate_unlimited_amount(conf: Dict[str, Any]) -> None:
"""
If edge is disabled, either max_open_trades or stake_amount need to be set.
:raise: OperationalException if config validation failed
:raise: ConfigurationError if config validation failed
"""
if (not conf.get('edge', {}).get('enabled')
and conf.get('max_open_trades') == float('inf')
and conf.get('stake_amount') == constants.UNLIMITED_STAKE_AMOUNT):
raise OperationalException("`max_open_trades` and `stake_amount` cannot both be unlimited.")
raise ConfigurationError("`max_open_trades` and `stake_amount` cannot both be unlimited.")
def _validate_price_config(conf: Dict[str, Any]) -> None:
@@ -113,18 +113,18 @@ def _validate_price_config(conf: Dict[str, Any]) -> None:
# TODO: The below could be an enforced setting when using market orders
if (conf.get('order_types', {}).get('entry') == 'market'
and conf.get('entry_pricing', {}).get('price_side') not in ('ask', 'other')):
raise OperationalException(
raise ConfigurationError(
'Market entry orders require entry_pricing.price_side = "other".')
if (conf.get('order_types', {}).get('exit') == 'market'
and conf.get('exit_pricing', {}).get('price_side') not in ('bid', 'other')):
raise OperationalException('Market exit orders require exit_pricing.price_side = "other".')
raise ConfigurationError('Market exit orders require exit_pricing.price_side = "other".')
def _validate_trailing_stoploss(conf: Dict[str, Any]) -> None:
if conf.get('stoploss') == 0.0:
raise OperationalException(
raise ConfigurationError(
'The config stoploss needs to be different from 0 to avoid problems with sell orders.'
)
# Skip if trailing stoploss is not activated
@@ -137,17 +137,17 @@ def _validate_trailing_stoploss(conf: Dict[str, Any]) -> None:
if tsl_only_offset:
if tsl_positive == 0.0:
raise OperationalException(
raise ConfigurationError(
'The config trailing_only_offset_is_reached needs '
'trailing_stop_positive_offset to be more than 0 in your config.')
if tsl_positive > 0 and 0 < tsl_offset <= tsl_positive:
raise OperationalException(
raise ConfigurationError(
'The config trailing_stop_positive_offset needs '
'to be greater than trailing_stop_positive in your config.')
# Fetch again without default
if 'trailing_stop_positive' in conf and float(conf['trailing_stop_positive']) == 0.0:
raise OperationalException(
raise ConfigurationError(
'The config trailing_stop_positive needs to be different from 0 '
'to avoid problems with sell orders.'
)
@@ -162,7 +162,7 @@ def _validate_edge(conf: Dict[str, Any]) -> None:
return
if not conf.get('use_exit_signal', True):
raise OperationalException(
raise ConfigurationError(
"Edge requires `use_exit_signal` to be True, otherwise no sells will happen."
)
@@ -178,7 +178,7 @@ def _validate_whitelist(conf: Dict[str, Any]) -> None:
for pl in conf.get('pairlists', [{'method': 'StaticPairList'}]):
if (isinstance(pl, dict) and pl.get('method') == 'StaticPairList'
and not conf.get('exchange', {}).get('pair_whitelist')):
raise OperationalException("StaticPairList requires pair_whitelist to be set.")
raise ConfigurationError("StaticPairList requires pair_whitelist to be set.")
def _validate_protections(conf: Dict[str, Any]) -> None:
@@ -188,13 +188,13 @@ def _validate_protections(conf: Dict[str, Any]) -> None:
for prot in conf.get('protections', []):
if ('stop_duration' in prot and 'stop_duration_candles' in prot):
raise OperationalException(
raise ConfigurationError(
"Protections must specify either `stop_duration` or `stop_duration_candles`.\n"
f"Please fix the protection {prot.get('method')}"
)
if ('lookback_period' in prot and 'lookback_period_candles' in prot):
raise OperationalException(
raise ConfigurationError(
"Protections must specify either `lookback_period` or `lookback_period_candles`.\n"
f"Please fix the protection {prot.get('method')}"
)
@@ -206,7 +206,7 @@ def _validate_ask_orderbook(conf: Dict[str, Any]) -> None:
ob_max = ask_strategy.get('order_book_max')
if ob_min is not None and ob_max is not None and ask_strategy.get('use_order_book'):
if ob_min != ob_max:
raise OperationalException(
raise ConfigurationError(
"Using order_book_max != order_book_min in exit_pricing is no longer supported."
"Please pick one value and use `order_book_top` in the future."
)
@@ -234,7 +234,7 @@ def _validate_time_in_force(conf: Dict[str, Any]) -> None:
time_in_force = conf.get('order_time_in_force', {})
if 'buy' in time_in_force or 'sell' in time_in_force:
if conf.get('trading_mode', TradingMode.SPOT) != TradingMode.SPOT:
raise OperationalException(
raise ConfigurationError(
"Please migrate your time_in_force settings to use 'entry' and 'exit'.")
else:
logger.warning(
@@ -255,7 +255,7 @@ def _validate_order_types(conf: Dict[str, Any]) -> None:
'forcesell', 'emergencyexit', 'forceexit', 'forceentry']
if any(x in order_types for x in old_order_types):
if conf.get('trading_mode', TradingMode.SPOT) != TradingMode.SPOT:
raise OperationalException(
raise ConfigurationError(
"Please migrate your order_types settings to use the new wording.")
else:
logger.warning(
@@ -280,7 +280,7 @@ def _validate_unfilledtimeout(conf: Dict[str, Any]) -> None:
unfilledtimeout = conf.get('unfilledtimeout', {})
if any(x in unfilledtimeout for x in ['buy', 'sell']):
if conf.get('trading_mode', TradingMode.SPOT) != TradingMode.SPOT:
raise OperationalException(
raise ConfigurationError(
"Please migrate your unfilledtimeout settings to use the new wording.")
else:
@@ -300,7 +300,7 @@ def _validate_pricing_rules(conf: Dict[str, Any]) -> None:
if conf.get('ask_strategy') or conf.get('bid_strategy'):
if conf.get('trading_mode', TradingMode.SPOT) != TradingMode.SPOT:
raise OperationalException(
raise ConfigurationError(
"Please migrate your pricing settings to use the new wording.")
else:
@@ -331,7 +331,7 @@ def _validate_freqai_hyperopt(conf: Dict[str, Any]) -> None:
freqai_enabled = conf.get('freqai', {}).get('enabled', False)
analyze_per_epoch = conf.get('analyze_per_epoch', False)
if analyze_per_epoch and freqai_enabled:
raise OperationalException(
raise ConfigurationError(
'Using analyze-per-epoch parameter is not supported with a FreqAI strategy.')
@@ -350,7 +350,7 @@ def _validate_freqai_include_timeframes(conf: Dict[str, Any], preliminary: bool)
if tf_s < main_tf_s:
offending_lines.append(tf)
if offending_lines:
raise OperationalException(
raise ConfigurationError(
f"Main timeframe of {main_tf} must be smaller or equal to FreqAI "
f"`include_timeframes`.Offending include-timeframes: {', '.join(offending_lines)}")
@@ -368,17 +368,17 @@ def _validate_freqai_backtest(conf: Dict[str, Any]) -> None:
timerange = conf.get('timerange')
freqai_backtest_live_models = conf.get('freqai_backtest_live_models', False)
if freqai_backtest_live_models and freqai_enabled and timerange:
raise OperationalException(
raise ConfigurationError(
'Using timerange parameter is not supported with '
'--freqai-backtest-live-models parameter.')
if freqai_backtest_live_models and not freqai_enabled:
raise OperationalException(
raise ConfigurationError(
'Using --freqai-backtest-live-models parameter is only '
'supported with a FreqAI strategy.')
if freqai_enabled and not freqai_backtest_live_models and not timerange:
raise OperationalException(
raise ConfigurationError(
'Please pass --timerange if you intend to use FreqAI for backtesting.')
@@ -386,12 +386,12 @@ def _validate_consumers(conf: Dict[str, Any]) -> None:
emc_conf = conf.get('external_message_consumer', {})
if emc_conf.get('enabled', False):
if len(emc_conf.get('producers', [])) < 1:
raise OperationalException("You must specify at least 1 Producer to connect to.")
raise ConfigurationError("You must specify at least 1 Producer to connect to.")
producer_names = [p['name'] for p in emc_conf.get('producers', [])]
duplicates = [item for item, count in Counter(producer_names).items() if count > 1]
if duplicates:
raise OperationalException(
raise ConfigurationError(
f"Producer names must be unique. Duplicate: {', '.join(duplicates)}")
if conf.get('process_only_new_candles', True):
# Warning here or require it?
+6
View File
@@ -200,6 +200,12 @@ class Configuration:
config['exportfilename'] = (config['user_data_dir']
/ 'backtest_results')
if self.args.get('show_sensitive'):
logger.warning(
"Sensitive information will be shown in the upcomming output. "
"Please make sure to never share this output without redacting "
"the information yourself.")
def _process_optimize_options(self, config: Config) -> None:
# This will override the strategy configuration
@@ -6,7 +6,7 @@ import logging
from typing import Optional
from freqtrade.constants import Config
from freqtrade.exceptions import OperationalException
from freqtrade.exceptions import ConfigurationError, OperationalException
logger = logging.getLogger(__name__)
@@ -41,7 +41,7 @@ def process_removed_setting(config: Config,
section1_config = config.get(section1, {})
if name1 in section1_config:
section_2 = f"{section2}.{name2}" if section2 else f"{name2}"
raise OperationalException(
raise ConfigurationError(
f"Setting `{section1}.{name1}` has been moved to `{section_2}. "
f"Please delete it from your configuration and use the `{section_2}` "
"setting instead."
@@ -122,7 +122,7 @@ def process_temporary_deprecated_settings(config: Config) -> None:
None, 'ignore_roi_if_entry_signal')
if (config.get('edge', {}).get('enabled', False)
and 'capital_available_percentage' in config.get('edge', {})):
raise OperationalException(
raise ConfigurationError(
"DEPRECATED: "
"Using 'edge.capital_available_percentage' has been deprecated in favor of "
"'tradable_balance_ratio'. Please migrate your configuration to "
@@ -131,7 +131,7 @@ def process_temporary_deprecated_settings(config: Config) -> None:
)
if 'ticker_interval' in config:
raise OperationalException(
raise ConfigurationError(
"DEPRECATED: 'ticker_interval' detected. "
"Please use 'timeframe' instead of 'ticker_interval."
)
+5 -5
View File
@@ -11,7 +11,7 @@ from typing import Any, Dict, List, Optional
import rapidjson
from freqtrade.constants import MINIMAL_CONFIG, Config
from freqtrade.exceptions import OperationalException
from freqtrade.exceptions import ConfigurationError, OperationalException
from freqtrade.misc import deep_merge_dicts
@@ -46,7 +46,7 @@ def load_file(path: Path) -> Dict[str, Any]:
with path.open('r') as file:
config = rapidjson.load(file, parse_mode=CONFIG_PARSE_MODE)
except FileNotFoundError:
raise OperationalException(f'File "{path}" not found!')
raise OperationalException(f'File "{path}" not found!') from None
return config
@@ -63,10 +63,10 @@ def load_config_file(path: str) -> Dict[str, Any]:
except FileNotFoundError:
raise OperationalException(
f'Config file "{path}" not found!'
' Please create a config file or check whether it exists.')
' Please create a config file or check whether it exists.') from None
except rapidjson.JSONDecodeError as e:
err_range = log_config_error_range(path, str(e))
raise OperationalException(
raise ConfigurationError(
f'{e}\n'
f'Please verify the following segment of your configuration:\n{err_range}'
if err_range else 'Please verify your configuration file for syntax errors.'
@@ -83,7 +83,7 @@ def load_from_files(
"""
config: Config = {}
if level > 5:
raise OperationalException("Config loop detected.")
raise ConfigurationError("Config loop detected.")
if not files:
return deepcopy(MINIMAL_CONFIG)
+3 -3
View File
@@ -9,7 +9,7 @@ from typing import Optional
from typing_extensions import Self
from freqtrade.constants import DATETIME_PRINT_FORMAT
from freqtrade.exceptions import OperationalException
from freqtrade.exceptions import ConfigurationError
logger = logging.getLogger(__name__)
@@ -156,7 +156,7 @@ class TimeRange:
else:
stop = int(stops)
if start > stop > 0:
raise OperationalException(
raise ConfigurationError(
f'Start date is after stop date for timerange "{text}"')
return cls(stype[0], stype[1], start, stop)
raise OperationalException(f'Incorrect syntax for timerange "{text}"')
raise ConfigurationError(f'Incorrect syntax for timerange "{text}"')
+4 -3
View File
@@ -33,9 +33,10 @@ HYPEROPT_LOSS_BUILTIN = ['ShortTradeDurHyperOptLoss', 'OnlyProfitHyperOptLoss',
'MaxDrawDownHyperOptLoss', 'MaxDrawDownRelativeHyperOptLoss',
'ProfitDrawDownHyperOptLoss']
AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList', 'ProducerPairList', 'RemotePairList',
'AgeFilter', "FullTradesFilter", 'OffsetFilter', 'PerformanceFilter',
'PrecisionFilter', 'PriceFilter', 'RangeStabilityFilter',
'ShuffleFilter', 'SpreadFilter', 'VolatilityFilter']
'MarketCapPairList', 'AgeFilter', "FullTradesFilter", 'OffsetFilter',
'PerformanceFilter', 'PrecisionFilter', 'PriceFilter',
'RangeStabilityFilter', 'ShuffleFilter', 'SpreadFilter',
'VolatilityFilter']
AVAILABLE_PROTECTIONS = ['CooldownPeriod',
'LowProfitPairs', 'MaxDrawdown', 'StoplossGuard']
AVAILABLE_DATAHANDLERS = ['json', 'jsongz', 'hdf5', 'feather', 'parquet']
+2 -2
View File
@@ -11,7 +11,7 @@ import numpy as np
import pandas as pd
from freqtrade.constants import LAST_BT_RESULT_FN, IntOrInf
from freqtrade.exceptions import OperationalException
from freqtrade.exceptions import ConfigurationError, OperationalException
from freqtrade.misc import file_dump_json, json_load
from freqtrade.optimize.backtest_caching import get_backtest_metadata_filename
from freqtrade.persistence import LocalTrade, Trade, init_db
@@ -106,7 +106,7 @@ def get_latest_hyperopt_file(
directory = Path(directory)
if predef_filename:
if Path(predef_filename).is_absolute():
raise OperationalException(
raise ConfigurationError(
"--hyperopt-filename expects only the filename, not an absolute path.")
return directory / predef_filename
return directory / get_latest_hyperopt_filename(directory)
+1 -1
View File
@@ -200,7 +200,7 @@ def convert_ohlcv_format(
:param convert_to: Target format
:param erase: Erase source data (does not apply if source and target format are identical)
"""
from freqtrade.data.history.idatahandler import get_datahandler
from freqtrade.data.history import get_datahandler
src = get_datahandler(config['datadir'], convert_from)
trg = get_datahandler(config['datadir'], convert_to)
timeframes = config.get('timeframes', [config.get('timeframe')])
+15 -16
View File
@@ -11,7 +11,7 @@ from pandas import DataFrame, to_datetime
from freqtrade.configuration import TimeRange
from freqtrade.constants import (DEFAULT_DATAFRAME_COLUMNS, DEFAULT_TRADES_COLUMNS, TRADES_DTYPES,
Config, TradeList)
from freqtrade.enums import CandleType
from freqtrade.enums import CandleType, TradingMode
from freqtrade.exceptions import OperationalException
@@ -88,25 +88,23 @@ def convert_trades_to_ohlcv(
timeframes: List[str],
datadir: Path,
timerange: TimeRange,
erase: bool = False,
data_format_ohlcv: str = 'feather',
data_format_trades: str = 'feather',
candle_type: CandleType = CandleType.SPOT
erase: bool,
data_format_ohlcv: str,
data_format_trades: str,
candle_type: CandleType,
) -> None:
"""
Convert stored trades data to ohlcv data
"""
from freqtrade.data.history.idatahandler import get_datahandler
from freqtrade.data.history import get_datahandler
data_handler_trades = get_datahandler(datadir, data_format=data_format_trades)
data_handler_ohlcv = get_datahandler(datadir, data_format=data_format_ohlcv)
if not pairs:
pairs = data_handler_trades.trades_get_pairs(datadir)
logger.info(f"About to convert pairs: '{', '.join(pairs)}', "
f"intervals: '{', '.join(timeframes)}' to {datadir}")
trading_mode = TradingMode.FUTURES if candle_type != CandleType.SPOT else TradingMode.SPOT
for pair in pairs:
trades = data_handler_trades.trades_load(pair)
trades = data_handler_trades.trades_load(pair, trading_mode)
for timeframe in timeframes:
if erase:
if data_handler_ohlcv.ohlcv_purge(pair, timeframe, candle_type=candle_type):
@@ -116,7 +114,7 @@ def convert_trades_to_ohlcv(
# Store ohlcv
data_handler_ohlcv.ohlcv_store(pair, timeframe, data=ohlcv, candle_type=candle_type)
except ValueError:
logger.exception(f'Could not convert {pair} to OHLCV.')
logger.warning(f'Could not convert {pair} to OHLCV.')
def convert_trades_format(config: Config, convert_from: str, convert_to: str, erase: bool):
@@ -137,18 +135,19 @@ def convert_trades_format(config: Config, convert_from: str, convert_to: str, er
import_kraken_trades_from_csv(config, convert_to)
return
from freqtrade.data.history.idatahandler import get_datahandler
from freqtrade.data.history import get_datahandler
src = get_datahandler(config['datadir'], convert_from)
trg = get_datahandler(config['datadir'], convert_to)
if 'pairs' not in config:
config['pairs'] = src.trades_get_pairs(config['datadir'])
logger.info(f"Converting trades for {config['pairs']}")
trading_mode: TradingMode = config.get('trading_mode', TradingMode.SPOT)
for pair in config['pairs']:
data = src.trades_load(pair=pair)
data = src.trades_load(pair, trading_mode)
logger.info(f"Converting {len(data)} trades for {pair}")
trg.trades_store(pair, data)
trg.trades_store(pair, data, trading_mode)
if erase and convert_from != convert_to:
logger.info(f"Deleting source Trade data for {pair}.")
src.trades_purge(pair=pair)
src.trades_purge(pair, trading_mode)
@@ -6,8 +6,10 @@ import pandas as pd
from freqtrade.constants import DATETIME_PRINT_FORMAT, DEFAULT_TRADES_COLUMNS, Config
from freqtrade.data.converter.trade_converter import (trades_convert_types,
trades_df_remove_duplicates)
from freqtrade.data.history.idatahandler import get_datahandler
from freqtrade.data.history import get_datahandler
from freqtrade.enums import TradingMode
from freqtrade.exceptions import OperationalException
from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist
from freqtrade.resolvers import ExchangeResolver
@@ -38,12 +40,22 @@ def import_kraken_trades_from_csv(config: Config, convert_to: str):
}
logger.info(f"Found csv files for {', '.join(data_symbols)}.")
if pairs_raw := config.get('pairs'):
pairs = expand_pairlist(pairs_raw, [m[0] for m in markets])
markets = {m for m in markets if m[0] in pairs}
if not markets:
logger.info(f"No data found for pairs {', '.join(pairs_raw)}.")
return
logger.info(f"Converting pairs: {', '.join(m[0] for m in markets)}.")
for pair, name in markets:
logger.debug(f"Converting pair {pair}, files */{name}.csv")
dfs = []
# Load and combine all csv files for this pair
for f in tradesdir.rglob(f"{name}.csv"):
df = pd.read_csv(f, names=KRAKEN_CSV_TRADE_COLUMNS)
dfs.append(df)
if not df.empty:
dfs.append(df)
# Load existing trades data
if not dfs:
@@ -52,19 +64,20 @@ def import_kraken_trades_from_csv(config: Config, convert_to: str):
continue
trades = pd.concat(dfs, ignore_index=True)
del dfs
trades.loc[:, 'timestamp'] = trades['timestamp'] * 1e3
trades.loc[:, 'cost'] = trades['price'] * trades['amount']
for col in DEFAULT_TRADES_COLUMNS:
if col not in trades.columns:
trades[col] = ''
trades.loc[:, col] = ''
trades = trades[DEFAULT_TRADES_COLUMNS]
trades = trades_convert_types(trades)
trades_df = trades_df_remove_duplicates(trades)
del trades
logger.info(f"{pair}: {len(trades_df)} trades, from "
f"{trades_df['date'].min():{DATETIME_PRINT_FORMAT}} to "
f"{trades_df['date'].max():{DATETIME_PRINT_FORMAT}}")
data_handler.trades_store(pair, trades_df)
data_handler.trades_store(pair, trades_df, TradingMode.SPOT)
+1 -1
View File
@@ -6,7 +6,7 @@ Includes:
* download data from exchange and store to disk
"""
# flake8: noqa: F401
from .datahandlers import get_datahandler
from .history_utils import (convert_trades_to_ohlcv, download_data_main, get_timerange, load_data,
load_pair_history, refresh_backtest_ohlcv_data,
refresh_backtest_trades_data, refresh_data, validate_backtest_data)
from .idatahandler import get_datahandler
@@ -0,0 +1,2 @@
# flake8: noqa: F401
from .idatahandler import IDataHandler, get_datahandler
@@ -5,7 +5,7 @@ from pandas import DataFrame, read_feather, to_datetime
from freqtrade.configuration import TimeRange
from freqtrade.constants import DEFAULT_DATAFRAME_COLUMNS, DEFAULT_TRADES_COLUMNS
from freqtrade.enums import CandleType
from freqtrade.enums import CandleType, TradingMode
from .idatahandler import IDataHandler
@@ -82,14 +82,15 @@ class FeatherDataHandler(IDataHandler):
"""
raise NotImplementedError()
def _trades_store(self, pair: str, data: DataFrame) -> None:
def _trades_store(self, pair: str, data: DataFrame, trading_mode: TradingMode) -> None:
"""
Store trades data (list of Dicts) to file
:param pair: Pair - used for filename
:param data: Dataframe containing trades
column sequence as in DEFAULT_TRADES_COLUMNS
:param trading_mode: Trading mode to use (used to determine the filename)
"""
filename = self._pair_trades_filename(self._datadir, pair)
filename = self._pair_trades_filename(self._datadir, pair, trading_mode)
self.create_dir_if_needed(filename)
data.reset_index(drop=True).to_feather(filename, compression_level=9, compression='lz4')
@@ -102,15 +103,18 @@ class FeatherDataHandler(IDataHandler):
"""
raise NotImplementedError()
def _trades_load(self, pair: str, timerange: Optional[TimeRange] = None) -> DataFrame:
def _trades_load(
self, pair: str, trading_mode: TradingMode, timerange: Optional[TimeRange] = None
) -> DataFrame:
"""
Load a pair from file, either .json.gz or .json
# TODO: respect timerange ...
:param pair: Load trades for this pair
:param trading_mode: Trading mode to use (used to determine the filename)
:param timerange: Timerange to load trades for - currently not implemented
:return: Dataframe containing trades
"""
filename = self._pair_trades_filename(self._datadir, pair)
filename = self._pair_trades_filename(self._datadir, pair, trading_mode)
if not filename.exists():
return DataFrame(columns=DEFAULT_TRADES_COLUMNS)
@@ -6,7 +6,7 @@ import pandas as pd
from freqtrade.configuration import TimeRange
from freqtrade.constants import DEFAULT_DATAFRAME_COLUMNS, DEFAULT_TRADES_COLUMNS
from freqtrade.enums import CandleType
from freqtrade.enums import CandleType, TradingMode
from .idatahandler import IDataHandler
@@ -35,7 +35,7 @@ class HDF5DataHandler(IDataHandler):
self.create_dir_if_needed(filename)
_data.loc[:, self._columns].to_hdf(
filename, key, mode='a', complevel=9, complib='blosc',
filename, key=key, mode='a', complevel=9, complib='blosc',
format='table', data_columns=['date']
)
@@ -100,17 +100,18 @@ class HDF5DataHandler(IDataHandler):
"""
raise NotImplementedError()
def _trades_store(self, pair: str, data: pd.DataFrame) -> None:
def _trades_store(self, pair: str, data: pd.DataFrame, trading_mode: TradingMode) -> None:
"""
Store trades data (list of Dicts) to file
:param pair: Pair - used for filename
:param data: Dataframe containing trades
column sequence as in DEFAULT_TRADES_COLUMNS
:param trading_mode: Trading mode to use (used to determine the filename)
"""
key = self._pair_trades_key(pair)
data.to_hdf(
self._pair_trades_filename(self._datadir, pair), key,
self._pair_trades_filename(self._datadir, pair, trading_mode), key=key,
mode='a', complevel=9, complib='blosc',
format='table', data_columns=['timestamp']
)
@@ -124,15 +125,18 @@ class HDF5DataHandler(IDataHandler):
"""
raise NotImplementedError()
def _trades_load(self, pair: str, timerange: Optional[TimeRange] = None) -> pd.DataFrame:
def _trades_load(
self, pair: str, trading_mode: TradingMode, timerange: Optional[TimeRange] = None
) -> pd.DataFrame:
"""
Load a pair from h5 file.
:param pair: Load trades for this pair
:param trading_mode: Trading mode to use (used to determine the filename)
:param timerange: Timerange to load trades for - currently not implemented
:return: Dataframe containing trades
"""
key = self._pair_trades_key(pair)
filename = self._pair_trades_filename(self._datadir, pair)
filename = self._pair_trades_filename(self._datadir, pair, trading_mode)
if not filename.exists():
return pd.DataFrame(columns=DEFAULT_TRADES_COLUMNS)
@@ -172,12 +172,13 @@ class IDataHandler(ABC):
return [cls.rebuild_pair_from_filename(match[0]) for match in _tmp if match]
@abstractmethod
def _trades_store(self, pair: str, data: DataFrame) -> None:
def _trades_store(self, pair: str, data: DataFrame, trading_mode: TradingMode) -> None:
"""
Store trades data (list of Dicts) to file
:param pair: Pair - used for filename
:param data: Dataframe containing trades
column sequence as in DEFAULT_TRADES_COLUMNS
:param trading_mode: Trading mode to use (used to determine the filename)
"""
@abstractmethod
@@ -190,45 +191,55 @@ class IDataHandler(ABC):
"""
@abstractmethod
def _trades_load(self, pair: str, timerange: Optional[TimeRange] = None) -> DataFrame:
def _trades_load(
self, pair: str, trading_mode: TradingMode, timerange: Optional[TimeRange] = None
) -> DataFrame:
"""
Load a pair from file, either .json.gz or .json
:param pair: Load trades for this pair
:param trading_mode: Trading mode to use (used to determine the filename)
:param timerange: Timerange to load trades for - currently not implemented
:return: Dataframe containing trades
"""
def trades_store(self, pair: str, data: DataFrame) -> None:
def trades_store(self, pair: str, data: DataFrame, trading_mode: TradingMode) -> None:
"""
Store trades data (list of Dicts) to file
:param pair: Pair - used for filename
:param data: Dataframe containing trades
column sequence as in DEFAULT_TRADES_COLUMNS
:param trading_mode: Trading mode to use (used to determine the filename)
"""
# Filter on expected columns (will remove the actual date column).
self._trades_store(pair, data[DEFAULT_TRADES_COLUMNS])
self._trades_store(pair, data[DEFAULT_TRADES_COLUMNS], trading_mode)
def trades_purge(self, pair: str) -> bool:
def trades_purge(self, pair: str, trading_mode: TradingMode) -> bool:
"""
Remove data for this pair
:param pair: Delete data for this pair.
:param trading_mode: Trading mode to use (used to determine the filename)
:return: True when deleted, false if file did not exist.
"""
filename = self._pair_trades_filename(self._datadir, pair)
filename = self._pair_trades_filename(self._datadir, pair, trading_mode)
if filename.exists():
filename.unlink()
return True
return False
def trades_load(self, pair: str, timerange: Optional[TimeRange] = None) -> DataFrame:
def trades_load(
self, pair: str, trading_mode: TradingMode, timerange: Optional[TimeRange] = None
) -> DataFrame:
"""
Load a pair from file, either .json.gz or .json
Removes duplicates in the process.
:param pair: Load trades for this pair
:param trading_mode: Trading mode to use (used to determine the filename)
:param timerange: Timerange to load trades for - currently not implemented
:return: List of trades
"""
trades = trades_df_remove_duplicates(self._trades_load(pair, timerange=timerange))
trades = trades_df_remove_duplicates(
self._trades_load(pair, trading_mode, timerange=timerange)
)
trades = trades_convert_types(trades)
return trades
@@ -264,8 +275,12 @@ class IDataHandler(ABC):
return filename
@classmethod
def _pair_trades_filename(cls, datadir: Path, pair: str) -> Path:
def _pair_trades_filename(cls, datadir: Path, pair: str, trading_mode: TradingMode) -> Path:
pair_s = misc.pair_to_filename(pair)
if trading_mode == TradingMode.FUTURES:
# Futures pair ...
datadir = datadir.joinpath('futures')
filename = datadir.joinpath(f'{pair_s}-trades.{cls._get_file_extension()}')
return filename
@@ -8,7 +8,7 @@ from freqtrade import misc
from freqtrade.configuration import TimeRange
from freqtrade.constants import DEFAULT_DATAFRAME_COLUMNS, DEFAULT_TRADES_COLUMNS
from freqtrade.data.converter import trades_dict_to_list, trades_list_to_df
from freqtrade.enums import CandleType
from freqtrade.enums import CandleType, TradingMode
from .idatahandler import IDataHandler
@@ -37,7 +37,7 @@ class JsonDataHandler(IDataHandler):
self.create_dir_if_needed(filename)
_data = data.copy()
# Convert date to int
_data['date'] = _data['date'].view(np.int64) // 1000 // 1000
_data['date'] = _data['date'].astype(np.int64) // 1000 // 1000
# Reset index, select only appropriate columns and save as json
_data.reset_index(drop=True).loc[:, self._columns].to_json(
@@ -94,14 +94,15 @@ class JsonDataHandler(IDataHandler):
"""
raise NotImplementedError()
def _trades_store(self, pair: str, data: DataFrame) -> None:
def _trades_store(self, pair: str, data: DataFrame, trading_mode: TradingMode) -> None:
"""
Store trades data (list of Dicts) to file
:param pair: Pair - used for filename
:param data: Dataframe containing trades
column sequence as in DEFAULT_TRADES_COLUMNS
:param trading_mode: Trading mode to use (used to determine the filename)
"""
filename = self._pair_trades_filename(self._datadir, pair)
filename = self._pair_trades_filename(self._datadir, pair, trading_mode)
trades = data.values.tolist()
misc.file_dump_json(filename, trades, is_zip=self._use_zip)
@@ -114,15 +115,18 @@ class JsonDataHandler(IDataHandler):
"""
raise NotImplementedError()
def _trades_load(self, pair: str, timerange: Optional[TimeRange] = None) -> DataFrame:
def _trades_load(
self, pair: str, trading_mode: TradingMode, timerange: Optional[TimeRange] = None
) -> DataFrame:
"""
Load a pair from file, either .json.gz or .json
# TODO: respect timerange ...
:param pair: Load trades for this pair
:param trading_mode: Trading mode to use (used to determine the filename)
:param timerange: Timerange to load trades for - currently not implemented
:return: Dataframe containing trades
"""
filename = self._pair_trades_filename(self._datadir, pair)
filename = self._pair_trades_filename(self._datadir, pair, trading_mode)
tradesdata = misc.file_load_json(filename)
if not tradesdata:
@@ -4,8 +4,8 @@ from typing import Optional
from pandas import DataFrame, read_parquet, to_datetime
from freqtrade.configuration import TimeRange
from freqtrade.constants import DEFAULT_DATAFRAME_COLUMNS, DEFAULT_TRADES_COLUMNS, TradeList
from freqtrade.enums import CandleType
from freqtrade.constants import DEFAULT_DATAFRAME_COLUMNS, DEFAULT_TRADES_COLUMNS
from freqtrade.enums import CandleType, TradingMode
from .idatahandler import IDataHandler
@@ -81,14 +81,15 @@ class ParquetDataHandler(IDataHandler):
"""
raise NotImplementedError()
def _trades_store(self, pair: str, data: DataFrame) -> None:
def _trades_store(self, pair: str, data: DataFrame, trading_mode: TradingMode) -> None:
"""
Store trades data (list of Dicts) to file
:param pair: Pair - used for filename
:param data: Dataframe containing trades
column sequence as in DEFAULT_TRADES_COLUMNS
:param trading_mode: Trading mode to use (used to determine the filename)
"""
filename = self._pair_trades_filename(self._datadir, pair)
filename = self._pair_trades_filename(self._datadir, pair, trading_mode)
self.create_dir_if_needed(filename)
data.reset_index(drop=True).to_parquet(filename)
@@ -101,15 +102,18 @@ class ParquetDataHandler(IDataHandler):
"""
raise NotImplementedError()
def _trades_load(self, pair: str, timerange: Optional[TimeRange] = None) -> TradeList:
def _trades_load(
self, pair: str, trading_mode: TradingMode, timerange: Optional[TimeRange] = None
) -> DataFrame:
"""
Load a pair from file, either .json.gz or .json
# TODO: respect timerange ...
:param pair: Load trades for this pair
:param trading_mode: Trading mode to use (used to determine the filename)
:param timerange: Timerange to load trades for - currently not implemented
:return: List of trades
"""
filename = self._pair_trades_filename(self._datadir, pair)
filename = self._pair_trades_filename(self._datadir, pair, trading_mode)
if not filename.exists():
return DataFrame(columns=DEFAULT_TRADES_COLUMNS)
+17 -12
View File
@@ -12,8 +12,8 @@ from freqtrade.constants import (DATETIME_PRINT_FORMAT, DEFAULT_DATAFRAME_COLUMN
from freqtrade.data.converter import (clean_ohlcv_dataframe, convert_trades_to_ohlcv,
ohlcv_to_dataframe, trades_df_remove_duplicates,
trades_list_to_df)
from freqtrade.data.history.idatahandler import IDataHandler, get_datahandler
from freqtrade.enums import CandleType
from freqtrade.data.history.datahandlers import IDataHandler, get_datahandler
from freqtrade.enums import CandleType, TradingMode
from freqtrade.exceptions import OperationalException
from freqtrade.exchange import Exchange
from freqtrade.plugins.pairlist.pairlist_helpers import dynamic_expand_pairlist
@@ -333,7 +333,8 @@ def _download_trades_history(exchange: Exchange,
pair: str, *,
new_pairs_days: int = 30,
timerange: Optional[TimeRange] = None,
data_handler: IDataHandler
data_handler: IDataHandler,
trading_mode: TradingMode,
) -> bool:
"""
Download trade history from the exchange.
@@ -349,7 +350,7 @@ def _download_trades_history(exchange: Exchange,
if timerange.stoptype == 'date':
until = timerange.stopts * 1000
trades = data_handler.trades_load(pair)
trades = data_handler.trades_load(pair, trading_mode)
# TradesList columns are defined in constants.DEFAULT_TRADES_COLUMNS
# DEFAULT_TRADES_COLUMNS: 0 -> timestamp
@@ -388,7 +389,7 @@ def _download_trades_history(exchange: Exchange,
trades = concat([trades, new_trades_df], axis=0)
# Remove duplicates to make sure we're not storing data we don't need
trades = trades_df_remove_duplicates(trades)
data_handler.trades_store(pair, data=trades)
data_handler.trades_store(pair, trades, trading_mode)
logger.debug("New Start: %s", 'None' if trades.empty else
f"{trades.iloc[0]['date']:{DATETIME_PRINT_FORMAT}}")
@@ -405,8 +406,10 @@ def _download_trades_history(exchange: Exchange,
def refresh_backtest_trades_data(exchange: Exchange, pairs: List[str], datadir: Path,
timerange: TimeRange, new_pairs_days: int = 30,
erase: bool = False, data_format: str = 'feather') -> List[str]:
timerange: TimeRange, trading_mode: TradingMode,
new_pairs_days: int = 30,
erase: bool = False, data_format: str = 'feather',
) -> List[str]:
"""
Refresh stored trades data for backtesting and hyperopt operations.
Used by freqtrade download-data subcommand.
@@ -421,7 +424,7 @@ def refresh_backtest_trades_data(exchange: Exchange, pairs: List[str], datadir:
continue
if erase:
if data_handler.trades_purge(pair):
if data_handler.trades_purge(pair, trading_mode):
logger.info(f'Deleting existing data for pair {pair}.')
logger.info(f'Downloading trades for pair {pair}.')
@@ -429,7 +432,8 @@ def refresh_backtest_trades_data(exchange: Exchange, pairs: List[str], datadir:
pair=pair,
new_pairs_days=new_pairs_days,
timerange=timerange,
data_handler=data_handler)
data_handler=data_handler,
trading_mode=trading_mode)
return pairs_not_available
@@ -516,12 +520,12 @@ def download_data_main(config: Config) -> None:
# Start downloading
try:
if config.get('download_trades'):
if config.get('trading_mode') == 'futures':
raise OperationalException("Trade download not supported for futures.")
pairs_not_available = refresh_backtest_trades_data(
exchange, pairs=expanded_pairs, datadir=config['datadir'],
timerange=timerange, new_pairs_days=config['new_pairs_days'],
erase=bool(config.get('erase')), data_format=config['dataformat_trades'])
erase=bool(config.get('erase')), data_format=config['dataformat_trades'],
trading_mode=config.get('trading_mode', TradingMode.SPOT),
)
# Convert downloaded trade data to different timeframes
convert_trades_to_ohlcv(
@@ -529,6 +533,7 @@ def download_data_main(config: Config) -> None:
datadir=config['datadir'], timerange=timerange, erase=bool(config.get('erase')),
data_format_ohlcv=config['dataformat_ohlcv'],
data_format_trades=config['dataformat_trades'],
candle_type=config.get('candle_type_def', CandleType.SPOT),
)
else:
if not exchange.get_option('ohlcv_has_history', True):
+7 -2
View File
@@ -143,8 +143,10 @@ def calculate_max_drawdown(trades: pd.DataFrame, *, date_col: str = 'close_date'
starting_balance=starting_balance
)
idxmin = max_drawdown_df['drawdown_relative'].idxmax() if relative \
else max_drawdown_df['drawdown'].idxmin()
idxmin = (
max_drawdown_df['drawdown_relative'].idxmax()
if relative else max_drawdown_df['drawdown'].idxmin()
)
if idxmin == 0:
raise ValueError("No losing trade, therefore no drawdown.")
high_date = profit_results.loc[max_drawdown_df.iloc[:idxmin]['high_value'].idxmax(), date_col]
@@ -191,6 +193,9 @@ def calculate_cagr(days_passed: int, starting_balance: float, final_balance: flo
:param final_balance: Final balance to calculate CAGR against
:return: CAGR
"""
if final_balance < 0:
# With leveraged trades, final_balance can become negative.
return 0
return (final_balance / starting_balance) ** (1 / (days_passed / 365)) - 1
+6
View File
@@ -12,6 +12,12 @@ class OperationalException(FreqtradeException):
"""
class ConfigurationError(OperationalException):
"""
Configuration error. Usually caused by invalid configuration.
"""
class DependencyException(FreqtradeException):
"""
Indicates that an assumed dependency is not met.
+5 -3
View File
@@ -15,10 +15,12 @@ from freqtrade.exchange.exchange_utils import (ROUND_DOWN, ROUND_UP, amount_to_c
contracts_to_amount, date_minus_candles,
is_exchange_known_ccxt, list_available_exchanges,
market_is_active, price_to_precision,
timeframe_to_minutes, timeframe_to_msecs,
timeframe_to_next_date, timeframe_to_prev_date,
timeframe_to_resample_freq, timeframe_to_seconds,
validate_exchange)
from freqtrade.exchange.exchange_utils_timeframe import (timeframe_to_minutes, timeframe_to_msecs,
timeframe_to_next_date,
timeframe_to_prev_date,
timeframe_to_resample_freq,
timeframe_to_seconds)
from freqtrade.exchange.gate import Gate
from freqtrade.exchange.hitbtc import Hitbtc
from freqtrade.exchange.htx import Htx
File diff suppressed because it is too large Load Diff
+15 -3
View File
@@ -25,6 +25,7 @@ class Bybit(Exchange):
officially supported by the Freqtrade development team. So some features
may still not work as expected.
"""
unified_account = False
_ft_has: Dict = {
"ohlcv_candle_limit": 1000,
@@ -82,9 +83,20 @@ class Bybit(Exchange):
Must be overridden in child methods if required.
"""
try:
if self.trading_mode == TradingMode.FUTURES and not self._config['dry_run']:
position_mode = self._api.set_position_mode(False)
self._log_exchange_response('set_position_mode', position_mode)
if not self._config['dry_run']:
if self.trading_mode == TradingMode.FUTURES:
position_mode = self._api.set_position_mode(False)
self._log_exchange_response('set_position_mode', position_mode)
is_unified = self._api.is_unified_enabled()
# Returns a tuple of bools, first for margin, second for Account
if is_unified and len(is_unified) > 1 and is_unified[1]:
self.unified_account = True
logger.info("Bybit: Unified account.")
raise OperationalException("Bybit: Unified account is not supported. "
"Please use a standard (sub)account.")
else:
self.unified_account = False
logger.info("Bybit: Standard account.")
except ccxt.DDoSProtection as e:
raise DDosProtection(e) from e
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
+10 -8
View File
@@ -2,7 +2,7 @@ import asyncio
import logging
import time
from functools import wraps
from typing import Any, Callable, Optional, TypeVar, cast, overload
from typing import Any, Callable, Dict, List, Optional, TypeVar, cast, overload
from freqtrade.constants import ExchangeConfig
from freqtrade.exceptions import DDosProtection, RetryableOrderError, TemporaryError
@@ -60,16 +60,17 @@ SUPPORTED_EXCHANGES = [
'okx',
]
EXCHANGE_HAS_REQUIRED = [
# either the main, or replacement methods (array) is required
EXCHANGE_HAS_REQUIRED: Dict[str, List[str]] = {
# Required / private
'fetchOrder',
'cancelOrder',
'createOrder',
'fetchBalance',
'fetchOrder': ['fetchOpenOrder', 'fetchClosedOrder'],
'cancelOrder': [],
'createOrder': [],
'fetchBalance': [],
# Public endpoints
'fetchOHLCV',
]
'fetchOHLCV': [],
}
EXCHANGE_HAS_OPTIONAL = [
# Private
@@ -86,6 +87,7 @@ EXCHANGE_HAS_OPTIONAL = [
# 'fetchPositions', # Futures trading
# 'fetchLeverageTiers', # Futures initialization
# 'fetchMarketLeverageTiers', # Futures initialization
# 'fetchOpenOrder', 'fetchClosedOrder', # replacement for fetchOrder
# 'fetchOpenOrders', 'fetchClosedOrders', # 'fetchOrders', # Refinding balance...
]
+109 -30
View File
@@ -8,7 +8,7 @@ import logging
import signal
from copy import deepcopy
from datetime import datetime, timedelta, timezone
from math import floor
from math import floor, isnan
from threading import Lock
from typing import Any, Coroutine, Dict, List, Literal, Optional, Tuple, Union
@@ -23,26 +23,29 @@ from freqtrade.constants import (DEFAULT_AMOUNT_RESERVE_PERCENT, NON_OPEN_EXCHAN
BuySell, Config, EntryExit, ExchangeConfig,
ListPairsWithTimeframes, MakerTaker, OBLiteral, PairWithTimeframe)
from freqtrade.data.converter import clean_ohlcv_dataframe, ohlcv_to_dataframe, trades_dict_to_list
from freqtrade.enums import OPTIMIZE_MODES, CandleType, MarginMode, PriceType, TradingMode
from freqtrade.exceptions import (DDosProtection, ExchangeError, InsufficientFundsError,
InvalidOrderException, OperationalException, PricingError,
RetryableOrderError, TemporaryError)
from freqtrade.enums import OPTIMIZE_MODES, CandleType, MarginMode, PriceType, RunMode, TradingMode
from freqtrade.exceptions import (ConfigurationError, DDosProtection, ExchangeError,
InsufficientFundsError, InvalidOrderException,
OperationalException, PricingError, RetryableOrderError,
TemporaryError)
from freqtrade.exchange.common import (API_FETCH_ORDER_RETRY_COUNT, remove_exchange_credentials,
retrier, retrier_async)
from freqtrade.exchange.exchange_utils import (ROUND, ROUND_DOWN, ROUND_UP, CcxtModuleType,
amount_to_contract_precision, amount_to_contracts,
amount_to_precision, contracts_to_amount,
date_minus_candles, is_exchange_known_ccxt,
market_is_active, price_to_precision,
timeframe_to_minutes, timeframe_to_msecs,
timeframe_to_next_date, timeframe_to_prev_date,
timeframe_to_seconds)
market_is_active, price_to_precision)
from freqtrade.exchange.exchange_utils_timeframe import (timeframe_to_minutes, timeframe_to_msecs,
timeframe_to_next_date,
timeframe_to_prev_date,
timeframe_to_seconds)
from freqtrade.exchange.types import OHLCVResponse, OrderBook, Ticker, Tickers
from freqtrade.misc import (chunks, deep_merge_dicts, file_dump_json, file_load_json,
safe_value_fallback2)
from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist
from freqtrade.util import dt_from_ts, dt_now
from freqtrade.util.datetime_helpers import dt_humanize, dt_ts
from freqtrade.util.periodic_cache import PeriodicCache
logger = logging.getLogger(__name__)
@@ -86,6 +89,8 @@ class Exchange:
"order_props_in_contracts": ['amount', 'filled', 'remaining'],
# Override createMarketBuyOrderRequiresPrice where ccxt has it wrong
"marketOrderRequiresPrice": False,
"exchange_has_overrides": {}, # Dictionary overriding ccxt's "has".
# Expected to be in the format {"fetchOHLCV": True} or {"fetchOHLCV": False}
}
_ft_has: Dict = {}
_ft_has_futures: Dict = {}
@@ -131,6 +136,7 @@ class Exchange:
# Holds candles
self._klines: Dict[PairWithTimeframe, DataFrame] = {}
self._expiring_candle_cache: Dict[Tuple[str, int], PeriodicCache] = {}
# Holds all open sell orders for dry_run
self._dry_run_open_orders: Dict[str, Any] = {}
@@ -524,7 +530,7 @@ class Exchange:
)
quote_currencies = self.get_quote_currencies()
if stake_currency not in quote_currencies:
raise OperationalException(
raise ConfigurationError(
f"{stake_currency} is not available as stake on {self.name}. "
f"Available currencies are: {', '.join(quote_currencies)}")
@@ -592,11 +598,15 @@ class Exchange:
f"is therefore not supported. ccxt fetchOHLCV: {self.exchange_has('fetchOHLCV')}")
if timeframe and (timeframe not in self.timeframes):
raise OperationalException(
raise ConfigurationError(
f"Invalid timeframe '{timeframe}'. This exchange supports: {self.timeframes}")
if timeframe and timeframe_to_minutes(timeframe) < 1:
raise OperationalException("Timeframes < 1m are currently not supported by Freqtrade.")
if (
timeframe
and self._config['runmode'] != RunMode.UTIL_EXCHANGE
and timeframe_to_minutes(timeframe) < 1
):
raise ConfigurationError("Timeframes < 1m are currently not supported by Freqtrade.")
def validate_ordertypes(self, order_types: Dict) -> None:
"""
@@ -604,7 +614,7 @@ class Exchange:
"""
if any(v == 'market' for k, v in order_types.items()):
if not self.exchange_has('createMarketOrder'):
raise OperationalException(
raise ConfigurationError(
f'Exchange {self.name} does not support market orders.')
self.validate_stop_ordertypes(order_types)
@@ -614,7 +624,7 @@ class Exchange:
"""
if (order_types.get("stoploss_on_exchange")
and not self._ft_has.get("stoploss_on_exchange", False)):
raise OperationalException(
raise ConfigurationError(
f'On exchange stoploss is not supported for {self.name}.'
)
if self.trading_mode == TradingMode.FUTURES:
@@ -624,17 +634,17 @@ class Exchange:
and 'stoploss_price_type' in order_types
and order_types['stoploss_price_type'] not in price_mapping
):
raise OperationalException(
raise ConfigurationError(
f'On exchange stoploss price type is not supported for {self.name}.'
)
def validate_pricing(self, pricing: Dict) -> None:
if pricing.get('use_order_book', False) and not self.exchange_has('fetchL2OrderBook'):
raise OperationalException(f'Orderbook not available for {self.name}.')
raise ConfigurationError(f'Orderbook not available for {self.name}.')
if (not pricing.get('use_order_book', False) and (
not self.exchange_has('fetchTicker')
or not self._ft_has['tickers_have_price'])):
raise OperationalException(f'Ticker pricing not available for {self.name}.')
raise ConfigurationError(f'Ticker pricing not available for {self.name}.')
def validate_order_time_in_force(self, order_time_in_force: Dict) -> None:
"""
@@ -642,7 +652,7 @@ class Exchange:
"""
if any(v.upper() not in self._ft_has["order_time_in_force"]
for k, v in order_time_in_force.items()):
raise OperationalException(
raise ConfigurationError(
f'Time in force policies are not supported for {self.name} yet.')
def validate_required_startup_candles(self, startup_candles: int, timeframe: str) -> int:
@@ -653,7 +663,7 @@ class Exchange:
candle_limit = self.ohlcv_candle_limit(
timeframe, self._config['candle_type_def'],
int(date_minus_candles(timeframe, startup_candles).timestamp() * 1000)
dt_ts(date_minus_candles(timeframe, startup_candles))
if timeframe else None)
# Require one more candle - to account for the still open candle.
candle_count = startup_candles + 1
@@ -664,12 +674,12 @@ class Exchange:
if required_candle_call_count > 5:
# Only allow 5 calls per pair to somewhat limit the impact
raise OperationalException(
raise ConfigurationError(
f"This strategy requires {startup_candles} candles to start, "
"which is more than 5x "
f"the amount of candles {self.name} provides for {timeframe}.")
elif required_candle_call_count > 1:
raise OperationalException(
raise ConfigurationError(
f"This strategy requires {startup_candles} candles to start, which is more than "
f"the amount of candles {self.name} provides for {timeframe}.")
if required_candle_call_count > 1:
@@ -710,6 +720,8 @@ class Exchange:
:param endpoint: Name of endpoint (e.g. 'fetchOHLCV', 'fetchTickers')
:return: bool
"""
if endpoint in self._ft_has.get('exchange_has_overrides', {}):
return self._ft_has['exchange_has_overrides'][endpoint]
return endpoint in self._api.has and self._api.has[endpoint]
def get_precision_amount(self, pair: str) -> Optional[float]:
@@ -1238,7 +1250,7 @@ class Exchange:
f'Insufficient funds to create {ordertype} {side} order on market {pair}. '
f'Tried to {side} amount {amount} at rate {limit_rate} with '
f'stop-price {stop_price_norm}. Message: {e}') from e
except (ccxt.InvalidOrder, ccxt.BadRequest) as e:
except (ccxt.InvalidOrder, ccxt.BadRequest, ccxt.OperationRejected) as e:
# Errors:
# `Order would trigger immediately.`
raise InvalidOrderException(
@@ -1254,11 +1266,43 @@ class Exchange:
except ccxt.BaseError as e:
raise OperationalException(e) from e
def fetch_order_emulated(self, order_id: str, pair: str, params: Dict) -> Dict:
"""
Emulated fetch_order if the exchange doesn't support fetch_order, but requires separate
calls for open and closed orders.
"""
try:
order = self._api.fetch_open_order(order_id, pair, params=params)
self._log_exchange_response('fetch_open_order', order)
order = self._order_contracts_to_amount(order)
return order
except ccxt.OrderNotFound:
try:
order = self._api.fetch_closed_order(order_id, pair, params=params)
self._log_exchange_response('fetch_closed_order', order)
order = self._order_contracts_to_amount(order)
return order
except ccxt.OrderNotFound as e:
raise RetryableOrderError(
f'Order not found (pair: {pair} id: {order_id}). Message: {e}') from e
except ccxt.InvalidOrder as e:
raise InvalidOrderException(
f'Tried to get an invalid order (pair: {pair} id: {order_id}). Message: {e}') from e
except ccxt.DDoSProtection as e:
raise DDosProtection(e) from e
except (ccxt.NetworkError, 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
@retrier(retries=API_FETCH_ORDER_RETRY_COUNT)
def fetch_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict:
if self._config['dry_run']:
return self.fetch_dry_run_order(order_id)
try:
if not self.exchange_has('fetchOrder'):
return self.fetch_order_emulated(order_id, pair, params)
order = self._api.fetch_order(order_id, pair, params=params)
self._log_exchange_response('fetch_order', order)
order = self._order_contracts_to_amount(order)
@@ -2005,7 +2049,7 @@ class Exchange:
timeframe, candle_type, since_ms)
move_to = one_call * self.required_candle_call_count
now = timeframe_to_next_date(timeframe)
since_ms = int((now - timedelta(seconds=move_to // 1000)).timestamp() * 1000)
since_ms = dt_ts(now - timedelta(seconds=move_to // 1000))
if since_ms:
return self._async_get_historic_ohlcv(
@@ -2120,6 +2164,39 @@ class Exchange:
return results_df
def refresh_ohlcv_with_cache(
self,
pairs: List[PairWithTimeframe],
since_ms: int
) -> Dict[PairWithTimeframe, DataFrame]:
"""
Refresh ohlcv data for all pairs in needed_pairs if necessary.
Caches data with expiring per timeframe.
Should only be used for pairlists which need "on time" expirarion, and no longer cache.
"""
timeframes = {p[1] for p in pairs}
for timeframe in timeframes:
if (timeframe, since_ms) not in self._expiring_candle_cache:
timeframe_in_sec = timeframe_to_seconds(timeframe)
# Initialise cache
self._expiring_candle_cache[(timeframe, since_ms)] = PeriodicCache(
ttl=timeframe_in_sec, maxsize=1000)
# Get candles from cache
candles = {
c: self._expiring_candle_cache[(c[1], since_ms)].get(c, None) for c in pairs
if c in self._expiring_candle_cache[(c[1], since_ms)]
}
pairs_to_download = [p for p in pairs if p not in candles]
if pairs_to_download:
candles = self.refresh_latest_ohlcv(
pairs_to_download, since_ms=since_ms, cache=False
)
for c, val in candles.items():
self._expiring_candle_cache[(c[1], since_ms)][c] = val
return candles
def _now_is_time_to_refresh(self, pair: str, timeframe: str, candle_type: CandleType) -> bool:
# Timeframe in seconds
interval_in_sec = timeframe_to_seconds(timeframe)
@@ -2432,7 +2509,7 @@ class Exchange:
)
if type(since) is datetime:
since = int(since.timestamp()) * 1000 # * 1000 for ms
since = dt_ts(since)
try:
funding_history = self._api.fetch_funding_history(
@@ -2681,7 +2758,7 @@ class Exchange:
self._log_exchange_response('set_leverage', res)
except ccxt.DDoSProtection as e:
raise DDosProtection(e) from e
except (ccxt.BadRequest, ccxt.InsufficientFunds) as e:
except (ccxt.BadRequest, ccxt.OperationRejected, ccxt.InsufficientFunds) as e:
if not accept_fail:
raise TemporaryError(
f'Could not set leverage due to {e.__class__.__name__}. Message: {e}') from e
@@ -2723,7 +2800,7 @@ class Exchange:
self._log_exchange_response('set_margin_mode', res)
except ccxt.DDoSProtection as e:
raise DDosProtection(e) from e
except ccxt.BadRequest as e:
except (ccxt.BadRequest, ccxt.OperationRejected) as e:
if not accept_fail:
raise TemporaryError(
f'Could not set margin mode due to {e.__class__.__name__}. Message: {e}') from e
@@ -2762,7 +2839,7 @@ class Exchange:
if not close_date:
close_date = datetime.now(timezone.utc)
since_ms = int(timeframe_to_prev_date(timeframe, open_date).timestamp()) * 1000
since_ms = dt_ts(timeframe_to_prev_date(timeframe, open_date))
mark_comb: PairWithTimeframe = (pair, timeframe, mark_price_type)
funding_comb: PairWithTimeframe = (pair, timeframe_ff, CandleType.FUNDING_RATE)
@@ -2816,7 +2893,7 @@ class Exchange:
else:
# Fill up missing funding_rate candles with fallback value
combined = mark_rates.merge(
funding_rates, on='date', how="outer", suffixes=["_mark", "_fund"]
funding_rates, on='date', how="left", suffixes=["_mark", "_fund"]
)
combined['open_fund'] = combined['open_fund'].fillna(futures_funding_rate)
return combined
@@ -2845,7 +2922,8 @@ class Exchange:
if not df.empty:
df1 = df[(df['date'] >= open_date) & (df['date'] <= close_date)]
fees = sum(df1['open_fund'] * df1['open_mark'] * amount)
if isnan(fees):
fees = 0.0
# Negate fees for longs as funding_fees expects it this way based on live endpoints.
return fees if is_short else -fees
@@ -3021,3 +3099,4 @@ class Exchange:
# describes the min amt for a tier, and the lowest tier will always go down to 0
else:
raise ExchangeError(f"Cannot get maintenance ratio using {self.name}")
raise ExchangeError(f"Cannot get maintenance ratio using {self.name}")
+20 -79
View File
@@ -11,9 +11,9 @@ from ccxt import (DECIMAL_PLACES, ROUND, ROUND_DOWN, ROUND_UP, SIGNIFICANT_DIGIT
from freqtrade.exchange.common import (BAD_EXCHANGES, EXCHANGE_HAS_OPTIONAL, EXCHANGE_HAS_REQUIRED,
SUPPORTED_EXCHANGES)
from freqtrade.exchange.exchange_utils_timeframe import timeframe_to_minutes, timeframe_to_prev_date
from freqtrade.types import ValidExchangesType
from freqtrade.util import FtPrecise
from freqtrade.util.datetime_helpers import dt_from_ts, dt_ts
CcxtModuleType = Any
@@ -40,21 +40,34 @@ def available_exchanges(ccxt_module: Optional[CcxtModuleType] = None) -> List[st
def validate_exchange(exchange: str) -> Tuple[bool, str]:
"""
returns: can_use, reason
with Reason including both missing and missing_opt
"""
ex_mod = getattr(ccxt, exchange.lower())()
result = True
reason = ''
if not ex_mod or not ex_mod.has:
return False, ''
missing = [k for k in EXCHANGE_HAS_REQUIRED if ex_mod.has.get(k) is not True]
missing = [
k for k, v in EXCHANGE_HAS_REQUIRED.items()
if ex_mod.has.get(k) is not True
and not (all(ex_mod.has.get(x) for x in v))
]
if missing:
return False, f"missing: {', '.join(missing)}"
result = False
reason += f"missing: {', '.join(missing)}"
missing_opt = [k for k in EXCHANGE_HAS_OPTIONAL if not ex_mod.has.get(k)]
if exchange.lower() in BAD_EXCHANGES:
return False, BAD_EXCHANGES.get(exchange.lower(), '')
if missing_opt:
return True, f"missing opt: {', '.join(missing_opt)}"
result = False
reason = BAD_EXCHANGES.get(exchange.lower(), '')
return True, ''
if missing_opt:
reason += f"{'. ' if reason else ''}missing opt: {', '.join(missing_opt)}. "
return result, reason
def _build_exchange_list_entry(
@@ -95,78 +108,6 @@ def list_available_exchanges(all_exchanges: bool) -> List[ValidExchangesType]:
return exchanges_valid
def timeframe_to_seconds(timeframe: str) -> int:
"""
Translates the timeframe interval value written in the human readable
form ('1m', '5m', '1h', '1d', '1w', etc.) to the number
of seconds for one timeframe interval.
"""
return ccxt.Exchange.parse_timeframe(timeframe)
def timeframe_to_minutes(timeframe: str) -> int:
"""
Same as timeframe_to_seconds, but returns minutes.
"""
return ccxt.Exchange.parse_timeframe(timeframe) // 60
def timeframe_to_msecs(timeframe: str) -> int:
"""
Same as timeframe_to_seconds, but returns milliseconds.
"""
return ccxt.Exchange.parse_timeframe(timeframe) * 1000
def timeframe_to_resample_freq(timeframe: str) -> str:
"""
Translates the timeframe interval value written in the human readable
form ('1m', '5m', '1h', '1d', '1w', etc.) to the resample frequency
used by pandas ('1T', '5T', '1H', '1D', '1W', etc.)
"""
if timeframe == '1y':
return '1YS'
timeframe_seconds = timeframe_to_seconds(timeframe)
timeframe_minutes = timeframe_seconds // 60
resample_interval = f'{timeframe_seconds}s'
if 10000 < timeframe_minutes < 43200:
resample_interval = '1W-MON'
elif timeframe_minutes >= 43200 and timeframe_minutes < 525600:
# Monthly candles need special treatment to stick to the 1st of the month
resample_interval = f'{timeframe}S'
elif timeframe_minutes > 43200:
resample_interval = timeframe
return resample_interval
def timeframe_to_prev_date(timeframe: str, date: Optional[datetime] = None) -> datetime:
"""
Use Timeframe and determine the candle start date for this date.
Does not round when given a candle start date.
:param timeframe: timeframe in string format (e.g. "5m")
:param date: date to use. Defaults to now(utc)
:returns: date of previous candle (with utc timezone)
"""
if not date:
date = datetime.now(timezone.utc)
new_timestamp = ccxt.Exchange.round_timeframe(timeframe, dt_ts(date), ROUND_DOWN) // 1000
return dt_from_ts(new_timestamp)
def timeframe_to_next_date(timeframe: str, date: Optional[datetime] = None) -> datetime:
"""
Use Timeframe and determine next candle.
:param timeframe: timeframe in string format (e.g. "5m")
:param date: date to use. Defaults to now(utc)
:returns: date of next candle (with utc timezone)
"""
if not date:
date = datetime.now(timezone.utc)
new_timestamp = ccxt.Exchange.round_timeframe(timeframe, dt_ts(date), ROUND_UP) // 1000
return dt_from_ts(new_timestamp)
def date_minus_candles(
timeframe: str, candle_count: int, date: Optional[datetime] = None) -> datetime:
"""
@@ -0,0 +1,81 @@
from datetime import datetime, timezone
from typing import Optional
import ccxt
from ccxt import ROUND_DOWN, ROUND_UP
from freqtrade.util.datetime_helpers import dt_from_ts, dt_ts
def timeframe_to_seconds(timeframe: str) -> int:
"""
Translates the timeframe interval value written in the human readable
form ('1m', '5m', '1h', '1d', '1w', etc.) to the number
of seconds for one timeframe interval.
"""
return ccxt.Exchange.parse_timeframe(timeframe)
def timeframe_to_minutes(timeframe: str) -> int:
"""
Same as timeframe_to_seconds, but returns minutes.
"""
return ccxt.Exchange.parse_timeframe(timeframe) // 60
def timeframe_to_msecs(timeframe: str) -> int:
"""
Same as timeframe_to_seconds, but returns milliseconds.
"""
return ccxt.Exchange.parse_timeframe(timeframe) * 1000
def timeframe_to_resample_freq(timeframe: str) -> str:
"""
Translates the timeframe interval value written in the human readable
form ('1m', '5m', '1h', '1d', '1w', etc.) to the resample frequency
used by pandas ('1T', '5T', '1H', '1D', '1W', etc.)
"""
if timeframe == '1y':
return '1YS'
timeframe_seconds = timeframe_to_seconds(timeframe)
timeframe_minutes = timeframe_seconds // 60
resample_interval = f'{timeframe_seconds}s'
if 10000 < timeframe_minutes < 43200:
resample_interval = '1W-MON'
elif timeframe_minutes >= 43200 and timeframe_minutes < 525600:
# Monthly candles need special treatment to stick to the 1st of the month
resample_interval = f'{timeframe}S'
elif timeframe_minutes > 43200:
resample_interval = timeframe
return resample_interval
def timeframe_to_prev_date(timeframe: str, date: Optional[datetime] = None) -> datetime:
"""
Use Timeframe and determine the candle start date for this date.
Does not round when given a candle start date.
:param timeframe: timeframe in string format (e.g. "5m")
:param date: date to use. Defaults to now(utc)
:returns: date of previous candle (with utc timezone)
"""
if not date:
date = datetime.now(timezone.utc)
new_timestamp = ccxt.Exchange.round_timeframe(
timeframe, dt_ts(date), ROUND_DOWN) // 1000
return dt_from_ts(new_timestamp)
def timeframe_to_next_date(timeframe: str, date: Optional[datetime] = None) -> datetime:
"""
Use Timeframe and determine next candle.
:param timeframe: timeframe in string format (e.g. "5m")
:param date: date to use. Defaults to now(utc)
:returns: date of next candle (with utc timezone)
"""
if not date:
date = datetime.now(timezone.utc)
new_timestamp = ccxt.Exchange.round_timeframe(
timeframe, dt_ts(date), ROUND_UP) // 1000
return dt_from_ts(new_timestamp)
+13 -13
View File
@@ -96,9 +96,7 @@ class Gate(Exchange):
return trades
def get_order_id_conditional(self, order: Dict[str, Any]) -> str:
if self.trading_mode == TradingMode.FUTURES:
return safe_value_fallback2(order, order, 'id_stop', 'id')
return order['id']
return safe_value_fallback2(order, order, 'id_stop', 'id')
def fetch_stoploss_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict:
order = self.fetch_order(
@@ -106,17 +104,19 @@ class Gate(Exchange):
pair=pair,
params={'stop': True}
)
if self.trading_mode == TradingMode.FUTURES:
if order['status'] == 'closed':
# Places a real order - which we need to fetch explicitly.
new_orderid = order.get('info', {}).get('trade_id')
if new_orderid:
order1 = self.fetch_order(order_id=new_orderid, pair=pair, params=params)
order1['id_stop'] = order1['id']
order1['id'] = order_id
order1['stopPrice'] = order.get('stopPrice')
if order.get('status', 'open') == 'closed':
# Places a real order - which we need to fetch explicitly.
val = 'trade_id' if self.trading_mode == TradingMode.FUTURES else 'fired_order_id'
return order1
if new_orderid := order.get('info', {}).get(val):
order1 = self.fetch_order(order_id=new_orderid, pair=pair, params=params)
order1['id_stop'] = order1['id']
order1['id'] = order_id
order1['type'] = 'stoploss'
order1['stopPrice'] = order.get('stopPrice')
order1['status_stop'] = 'triggered'
return order1
return order
def cancel_stoploss_order(self, order_id: str, pair: str, params: Dict = {}) -> Dict:
+1 -1
View File
@@ -825,7 +825,7 @@ class IFreqaiModel(ABC):
"""
if self.config.get("freqai_backtest_live_models", False) and len_dataframe_backtest == 0:
logger.info(f"No data found for pair {pair} from "
f"from { tr_backtest.start_fmt} to {tr_backtest.stop_fmt}. "
f"from {tr_backtest.start_fmt} to {tr_backtest.stop_fmt}. "
"Probably more than one training within the same candle period.")
return False
return True
@@ -36,8 +36,15 @@ class XGBoostRegressor(BaseRegressionModel):
eval_set = None
eval_weights = None
else:
eval_set = [(data_dictionary["test_features"], data_dictionary["test_labels"])]
eval_weights = [data_dictionary['test_weights']]
eval_set = [
(data_dictionary["test_features"],
data_dictionary["test_labels"]),
(X, y)
]
eval_weights = [
data_dictionary['test_weights'],
data_dictionary['train_weights']
]
sample_weight = data_dictionary["train_weights"]
+4 -6
View File
@@ -43,13 +43,11 @@ class TensorBoardCallback(BaseTensorBoardCallback):
if not evals_log:
return False
for data, metric in evals_log.items():
for metric_name, log in metric.items():
evals = ["validation", "train"]
for metric, eval in zip(evals_log.items(), evals):
for metric_name, log in metric[1].items():
score = log[-1][0] if isinstance(log[-1], tuple) else log[-1]
if data == "train":
self.writer.add_scalar("train_loss", score, epoch)
else:
self.writer.add_scalar("valid_loss", score, epoch)
self.writer.add_scalar(f"{eval}-{metric_name}", score, epoch)
return False
@@ -152,7 +152,7 @@ class PyTorchModelTrainer(PyTorchTrainerInterface):
"""
assert isinstance(self.n_steps, int), "Either `n_steps` or `n_epochs` should be set."
n_batches = n_obs // self.batch_size
n_epochs = min(self.n_steps // n_batches, 1)
n_epochs = max(self.n_steps // n_batches, 1)
if n_epochs <= 10:
logger.warning(
f"Setting low n_epochs: {n_epochs}. "
+4 -2
View File
@@ -118,10 +118,12 @@ def plot_feature_importance(model: Any, pair: str, dk: FreqaiDataKitchen,
mdl = models[label]
if "catboost.core" in str(mdl.__class__):
feature_importance = mdl.get_feature_importance()
elif "lightgbm.sklearn" or "xgb" in str(mdl.__class__):
elif "lightgbm.sklearn" in str(mdl.__class__):
feature_importance = mdl.feature_importances_
elif "xgb" in str(mdl.__class__):
feature_importance = mdl.feature_importances_
else:
logger.info('Model type not support for generating feature importances.')
logger.info('Model type does not support generating feature importances.')
return
# Data preparation
+83 -72
View File
@@ -37,7 +37,6 @@ from freqtrade.rpc.rpc_types import (ProfitLossStr, RPCCancelMsg, RPCEntryMsg, R
RPCExitMsg, RPCProtectionMsg)
from freqtrade.strategy.interface import IStrategy
from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper
from freqtrade.util import FtPrecise
from freqtrade.util.migrations import migrate_binance_futures_names
from freqtrade.wallets import Wallets
@@ -82,7 +81,6 @@ class FreqtradeBot(LoggingMixin):
PairLocks.timeframe = self.config['timeframe']
self.pairlists = PairListManager(self.exchange, self.config)
self.trading_mode: TradingMode = self.config.get('trading_mode', TradingMode.SPOT)
self.last_process: Optional[datetime] = None
@@ -129,8 +127,9 @@ class FreqtradeBot(LoggingMixin):
self.update_funding_fees()
self.wallets.update()
# TODO: This would be more efficient if scheduled in utc time, and performed at each
# TODO: funding interval, specified by funding_fee_times on the exchange classes
# This would be more efficient if scheduled in utc time, and performed at each
# funding interval, specified by funding_fee_times on the exchange classes
# However, this reduces the precision - and might therefore lead to problems.
for time_slot in range(0, 24):
for minutes in [1, 31]:
t = str(time(time_slot, minutes, 2))
@@ -432,10 +431,6 @@ class FreqtradeBot(LoggingMixin):
try:
fo = self.exchange.fetch_order_or_stoploss_order(order.order_id, order.ft_pair,
order.ft_order_side == 'stoploss')
if order.ft_order_side == 'stoploss':
if fo and fo['status'] == 'open':
# Assume this as the open stoploss order
trade.stoploss_order_id = order.order_id
if fo:
logger.info(f"Found {order} for trade {trade}.")
self.update_trade_state(trade, order.order_id, fo,
@@ -645,8 +640,7 @@ class FreqtradeBot(LoggingMixin):
max_entry_stake = self.exchange.get_max_pair_stake_amount(trade.pair, current_entry_rate)
stake_available = self.wallets.get_available_stake_amount()
logger.debug(f"Calling adjust_trade_position for pair {trade.pair}")
stake_amount = strategy_safe_wrapper(self.strategy.adjust_trade_position,
default_retval=None, supress_error=True)(
stake_amount, order_tag = self.strategy._adjust_trade_position_internal(
trade=trade,
current_time=datetime.now(timezone.utc), current_rate=current_entry_rate,
current_profit=current_entry_profit, min_stake=min_entry_stake,
@@ -665,13 +659,14 @@ class FreqtradeBot(LoggingMixin):
else:
logger.debug("Max adjustment entries is set to unlimited.")
self.execute_entry(trade.pair, stake_amount, price=current_entry_rate,
trade=trade, is_short=trade.is_short, mode='pos_adjust')
trade=trade, is_short=trade.is_short, mode='pos_adjust',
enter_tag=order_tag)
if stake_amount is not None and stake_amount < 0.0:
# We should decrease our position
amount = self.exchange.amount_to_contract_precision(
trade.pair,
abs(float(FtPrecise(stake_amount * trade.leverage) / FtPrecise(current_exit_rate))))
abs(float(stake_amount * trade.amount / trade.stake_amount)))
if amount == 0.0:
logger.info("Amount to exit is 0.0 due to exchange limits - not exiting.")
@@ -684,7 +679,7 @@ class FreqtradeBot(LoggingMixin):
return
self.execute_trade_exit(trade, current_exit_rate, exit_check=ExitCheckTuple(
exit_type=ExitType.PARTIAL_EXIT), sub_trade_amt=amount)
exit_type=ExitType.PARTIAL_EXIT), sub_trade_amt=amount, exit_tag=order_tag)
def _check_depth_of_market(self, pair: str, conf: Dict, side: SignalDirection) -> bool:
"""
@@ -706,7 +701,7 @@ class FreqtradeBot(LoggingMixin):
delta = f"Delta: {bids_ask_delta}"
logger.info(
f"{bids}, {asks}, {delta}, Direction: {side.value}"
f"{bids}, {asks}, {delta}, Direction: {side.value} "
f"Bid Price: {order_book['bids'][0][0]}, Ask Price: {order_book['asks'][0][0]}, "
f"Immediate Bid Quantity: {order_book['bids'][0][1]}, "
f"Immediate Ask Quantity: {order_book['asks'][0][1]}."
@@ -782,6 +777,7 @@ class FreqtradeBot(LoggingMixin):
leverage=leverage
)
order_obj = Order.parse_from_ccxt_object(order, pair, side, amount, enter_limit_requested)
order_obj.ft_order_tag = enter_tag
order_id = order['id']
order_status = order.get('status')
logger.info(f"Order {order_id} was created for {pair} and status is {order_status}.")
@@ -894,17 +890,15 @@ class FreqtradeBot(LoggingMixin):
def cancel_stoploss_on_exchange(self, trade: Trade) -> Trade:
# First cancelling stoploss on exchange ...
if trade.stoploss_order_id:
for oslo in trade.open_sl_orders:
try:
logger.info(f"Cancelling stoploss on exchange for {trade}")
logger.info(f"Cancelling stoploss on exchange for {trade} "
f"order: {oslo.order_id}")
co = self.exchange.cancel_stoploss_order_with_result(
trade.stoploss_order_id, trade.pair, trade.amount)
self.update_trade_state(trade, trade.stoploss_order_id, co, stoploss_order=True)
# Reset stoploss order id.
trade.stoploss_order_id = None
oslo.order_id, trade.pair, trade.amount)
self.update_trade_state(trade, oslo.order_id, co, stoploss_order=True)
except InvalidOrderException:
logger.exception(f"Could not cancel stoploss order {trade.stoploss_order_id} "
logger.exception(f"Could not cancel stoploss order {oslo.order_id} "
f"for pair {trade.pair}")
return trade
@@ -967,7 +961,7 @@ class FreqtradeBot(LoggingMixin):
# edge-case for now.
min_stake_amount = self.exchange.get_min_pair_stake_amount(
pair, enter_limit_requested,
self.strategy.stoploss if not mode != 'pos_adjust' else 0.0,
self.strategy.stoploss if not mode == 'pos_adjust' else 0.0,
leverage)
max_stake_amount = self.exchange.get_max_pair_stake_amount(
pair, enter_limit_requested, leverage)
@@ -992,7 +986,7 @@ class FreqtradeBot(LoggingMixin):
return enter_limit_requested, stake_amount, leverage
def _notify_enter(self, trade: Trade, order: Order, order_type: str,
def _notify_enter(self, trade: Trade, order: Order, order_type: Optional[str],
fill: bool = False, sub_trade: bool = False) -> None:
"""
Sends rpc notification when a entry order occurred.
@@ -1016,7 +1010,7 @@ class FreqtradeBot(LoggingMixin):
'direction': 'Short' if trade.is_short else 'Long',
'limit': open_rate, # Deprecated (?)
'open_rate': open_rate,
'order_type': order_type,
'order_type': order_type or 'unknown',
'stake_amount': trade.stake_amount,
'stake_currency': self.config['stake_currency'],
'base_currency': self.exchange.get_pair_base_currency(trade.pair),
@@ -1079,7 +1073,7 @@ class FreqtradeBot(LoggingMixin):
if (
not trade.has_open_orders
and not trade.stoploss_order_id
and not trade.has_open_sl_orders
and not self.wallets.check_exit_amount(trade)
):
logger.warning(
@@ -1189,8 +1183,6 @@ class FreqtradeBot(LoggingMixin):
order_obj = Order.parse_from_ccxt_object(stoploss_order, trade.pair, 'stoploss',
trade.amount, stop_price)
trade.orders.append(order_obj)
trade.stoploss_order_id = str(stoploss_order['id'])
trade.stoploss_last_update = datetime.now(timezone.utc)
return True
except InsufficientFundsError as e:
logger.warning(f"Unable to place stoploss order {e}.")
@@ -1198,13 +1190,11 @@ class FreqtradeBot(LoggingMixin):
self.handle_insufficient_funds(trade)
except InvalidOrderException as e:
trade.stoploss_order_id = None
logger.error(f'Unable to place a stoploss order on exchange. {e}')
logger.warning('Exiting the trade forcefully')
self.emergency_exit(trade, stop_price)
except ExchangeError:
trade.stoploss_order_id = None
logger.exception('Unable to place a stoploss order on exchange.')
return False
@@ -1218,27 +1208,28 @@ class FreqtradeBot(LoggingMixin):
"""
logger.debug('Handling stoploss on exchange %s ...', trade)
stoploss_order = None
try:
# First we check if there is already a stoploss on exchange
stoploss_order = self.exchange.fetch_stoploss_order(
trade.stoploss_order_id, trade.pair) if trade.stoploss_order_id else None
except InvalidOrderException as exception:
logger.warning('Unable to fetch stoploss order: %s', exception)
stoploss_orders = []
for slo in trade.open_sl_orders:
stoploss_order = None
try:
# First we check if there is already a stoploss on exchange
stoploss_order = self.exchange.fetch_stoploss_order(
slo.order_id, trade.pair) if slo.order_id else None
except InvalidOrderException as exception:
logger.warning('Unable to fetch stoploss order: %s', exception)
if stoploss_order:
self.update_trade_state(trade, trade.stoploss_order_id, stoploss_order,
stoploss_order=True)
if stoploss_order:
stoploss_orders.append(stoploss_order)
self.update_trade_state(trade, slo.order_id, stoploss_order,
stoploss_order=True)
# We check if stoploss order is fulfilled
if stoploss_order and stoploss_order['status'] in ('closed', 'triggered'):
trade.exit_reason = ExitType.STOPLOSS_ON_EXCHANGE.value
self.update_trade_state(trade, trade.stoploss_order_id, stoploss_order,
stoploss_order=True)
self._notify_exit(trade, "stoploss", True)
self.handle_protections(trade.pair, trade.trade_direction)
return True
# We check if stoploss order is fulfilled
if stoploss_order and stoploss_order['status'] in ('closed', 'triggered'):
trade.exit_reason = ExitType.STOPLOSS_ON_EXCHANGE.value
self._notify_exit(trade, "stoploss", True)
self.handle_protections(trade.pair, trade.trade_direction)
return True
if trade.has_open_orders or not trade.is_open:
# Trade has an open Buy or Sell order, Stoploss-handling can't happen in this case
@@ -1247,7 +1238,7 @@ class FreqtradeBot(LoggingMixin):
return False
# If enter order is fulfilled but there is no stoploss, we add a stoploss on exchange
if not stoploss_order:
if len(stoploss_orders) == 0:
stop_price = trade.stoploss_or_liquidation
if self.edge:
stoploss = self.edge.get_stoploss(pair=trade.pair)
@@ -1261,27 +1252,7 @@ class FreqtradeBot(LoggingMixin):
# in which case the trade will be closed - which we must check below.
return False
# If stoploss order is canceled for some reason we add it again
if (trade.is_open
and stoploss_order
and stoploss_order['status'] in ('canceled', 'cancelled')):
if self.create_stoploss_order(trade=trade, stop_price=trade.stoploss_or_liquidation):
return False
else:
logger.warning('Stoploss order was cancelled, but unable to recreate one.')
# Finally we check if stoploss on exchange should be moved up because of trailing.
# Triggered Orders are now real orders - so don't replace stoploss anymore
if (
trade.is_open and stoploss_order
and stoploss_order.get('status_stop') != 'triggered'
and (self.config.get('trailing_stop', False)
or self.config.get('use_custom_stoploss', False))
):
# if trailing stoploss is enabled we check if stoploss value has changed
# in which case we cancel stoploss order and put another one with new
# value immediately
self.handle_trailing_stoploss_on_exchange(trade, stoploss_order)
self.manage_trade_stoploss_orders(trade, stoploss_orders)
return False
@@ -1317,6 +1288,42 @@ class FreqtradeBot(LoggingMixin):
logger.warning(f"Could not create trailing stoploss order "
f"for pair {trade.pair}.")
def manage_trade_stoploss_orders(self, trade: Trade, stoploss_orders: List[Dict]):
"""
Perform required actions acording to existing stoploss orders of trade
:param trade: Corresponding Trade
:param stoploss_orders: Current on exchange stoploss orders
:return: None
"""
# If all stoploss orderd are canceled for some reason we add it again
canceled_sl_orders = [o for o in stoploss_orders
if o['status'] in ('canceled', 'cancelled')]
if (
trade.is_open and
len(stoploss_orders) > 0 and
len(stoploss_orders) == len(canceled_sl_orders)
):
if self.create_stoploss_order(trade=trade, stop_price=trade.stoploss_or_liquidation):
return False
else:
logger.warning('All Stoploss orders are cancelled, but unable to recreate one.')
active_sl_orders = [o for o in stoploss_orders if o not in canceled_sl_orders]
if len(active_sl_orders) > 0:
last_active_sl_order = active_sl_orders[-1]
# Finally we check if stoploss on exchange should be moved up because of trailing.
# Triggered Orders are now real orders - so don't replace stoploss anymore
if (trade.is_open and
last_active_sl_order.get('status_stop') != 'triggered' and
(self.config.get('trailing_stop', False) or
self.config.get('use_custom_stoploss', False))):
# if trailing stoploss is enabled we check if stoploss value has changed
# in which case we cancel stoploss order and put another one with new
# value immediately
self.handle_trailing_stoploss_on_exchange(trade, last_active_sl_order)
return
def manage_open_orders(self) -> None:
"""
Management of open orders on exchange. Unfilled orders might be cancelled if timeout
@@ -1753,6 +1760,7 @@ class FreqtradeBot(LoggingMixin):
return False
order_obj = Order.parse_from_ccxt_object(order, trade.pair, trade.exit_side, amount, limit)
order_obj.ft_order_tag = exit_reason
trade.orders.append(order_obj)
trade.exit_order_status = ''
@@ -1767,7 +1775,7 @@ class FreqtradeBot(LoggingMixin):
return True
def _notify_exit(self, trade: Trade, order_type: str, fill: bool = False,
def _notify_exit(self, trade: Trade, order_type: Optional[str], fill: bool = False,
sub_trade: bool = False, order: Optional[Order] = None) -> None:
"""
Sends rpc notification when a sell occurred.
@@ -1799,7 +1807,7 @@ class FreqtradeBot(LoggingMixin):
'gain': gain,
'limit': order_rate, # Deprecated
'order_rate': order_rate,
'order_type': order_type,
'order_type': order_type or 'unknown',
'amount': amount,
'open_rate': trade.open_rate,
'close_rate': order_rate,
@@ -1936,6 +1944,9 @@ class FreqtradeBot(LoggingMixin):
def _update_trade_after_fill(self, trade: Trade, order: Order) -> Trade:
if order.status in constants.NON_OPEN_EXCHANGE_STATES:
strategy_safe_wrapper(
self.strategy.order_filled, default_retval=None)(
pair=trade.pair, trade=trade, order=order, current_time=datetime.now(timezone.utc))
# If a entry order was closed, force update on stoploss on exchange
if order.ft_order_side == trade.entry_side:
trade = self.cancel_stoploss_on_exchange(trade)
+6 -3
View File
@@ -7,8 +7,6 @@ import logging
import sys
from typing import Any, List, Optional
from freqtrade.util.gc_setup import gc_set_threshold
# check min. python version
if sys.version_info < (3, 9): # pragma: no cover
@@ -16,8 +14,10 @@ if sys.version_info < (3, 9): # pragma: no cover
from freqtrade import __version__
from freqtrade.commands import Arguments
from freqtrade.exceptions import FreqtradeException, OperationalException
from freqtrade.constants import DOCS_LINK
from freqtrade.exceptions import ConfigurationError, FreqtradeException, OperationalException
from freqtrade.loggers import setup_logging_pre
from freqtrade.util.gc_setup import gc_set_threshold
logger = logging.getLogger('freqtrade')
@@ -56,6 +56,9 @@ def main(sysargv: Optional[List[str]] = None) -> None:
except KeyboardInterrupt:
logger.info('SIGINT received, aborting ...')
return_code = 0
except ConfigurationError as e:
logger.error(f"Configuration error: {e}\n"
f"Please make sure to review the documentation at {DOCS_LINK}.")
except FreqtradeException as e:
logger.error(str(e))
return_code = 2
@@ -107,9 +107,9 @@ class LookaheadAnalysisSubFunctions:
csv_df = add_or_update_row(csv_df, new_row_data)
# Fill NaN values with a default value (e.g., 0)
csv_df['total_signals'] = csv_df['total_signals'].fillna(0)
csv_df['biased_entry_signals'] = csv_df['biased_entry_signals'].fillna(0)
csv_df['biased_exit_signals'] = csv_df['biased_exit_signals'].fillna(0)
csv_df['total_signals'] = csv_df['total_signals'].astype(int).fillna(0)
csv_df['biased_entry_signals'] = csv_df['biased_entry_signals'].astype(int).fillna(0)
csv_df['biased_exit_signals'] = csv_df['biased_exit_signals'].astype(int).fillna(0)
# Convert columns to integers
csv_df['total_signals'] = csv_df['total_signals'].astype(int)
@@ -121,14 +121,22 @@ class LookaheadAnalysisSubFunctions:
@staticmethod
def calculate_config_overrides(config: Config):
if config.get('enable_protections', False):
# if protections are used globally, they can produce false positives.
config['enable_protections'] = False
logger.info('Protections were enabled. '
'Disabling protections now '
'since they could otherwise produce false positives.')
if config['targeted_trade_amount'] < config['minimum_trade_amount']:
# this combo doesn't make any sense.
raise OperationalException(
"Targeted trade amount can't be smaller than minimum trade amount."
)
if len(config['pairs']) > config['max_open_trades']:
logger.info('Max_open_trades were less than amount of pairs. '
'Set max_open_trades to amount of pairs just to avoid false positives.')
if len(config['pairs']) > config.get('max_open_trades', 0):
logger.info('Max_open_trades were less than amount of pairs '
'or defined in the strategy. '
'Set max_open_trades to amount of pairs '
'just to avoid false positives.')
config['max_open_trades'] = len(config['pairs'])
min_dry_run_wallet = 1000000000
+38 -21
View File
@@ -23,7 +23,7 @@ from freqtrade.enums import (BacktestState, CandleType, ExitCheckTuple, ExitType
TradingMode)
from freqtrade.exceptions import DependencyException, OperationalException
from freqtrade.exchange import (amount_to_contract_precision, price_to_precision,
timeframe_to_minutes, timeframe_to_seconds)
timeframe_to_seconds)
from freqtrade.exchange.exchange import Exchange
from freqtrade.mixins import LoggingMixin
from freqtrade.optimize.backtest_caching import get_strategy_run_id
@@ -33,8 +33,8 @@ from freqtrade.optimize.optimize_reports import (generate_backtest_stats, genera
show_backtest_results,
store_backtest_analysis_results,
store_backtest_stats)
from freqtrade.persistence import (LocalTrade, Order, PairLocks, Trade, disable_database_use,
enable_database_use)
from freqtrade.persistence import (CustomDataWrapper, LocalTrade, Order, PairLocks, Trade,
disable_database_use, enable_database_use)
from freqtrade.plugins.pairlistmanager import PairListManager
from freqtrade.plugins.protectionmanager import ProtectionManager
from freqtrade.resolvers import ExchangeResolver, StrategyResolver
@@ -117,8 +117,9 @@ class Backtesting:
raise OperationalException("Timeframe needs to be set in either "
"configuration or as cli argument `--timeframe 5m`")
self.timeframe = str(self.config.get('timeframe'))
self.timeframe_min = timeframe_to_minutes(self.timeframe)
self.timeframe_td = timedelta(minutes=self.timeframe_min)
self.timeframe_secs = timeframe_to_seconds(self.timeframe)
self.timeframe_min = self.timeframe_secs // 60
self.timeframe_td = timedelta(seconds=self.timeframe_secs)
self.disable_database_use()
self.init_backtest_detail()
self.pairlists = PairListManager(self.exchange, self.config, self.dataprovider)
@@ -185,13 +186,14 @@ class Backtesting:
# Load detail timeframe if specified
self.timeframe_detail = str(self.config.get('timeframe_detail', ''))
if self.timeframe_detail:
self.timeframe_detail_min = timeframe_to_minutes(self.timeframe_detail)
if self.timeframe_min <= self.timeframe_detail_min:
timeframe_detail_secs = timeframe_to_seconds(self.timeframe_detail)
self.timeframe_detail_td = timedelta(seconds=timeframe_detail_secs)
if self.timeframe_secs <= timeframe_detail_secs:
raise OperationalException(
"Detail timeframe must be smaller than strategy timeframe.")
else:
self.timeframe_detail_min = 0
self.timeframe_detail_td = timedelta(seconds=0)
self.detail_data: Dict[str, DataFrame] = {}
self.futures_data: Dict[str, DataFrame] = {}
@@ -199,7 +201,7 @@ class Backtesting:
self.prepare_backtest(False)
self.wallets = Wallets(self.config, self.exchange, log=False)
self.wallets = Wallets(self.config, self.exchange, is_backtest=True)
self.progress = BTProgress()
self.abort = False
@@ -335,6 +337,7 @@ class Backtesting:
self.disable_database_use()
PairLocks.reset_locks()
Trade.reset_trades()
CustomDataWrapper.reset_custom_data()
self.rejected_trades = 0
self.timedout_entry_orders = 0
self.timedout_exit_orders = 0
@@ -537,14 +540,14 @@ class Backtesting:
min_stake = self.exchange.get_min_pair_stake_amount(trade.pair, current_rate, -0.1)
max_stake = self.exchange.get_max_pair_stake_amount(trade.pair, current_rate)
stake_available = self.wallets.get_available_stake_amount()
stake_amount = strategy_safe_wrapper(self.strategy.adjust_trade_position,
default_retval=None, supress_error=True)(
stake_amount, order_tag = self.strategy._adjust_trade_position_internal(
trade=trade, # type: ignore[arg-type]
current_time=current_time, current_rate=current_rate,
current_profit=current_profit, min_stake=min_stake,
max_stake=min(max_stake, stake_available),
current_entry_rate=current_rate, current_exit_rate=current_rate,
current_entry_profit=current_profit, current_exit_profit=current_profit)
current_entry_profit=current_profit, current_exit_profit=current_profit
)
# Check if we should increase our position
if stake_amount is not None and stake_amount > 0.0:
@@ -554,7 +557,8 @@ class Backtesting:
check_adjust_entry = (entry_count <= self.strategy.max_entry_position_adjustment)
if check_adjust_entry:
pos_trade = self._enter_trade(
trade.pair, row, 'short' if trade.is_short else 'long', stake_amount, trade)
trade.pair, row, 'short' if trade.is_short else 'long', stake_amount, trade,
entry_tag1=order_tag)
if pos_trade is not None:
self.wallets.update()
return pos_trade
@@ -569,7 +573,7 @@ class Backtesting:
if min_stake and remaining != 0 and remaining < min_stake:
# Remaining stake is too low to be sold.
return trade
exit_ = ExitCheckTuple(ExitType.PARTIAL_EXIT)
exit_ = ExitCheckTuple(ExitType.PARTIAL_EXIT, order_tag)
pos_trade = self._get_exit_for_signal(trade, row, exit_, current_time, amount)
if pos_trade is not None:
order = pos_trade.orders[-1]
@@ -599,6 +603,11 @@ class Backtesting:
if order and self._get_order_filled(order.ft_price, row):
order.close_bt_order(current_date, trade)
self._run_funding_fees(trade, current_date, force=True)
strategy_safe_wrapper(
self.strategy.order_filled,
default_retval=None)(
pair=trade.pair, trade=trade, # type: ignore[arg-type]
order=order, current_time=current_date)
if not (order.ft_order_side == trade.exit_side and order.safe_amount == trade.amount):
# trade is still open
@@ -681,11 +690,11 @@ class Backtesting:
trade.exit_reason = exit_reason
return self._exit_trade(trade, row, close_rate, amount_)
return self._exit_trade(trade, row, close_rate, amount_, exit_reason)
return None
def _exit_trade(self, trade: LocalTrade, sell_row: Tuple,
close_rate: float, amount: Optional[float] = None) -> Optional[LocalTrade]:
def _exit_trade(self, trade: LocalTrade, sell_row: Tuple, close_rate: float,
amount: float, exit_reason: Optional[str]) -> Optional[LocalTrade]:
self.order_id_counter += 1
exit_candle_time = sell_row[DATE_IDX].to_pydatetime()
order_type = self.strategy.order_types['exit']
@@ -712,6 +721,7 @@ class Backtesting:
filled=0,
remaining=amount,
cost=amount * close_rate,
ft_order_tag=exit_reason,
)
order._trade_bt = trade
trade.orders.append(order)
@@ -835,7 +845,9 @@ class Backtesting:
stake_amount: Optional[float] = None,
trade: Optional[LocalTrade] = None,
requested_rate: Optional[float] = None,
requested_stake: Optional[float] = None) -> Optional[LocalTrade]:
requested_stake: Optional[float] = None,
entry_tag1: Optional[str] = None
) -> Optional[LocalTrade]:
"""
:param trade: Trade to adjust - initial entry if None
:param requested_rate: Adjusted entry rate
@@ -843,7 +855,7 @@ class Backtesting:
"""
current_time = row[DATE_IDX].to_pydatetime()
entry_tag = row[ENTER_TAG_IDX] if len(row) >= ENTER_TAG_IDX + 1 else None
entry_tag = entry_tag1 or (row[ENTER_TAG_IDX] if len(row) >= ENTER_TAG_IDX + 1 else None)
# let's call the custom entry price, using the open price as default price
order_type = self.strategy.order_types['entry']
pos_adjust = trade is not None and requested_rate is None
@@ -875,6 +887,9 @@ class Backtesting:
precision_amount = self.exchange.get_precision_amount(pair)
amount = amount_to_contract_precision(amount_p, precision_amount, self.precision_mode,
contract_size)
if not amount:
# No amount left after truncating to precision.
return trade
# Backcalculate actual stake amount.
stake_amount = amount * propose_rate / leverage
@@ -944,6 +959,7 @@ class Backtesting:
filled=0,
remaining=amount,
cost=amount * propose_rate + trade.fee_open,
ft_order_tag=entry_tag,
)
order._trade_bt = trade
trade.orders.append(order)
@@ -963,7 +979,8 @@ class Backtesting:
# Ignore trade if entry-order did not fill yet
continue
exit_row = data[pair][-1]
self._exit_trade(trade, exit_row, exit_row[OPEN_IDX], trade.amount)
self._exit_trade(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()
@@ -1262,7 +1279,7 @@ class Backtesting:
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 += timedelta(minutes=self.timeframe_detail_min)
current_time_det += self.timeframe_detail_td
is_first = False
else:
self.dataprovider._set_dataframe_max_date(current_time)
@@ -16,7 +16,7 @@ def _get_line_floatfmt(stake_currency: str) -> List[str]:
"""
Generate floatformat (goes in line with _generate_result_line())
"""
return ['s', 'd', '.2f', '.2f', f'.{decimals_per_coin(stake_currency)}f',
return ['s', 'd', '.2f', f'.{decimals_per_coin(stake_currency)}f',
'.2f', 'd', 's', 's']
@@ -25,7 +25,7 @@ def _get_line_header(first_column: str, stake_currency: str,
"""
Generate header lines (goes in line with _generate_result_line())
"""
return [first_column, direction, 'Avg Profit %', 'Cum Profit %',
return [first_column, direction, 'Avg Profit %',
f'Tot Profit {stake_currency}', 'Tot Profit %', 'Avg Duration',
'Win Draw Loss Win%']
@@ -51,7 +51,7 @@ def text_table_bt_results(pair_results: List[Dict[str, Any]], stake_currency: st
headers = _get_line_header('Pair', stake_currency)
floatfmt = _get_line_floatfmt(stake_currency)
output = [[
t['key'], t['trades'], t['profit_mean_pct'], t['profit_sum_pct'], t['profit_total_abs'],
t['key'], t['trades'], t['profit_mean_pct'], t['profit_total_abs'],
t['profit_total_pct'], t['duration_avg'],
generate_wins_draws_losses(t['wins'], t['draws'], t['losses'])
] for t in pair_results]
@@ -72,7 +72,6 @@ def text_table_exit_reason(exit_reason_stats: List[Dict[str, Any]], stake_curren
'Exits',
'Win Draws Loss Win%',
'Avg Profit %',
'Cum Profit %',
f'Tot Profit {stake_currency}',
'Tot Profit %',
]
@@ -80,7 +79,7 @@ def text_table_exit_reason(exit_reason_stats: List[Dict[str, Any]], stake_curren
output = [[
t.get('exit_reason', t.get('sell_reason')), t['trades'],
generate_wins_draws_losses(t['wins'], t['draws'], t['losses']),
t['profit_mean_pct'], t['profit_sum_pct'],
t['profit_mean_pct'],
fmt_coin(t['profit_total_abs'], stake_currency, False),
t['profit_total_pct'],
] for t in exit_reason_stats]
@@ -105,7 +104,6 @@ def text_table_tags(tag_type: str, tag_results: List[Dict[str, Any]], stake_curr
t['key']) > 0 else "OTHER",
t['trades'],
t['profit_mean_pct'],
t['profit_sum_pct'],
t['profit_total_abs'],
t['profit_total_pct'],
t['duration_avg'],
@@ -166,7 +164,7 @@ def text_table_strategy(strategy_results, stake_currency: str) -> str:
for t, dd in zip(strategy_results, drawdown)]
output = [[
t['key'], t['trades'], t['profit_mean_pct'], t['profit_sum_pct'], t['profit_total_abs'],
t['key'], t['trades'], t['profit_mean_pct'], t['profit_total_abs'],
t['profit_total_pct'], t['duration_avg'],
generate_wins_draws_losses(t['wins'], t['draws'], t['losses']), drawdown]
for t, drawdown in zip(strategy_results, drawdown)]
@@ -256,9 +254,9 @@ def text_table_add_metrics(strat_results: Dict) -> str:
*short_metrics,
('', ''), # Empty line to improve readability
('Best Pair', f"{strat_results['best_pair']['key']} "
f"{strat_results['best_pair']['profit_sum']:.2%}"),
f"{strat_results['best_pair']['profit_total']:.2%}"),
('Worst Pair', f"{strat_results['worst_pair']['key']} "
f"{strat_results['worst_pair']['profit_sum']:.2%}"),
f"{strat_results['worst_pair']['profit_total']:.2%}"),
('Best trade', f"{best_trade['pair']} {best_trade['profit_ratio']:.2%}"),
('Worst trade', f"{worst_trade['pair']} "
f"{worst_trade['profit_ratio']:.2%}"),
@@ -215,7 +215,7 @@ def _get_resample_from_period(period: str) -> str:
# Weekly defaulting to Monday.
return '1W-MON'
if period == 'month':
return '1M'
return '1ME'
raise ValueError(f"Period {period} is not supported.")
+4 -2
View File
@@ -20,8 +20,10 @@ class SKDecimal(Integer):
super().__init__(_low, _high, prior, base, transform, name, dtype)
def __repr__(self):
return "Decimal(low={}, high={}, decimals={}, prior='{}', transform='{}')".format(
self.low_orig, self.high_orig, self.decimals, self.prior, self.transform_)
return (
f"Decimal(low={self.low_orig}, high={self.high_orig}, decimals={self.decimals}, "
f"prior='{self.prior}', transform='{self.transform_}')"
)
def __contains__(self, point):
if isinstance(point, list):
+1
View File
@@ -1,5 +1,6 @@
# flake8: noqa: F401
from freqtrade.persistence.custom_data import CustomDataWrapper
from freqtrade.persistence.key_value_store import KeyStoreKeys, KeyValueStore
from freqtrade.persistence.models import init_db
from freqtrade.persistence.pairlock_middleware import PairLocks
+174
View File
@@ -0,0 +1,174 @@
import json
import logging
from datetime import datetime
from typing import Any, ClassVar, List, Optional, Sequence
from sqlalchemy import DateTime, ForeignKey, Integer, String, Text, UniqueConstraint, select
from sqlalchemy.orm import Mapped, mapped_column, relationship
from freqtrade.constants import DATETIME_PRINT_FORMAT
from freqtrade.persistence.base import ModelBase, SessionType
from freqtrade.util import dt_now
logger = logging.getLogger(__name__)
class _CustomData(ModelBase):
"""
CustomData database model
Keeps records of metadata as key/value store
for trades or global persistant values
One to many relationship with Trades:
- One trade can have many metadata entries
- One metadata entry can only be associated with one Trade
"""
__tablename__ = 'trade_custom_data'
__allow_unmapped__ = True
session: ClassVar[SessionType]
# Uniqueness should be ensured over pair, order_id
# its likely that order_id is unique per Pair on some exchanges.
__table_args__ = (UniqueConstraint('ft_trade_id', 'cd_key', name="_trade_id_cd_key"),)
id = mapped_column(Integer, primary_key=True)
ft_trade_id = mapped_column(Integer, ForeignKey('trades.id'), index=True)
trade = relationship("Trade", back_populates="custom_data")
cd_key: Mapped[str] = mapped_column(String(255), nullable=False)
cd_type: Mapped[str] = mapped_column(String(25), nullable=False)
cd_value: Mapped[str] = mapped_column(Text, nullable=False)
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=dt_now)
updated_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
# Empty container value - not persisted, but filled with cd_value on query
value: Any = None
def __repr__(self):
create_time = (self.created_at.strftime(DATETIME_PRINT_FORMAT)
if self.created_at is not None else None)
update_time = (self.updated_at.strftime(DATETIME_PRINT_FORMAT)
if self.updated_at is not None else None)
return (f'CustomData(id={self.id}, key={self.cd_key}, type={self.cd_type}, ' +
f'value={self.cd_value}, trade_id={self.ft_trade_id}, created={create_time}, ' +
f'updated={update_time})')
@classmethod
def query_cd(cls, key: Optional[str] = None,
trade_id: Optional[int] = None) -> Sequence['_CustomData']:
"""
Get all CustomData, if trade_id is not specified
return will be for generic values not tied to a trade
:param trade_id: id of the Trade
"""
filters = []
if trade_id is not None:
filters.append(_CustomData.ft_trade_id == trade_id)
if key is not None:
filters.append(_CustomData.cd_key.ilike(key))
return _CustomData.session.scalars(select(_CustomData).filter(*filters)).all()
class CustomDataWrapper:
"""
CustomData middleware class
Abstracts the database layer away so it becomes optional - which will be necessary to support
backtesting and hyperopt in the future.
"""
use_db = True
custom_data: List[_CustomData] = []
unserialized_types = ['bool', 'float', 'int', 'str']
@staticmethod
def _convert_custom_data(data: _CustomData) -> _CustomData:
if data.cd_type in CustomDataWrapper.unserialized_types:
data.value = data.cd_value
if data.cd_type == 'bool':
data.value = data.cd_value.lower() == 'true'
elif data.cd_type == 'int':
data.value = int(data.cd_value)
elif data.cd_type == 'float':
data.value = float(data.cd_value)
else:
data.value = json.loads(data.cd_value)
return data
@staticmethod
def reset_custom_data() -> None:
"""
Resets all key-value pairs. Only active for backtesting mode.
"""
if not CustomDataWrapper.use_db:
CustomDataWrapper.custom_data = []
@staticmethod
def delete_custom_data(trade_id: int) -> None:
_CustomData.session.query(_CustomData).filter(_CustomData.ft_trade_id == trade_id).delete()
_CustomData.session.commit()
@staticmethod
def get_custom_data(*, trade_id: int, key: Optional[str] = None) -> List[_CustomData]:
if CustomDataWrapper.use_db:
filters = [
_CustomData.ft_trade_id == trade_id,
]
if key is not None:
filters.append(_CustomData.cd_key.ilike(key))
filtered_custom_data = _CustomData.session.scalars(select(_CustomData).filter(
*filters)).all()
else:
filtered_custom_data = [
data_entry for data_entry in CustomDataWrapper.custom_data
if (data_entry.ft_trade_id == trade_id)
]
if key is not None:
filtered_custom_data = [
data_entry for data_entry in filtered_custom_data
if (data_entry.cd_key.casefold() == key.casefold())
]
return [CustomDataWrapper._convert_custom_data(d) for d in filtered_custom_data]
@staticmethod
def set_custom_data(trade_id: int, key: str, value: Any) -> None:
value_type = type(value).__name__
if value_type not in CustomDataWrapper.unserialized_types:
try:
value_db = json.dumps(value)
except TypeError as e:
logger.warning(f"could not serialize {key} value due to {e}")
return
else:
value_db = str(value)
if trade_id is None:
trade_id = 0
custom_data = CustomDataWrapper.get_custom_data(trade_id=trade_id, key=key)
if custom_data:
data_entry = custom_data[0]
data_entry.cd_value = value_db
data_entry.updated_at = dt_now()
else:
data_entry = _CustomData(
ft_trade_id=trade_id,
cd_key=key,
cd_type=value_type,
cd_value=value_db,
created_at=dt_now(),
)
data_entry.value = value
if CustomDataWrapper.use_db and value_db is not None:
_CustomData.session.add(data_entry)
_CustomData.session.commit()
else:
if not custom_data:
CustomDataWrapper.custom_data.append(data_entry)
# Existing data will have updated interactively.
+10 -14
View File
@@ -1,7 +1,7 @@
import logging
from typing import List, Optional
from sqlalchemy import inspect, select, text, tuple_, update
from sqlalchemy import inspect, select, text, update
from freqtrade.exceptions import OperationalException
from freqtrade.persistence.trade_model import Order, Trade
@@ -91,8 +91,6 @@ def migrate_trades_and_orders_table(
is_stop_loss_trailing = get_column_def(
cols, 'is_stop_loss_trailing',
f'coalesce({stop_loss_pct}, 0.0) <> coalesce({initial_stop_loss_pct}, 0.0)')
stoploss_order_id = get_column_def(cols, 'stoploss_order_id', 'null')
stoploss_last_update = get_column_def(cols, 'stoploss_last_update', 'null')
max_rate = get_column_def(cols, 'max_rate', '0.0')
min_rate = get_column_def(cols, 'min_rate', 'null')
exit_reason = get_column_def(cols, 'sell_reason', get_column_def(cols, 'exit_reason', 'null'))
@@ -160,7 +158,7 @@ def migrate_trades_and_orders_table(
open_rate_requested, close_rate, close_rate_requested, close_profit,
stake_amount, amount, amount_requested, open_date, close_date,
stop_loss, stop_loss_pct, initial_stop_loss, initial_stop_loss_pct,
is_stop_loss_trailing, stoploss_order_id, stoploss_last_update,
is_stop_loss_trailing,
max_rate, min_rate, exit_reason, exit_order_status, strategy, enter_tag,
timeframe, open_trade_value, close_profit_abs,
trading_mode, leverage, liquidation_price, is_short,
@@ -180,7 +178,6 @@ def migrate_trades_and_orders_table(
{initial_stop_loss} initial_stop_loss,
{initial_stop_loss_pct} initial_stop_loss_pct,
{is_stop_loss_trailing} is_stop_loss_trailing,
{stoploss_order_id} stoploss_order_id, {stoploss_last_update} stoploss_last_update,
{max_rate} max_rate, {min_rate} min_rate,
case when {exit_reason} = 'sell_signal' then 'exit_signal'
when {exit_reason} = 'custom_sell' then 'custom_exit'
@@ -223,6 +220,7 @@ def migrate_orders_table(engine, table_back_name: str, cols_order: List):
ft_amount = get_column_def(cols_order, 'ft_amount', 'coalesce(amount, 0.0)')
ft_price = get_column_def(cols_order, 'ft_price', 'coalesce(price, 0.0)')
ft_cancel_reason = get_column_def(cols_order, 'ft_cancel_reason', 'null')
ft_order_tag = get_column_def(cols_order, 'ft_order_tag', 'null')
# sqlite does not support literals for booleans
with engine.begin() as connection:
@@ -230,13 +228,14 @@ def migrate_orders_table(engine, table_back_name: str, cols_order: List):
insert into orders (id, ft_trade_id, ft_order_side, ft_pair, ft_is_open, order_id,
status, symbol, order_type, side, price, amount, filled, average, remaining, cost,
stop_price, order_date, order_filled_date, order_update_date, ft_fee_base, funding_fee,
ft_amount, ft_price, ft_cancel_reason
ft_amount, ft_price, ft_cancel_reason, ft_order_tag
)
select id, ft_trade_id, ft_order_side, ft_pair, ft_is_open, order_id,
status, symbol, order_type, side, price, amount, filled, {average} average, remaining,
cost, {stop_price} stop_price, order_date, order_filled_date,
order_update_date, {ft_fee_base} ft_fee_base, {funding_fee} funding_fee,
{ft_amount} ft_amount, {ft_price} ft_price, {ft_cancel_reason} ft_cancel_reason
{ft_amount} ft_amount, {ft_price} ft_price, {ft_cancel_reason} ft_cancel_reason,
{ft_order_tag} ft_order_tag
from {table_back_name}
"""))
@@ -277,6 +276,8 @@ def fix_old_dry_orders(engine):
with engine.begin() as connection:
# Update current dry-run Orders where
# - stoploss order is Open (will be replaced eventually)
# 2nd query:
# - current Order is open
# - current Trade is closed
# - current Order trade_id not equal to current Trade.id
@@ -284,11 +285,6 @@ def fix_old_dry_orders(engine):
stmt = update(Order).where(
Order.ft_is_open.is_(True),
tuple_(Order.ft_trade_id, Order.order_id).not_in(
select(
Trade.id, Trade.stoploss_order_id
).where(Trade.stoploss_order_id.is_not(None))
),
Order.ft_order_side == 'stoploss',
Order.order_id.like('dry%'),
@@ -331,8 +327,8 @@ def check_migrate(engine, decl_base, previous_tables) -> None:
# if ('orders' not in previous_tables
# or not has_column(cols_orders, 'funding_fee')):
migrating = False
# if not has_column(cols_orders, 'ft_cancel_reason'):
if not has_column(cols_trades, 'funding_fee_running'):
# if not has_column(cols_trades, 'funding_fee_running'):
if not has_column(cols_orders, 'ft_order_tag'):
migrating = True
logger.info(f"Running database migration for trades - "
f"backup: {table_back_name}, {order_table_bak_name}")
+3
View File
@@ -13,6 +13,7 @@ from sqlalchemy.pool import StaticPool
from freqtrade.exceptions import OperationalException
from freqtrade.persistence.base import ModelBase
from freqtrade.persistence.custom_data import _CustomData
from freqtrade.persistence.key_value_store import _KeyValueStoreModel
from freqtrade.persistence.migrations import check_migrate
from freqtrade.persistence.pairlock import PairLock
@@ -78,6 +79,8 @@ def init_db(db_url: str) -> None:
Order.session = Trade.session
PairLock.session = Trade.session
_KeyValueStoreModel.session = Trade.session
_CustomData.session = scoped_session(sessionmaker(bind=engine, autoflush=True),
scopefunc=get_request_or_thread_id)
previous_tables = inspect(engine).get_table_names()
ModelBase.metadata.create_all(engine)
+115 -34
View File
@@ -23,7 +23,8 @@ from freqtrade.exchange import (ROUND_DOWN, ROUND_UP, amount_to_contract_precisi
from freqtrade.leverage import interest
from freqtrade.misc import safe_value_fallback
from freqtrade.persistence.base import ModelBase, SessionType
from freqtrade.util import FtPrecise, dt_from_ts, dt_now, dt_ts
from freqtrade.persistence.custom_data import CustomDataWrapper, _CustomData
from freqtrade.util import FtPrecise, dt_from_ts, dt_now, dt_ts, dt_ts_none
logger = logging.getLogger(__name__)
@@ -73,8 +74,7 @@ class Order(ModelBase):
order_id: Mapped[str] = mapped_column(String(255), nullable=False, index=True)
status: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
symbol: Mapped[Optional[str]] = mapped_column(String(25), nullable=True)
# TODO: type: order_type type is Optional[str]
order_type: Mapped[str] = mapped_column(String(50), nullable=True)
order_type: Mapped[Optional[str]] = mapped_column(String(50), nullable=True)
side: Mapped[str] = mapped_column(String(25), nullable=True)
price: Mapped[Optional[float]] = mapped_column(Float(), nullable=True)
average: Mapped[Optional[float]] = mapped_column(Float(), nullable=True)
@@ -89,6 +89,8 @@ class Order(ModelBase):
funding_fee: Mapped[Optional[float]] = mapped_column(Float(), nullable=True)
ft_fee_base: Mapped[Optional[float]] = mapped_column(Float(), nullable=True)
ft_order_tag: Mapped[Optional[str]] = mapped_column(String(CUSTOM_TAG_MAX_LENGTH),
nullable=True)
@property
def order_date_utc(self) -> datetime:
@@ -175,6 +177,8 @@ class Order(ModelBase):
order_date = safe_value_fallback(order, 'timestamp')
if order_date:
self.order_date = datetime.fromtimestamp(order_date / 1000, tz=timezone.utc)
elif not self.order_date:
self.order_date = dt_now()
self.ft_is_open = True
if self.status in NON_OPEN_EXCHANGE_STATES:
@@ -212,13 +216,17 @@ class Order(ModelBase):
return order
def to_json(self, entry_side: str, minified: bool = False) -> Dict[str, Any]:
"""
:param minified: If True, only return a subset of the data is returned.
Only used for backtesting.
"""
resp = {
'amount': self.safe_amount,
'safe_price': self.safe_price,
'ft_order_side': self.ft_order_side,
'order_filled_timestamp': int(self.order_filled_date.replace(
tzinfo=timezone.utc).timestamp() * 1000) if self.order_filled_date else None,
'order_filled_timestamp': dt_ts_none(self.order_filled_utc),
'ft_is_entry': self.ft_order_side == entry_side,
'ft_order_tag': self.ft_order_tag,
}
if not minified:
resp.update({
@@ -369,10 +377,6 @@ class LocalTrade:
# percentage value of the initial stop loss
initial_stop_loss_pct: Optional[float] = None
is_stop_loss_trailing: bool = False
# stoploss order id which is on exchange
stoploss_order_id: Optional[str] = None
# last update time of the stoploss order on exchange
stoploss_last_update: Optional[datetime] = None
# absolute value of the highest reached price
max_rate: Optional[float] = None
# Lowest price reached
@@ -456,14 +460,25 @@ class LocalTrade:
return self.open_date_utc
return max([self.open_date_utc, dt_last_filled])
@property
def date_entry_fill_utc(self) -> Optional[datetime]:
""" Date of the first filled order"""
orders = self.select_filled_orders(self.entry_side)
if (
orders
and len(filled_date := [o.order_filled_utc for o in orders if o.order_filled_utc])
):
return min(filled_date)
return None
@property
def open_date_utc(self):
return self.open_date.replace(tzinfo=timezone.utc)
@property
def stoploss_last_update_utc(self):
if self.stoploss_last_update:
return self.stoploss_last_update.replace(tzinfo=timezone.utc)
if self.has_open_sl_orders:
return max(o.order_date_utc for o in self.open_sl_orders)
return None
@property
@@ -519,7 +534,7 @@ class LocalTrade:
return [o for o in self.orders if o.ft_is_open and o.ft_order_side != 'stoploss']
@property
def has_open_orders(self) -> int:
def has_open_orders(self) -> bool:
"""
True if there are open orders for this trade excluding stoploss orders
"""
@@ -529,6 +544,37 @@ class LocalTrade:
]
return len(open_orders_wo_sl) > 0
@property
def open_sl_orders(self) -> List[Order]:
"""
All open stoploss orders for this trade
"""
return [
o for o in self.orders
if o.ft_order_side in ['stoploss'] and o.ft_is_open
]
@property
def has_open_sl_orders(self) -> bool:
"""
True if there are open stoploss orders for this trade
"""
open_sl_orders = [
o for o in self.orders
if o.ft_order_side in ['stoploss'] and o.ft_is_open
]
return len(open_sl_orders) > 0
@property
def sl_orders(self) -> List[Order]:
"""
All stoploss orders for this trade
"""
return [
o for o in self.orders
if o.ft_order_side in ['stoploss']
]
@property
def open_orders_ids(self) -> List[str]:
open_orders_ids_wo_sl = [
@@ -558,6 +604,11 @@ class LocalTrade:
)
def to_json(self, minified: bool = False) -> Dict[str, Any]:
"""
:param minified: If True, only return a subset of the data is returned.
Only used for backtesting.
:return: Dictionary with trade data
"""
filled_or_open_orders = self.select_filled_or_open_orders()
orders_json = [order.to_json(self.entry_side, minified) for order in filled_or_open_orders]
@@ -584,15 +635,17 @@ class LocalTrade:
'fee_close_currency': self.fee_close_currency,
'open_date': self.open_date.strftime(DATETIME_PRINT_FORMAT),
'open_timestamp': int(self.open_date.replace(tzinfo=timezone.utc).timestamp() * 1000),
'open_timestamp': dt_ts_none(self.open_date_utc),
'open_fill_date': (self.date_entry_fill_utc.strftime(DATETIME_PRINT_FORMAT)
if self.date_entry_fill_utc else None),
'open_fill_timestamp': dt_ts_none(self.date_entry_fill_utc),
'open_rate': self.open_rate,
'open_rate_requested': self.open_rate_requested,
'open_trade_value': round(self.open_trade_value, 8),
'close_date': (self.close_date.strftime(DATETIME_PRINT_FORMAT)
if self.close_date else None),
'close_timestamp': int(self.close_date.replace(
tzinfo=timezone.utc).timestamp() * 1000) if self.close_date else None,
'close_timestamp': dt_ts_none(self.close_date_utc),
'realized_profit': self.realized_profit or 0.0,
# Close-profit corresponds to relative realized_profit ratio
'realized_profit_ratio': self.close_profit or None,
@@ -616,11 +669,9 @@ class LocalTrade:
'stop_loss_abs': self.stop_loss,
'stop_loss_ratio': self.stop_loss_pct if self.stop_loss_pct else None,
'stop_loss_pct': (self.stop_loss_pct * 100) if self.stop_loss_pct else None,
'stoploss_order_id': self.stoploss_order_id,
'stoploss_last_update': (self.stoploss_last_update.strftime(DATETIME_PRINT_FORMAT)
if self.stoploss_last_update else None),
'stoploss_last_update_timestamp': int(self.stoploss_last_update.replace(
tzinfo=timezone.utc).timestamp() * 1000) if self.stoploss_last_update else None,
'stoploss_last_update': (self.stoploss_last_update_utc.strftime(DATETIME_PRINT_FORMAT)
if self.stoploss_last_update_utc else None),
'stoploss_last_update_timestamp': dt_ts_none(self.stoploss_last_update_utc),
'initial_stop_loss_abs': self.initial_stop_loss,
'initial_stop_loss_ratio': (self.initial_stop_loss_pct
if self.initial_stop_loss_pct else None),
@@ -764,6 +815,7 @@ class LocalTrade:
order.funding_fee = self.funding_fee_running
# Reset running funding fees
self.funding_fee_running = 0.0
order_type = order.order_type.upper() if order.order_type else None
if order.ft_order_side == self.entry_side:
# Update open rate and actual amount
@@ -771,21 +823,20 @@ class LocalTrade:
self.amount = order.safe_amount_after_fee
if self.is_open:
payment = "SELL" if self.is_short else "BUY"
logger.info(f'{order.order_type.upper()}_{payment} has been fulfilled for {self}.')
logger.info(f'{order_type}_{payment} has been fulfilled for {self}.')
self.recalc_trade_from_orders()
elif order.ft_order_side == self.exit_side:
if self.is_open:
payment = "BUY" if self.is_short else "SELL"
# * On margin shorts, you buy a little bit more than the amount (amount + interest)
logger.info(f'{order.order_type.upper()}_{payment} has been fulfilled for {self}.')
logger.info(f'{order_type}_{payment} has been fulfilled for {self}.')
elif order.ft_order_side == 'stoploss' and order.status not in ('open', ):
self.stoploss_order_id = None
self.close_rate_requested = self.stop_loss
self.exit_reason = ExitType.STOPLOSS_ON_EXCHANGE.value
if self.is_open and order.safe_filled > 0:
logger.info(f'{order.order_type.upper()} is hit for {self}.')
logger.info(f'{order_type} is hit for {self}.')
else:
raise ValueError(f'Unknown order type: {order.order_type}')
@@ -1164,6 +1215,40 @@ class LocalTrade:
or (o.ft_is_open is True and o.status is not None)
]
def set_custom_data(self, key: str, value: Any) -> None:
"""
Set custom data for this trade
:param key: key of the custom data
:param value: value of the custom data (must be JSON serializable)
"""
CustomDataWrapper.set_custom_data(trade_id=self.id, key=key, value=value)
def get_custom_data(self, key: str, default: Any = None) -> Any:
"""
Get custom data for this trade
:param key: key of the custom data
"""
data = CustomDataWrapper.get_custom_data(trade_id=self.id, key=key)
if data:
return data[0].value
return default
def get_custom_data_entry(self, key: str) -> Optional[_CustomData]:
"""
Get custom data for this trade
:param key: key of the custom data
"""
data = CustomDataWrapper.get_custom_data(trade_id=self.id, key=key)
if data:
return data[0]
return None
def get_all_custom_data(self) -> List[_CustomData]:
"""
Get all custom data for this trade
"""
return CustomDataWrapper.get_custom_data(trade_id=self.id)
@property
def nr_of_successful_entries(self) -> int:
"""
@@ -1358,11 +1443,6 @@ class LocalTrade:
exit_order_status=data["exit_order_status"],
stop_loss=data["stop_loss_abs"],
stop_loss_pct=data["stop_loss_ratio"],
stoploss_order_id=data["stoploss_order_id"],
stoploss_last_update=(
datetime.fromtimestamp(data["stoploss_last_update_timestamp"] // 1000,
tz=timezone.utc)
if data["stoploss_last_update_timestamp"] else None),
initial_stop_loss=data["initial_stop_loss_abs"],
initial_stop_loss_pct=data["initial_stop_loss_ratio"],
min_rate=data["min_rate"],
@@ -1400,6 +1480,7 @@ class LocalTrade:
ft_price=order["price"],
remaining=order["remaining"],
funding_fee=order.get("funding_fee", None),
ft_order_tag=order.get("ft_order_tag", None),
)
trade.orders.append(order_obj)
@@ -1423,6 +1504,9 @@ class Trade(ModelBase, LocalTrade):
orders: Mapped[List[Order]] = relationship(
"Order", order_by="Order.id", cascade="all, delete-orphan", lazy="selectin",
innerjoin=True) # type: ignore
custom_data: Mapped[List[_CustomData]] = relationship(
"_CustomData", cascade="all, delete-orphan",
lazy="raise")
exchange: Mapped[str] = mapped_column(String(25), nullable=False) # type: ignore
pair: Mapped[str] = mapped_column(String(25), nullable=False, index=True) # type: ignore
@@ -1468,11 +1552,6 @@ class Trade(ModelBase, LocalTrade):
Float(), nullable=True) # type: ignore
is_stop_loss_trailing: Mapped[bool] = mapped_column(
nullable=False, default=False) # type: ignore
# stoploss order id which is on exchange
stoploss_order_id: Mapped[Optional[str]] = mapped_column(
String(255), nullable=True, index=True) # type: ignore
# last update time of the stoploss order on exchange
stoploss_last_update: Mapped[Optional[datetime]] = mapped_column(nullable=True) # type: ignore
# absolute value of the highest reached price
max_rate: Mapped[Optional[float]] = mapped_column(
Float(), nullable=True, default=0.0) # type: ignore
@@ -1531,6 +1610,8 @@ class Trade(ModelBase, LocalTrade):
for order in self.orders:
Order.session.delete(order)
CustomDataWrapper.delete_custom_data(trade_id=self.id)
Trade.session.delete(self)
Trade.commit()
+3
View File
@@ -1,4 +1,5 @@
from freqtrade.persistence.custom_data import CustomDataWrapper
from freqtrade.persistence.pairlock_middleware import PairLocks
from freqtrade.persistence.trade_model import Trade
@@ -11,6 +12,7 @@ def disable_database_use(timeframe: str) -> None:
PairLocks.use_db = False
PairLocks.timeframe = timeframe
Trade.use_db = False
CustomDataWrapper.use_db = False
def enable_database_use() -> None:
@@ -20,6 +22,7 @@ def enable_database_use() -> None:
PairLocks.use_db = True
PairLocks.timeframe = ''
Trade.use_db = True
CustomDataWrapper.use_db = True
class FtNoDBContext:
@@ -0,0 +1,157 @@
"""
Market Cap PairList provider
Provides dynamic pair list based on Market Cap
"""
import logging
from typing import Any, Dict, List
from cachetools import TTLCache
from pycoingecko import CoinGeckoAPI
from freqtrade.constants import Config
from freqtrade.exceptions import OperationalException
from freqtrade.exchange.types import Tickers
from freqtrade.plugins.pairlist.IPairList import IPairList, PairlistParameter
logger = logging.getLogger(__name__)
class MarketCapPairList(IPairList):
is_pairlist_generator = True
def __init__(self, exchange, pairlistmanager,
config: Config, pairlistconfig: Dict[str, Any],
pairlist_pos: int) -> None:
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
if 'number_assets' not in self._pairlistconfig:
raise OperationalException(
'`number_assets` not specified. Please check your configuration '
'for "pairlist.config.number_assets"')
self._stake_currency = config['stake_currency']
self._number_assets = self._pairlistconfig['number_assets']
self._max_rank = self._pairlistconfig.get('max_rank', 30)
self._refresh_period = self._pairlistconfig.get('refresh_period', 86400)
self._marketcap_cache: TTLCache = TTLCache(maxsize=1, ttl=self._refresh_period)
self._def_candletype = self._config['candle_type_def']
self._coingekko: CoinGeckoAPI = CoinGeckoAPI()
if self._max_rank > 250:
raise OperationalException(
"This filter only support marketcap rank up to 250."
)
@property
def needstickers(self) -> bool:
"""
Boolean property defining if tickers are necessary.
If no Pairlist requires tickers, an empty Dict is passed
as tickers argument to filter_pairlist
"""
return False
def short_desc(self) -> str:
"""
Short whitelist method description - used for startup-messages
"""
num = self._number_assets
rank = self._max_rank
msg = f"{self.name} - {num} pairs placed within top {rank} market cap."
return msg
@staticmethod
def description() -> str:
return "Provides pair list based on CoinGecko's market cap rank."
@staticmethod
def available_parameters() -> Dict[str, PairlistParameter]:
return {
"number_assets": {
"type": "number",
"default": 30,
"description": "Number of assets",
"help": "Number of assets to use from the pairlist",
},
"max_rank": {
"type": "number",
"default": 30,
"description": "Max rank of assets",
"help": "Maximum rank of assets to use from the pairlist",
},
"refresh_period": {
"type": "number",
"default": 86400,
"description": "Refresh period",
"help": "Refresh period in seconds",
}
}
def gen_pairlist(self, tickers: Tickers) -> List[str]:
"""
Generate the pairlist
:param tickers: Tickers (from exchange.get_tickers). May be cached.
:return: List of pairs
"""
# Generate dynamic whitelist
# Must always run if this pairlist is the first in the list.
pairlist = self._marketcap_cache.get('pairlist_mc')
if pairlist:
# Item found - no refresh necessary
return pairlist.copy()
else:
# Use fresh pairlist
# Check if pair quote currency equals to the stake currency.
_pairlist = [k for k in self._exchange.get_markets(
quote_currencies=[self._stake_currency],
tradable_only=True, active_only=True).keys()]
# No point in testing for blacklisted pairs...
_pairlist = self.verify_blacklist(_pairlist, logger.info)
pairlist = self.filter_pairlist(_pairlist, tickers)
self._marketcap_cache['pairlist_mc'] = pairlist.copy()
return pairlist
def filter_pairlist(self, pairlist: List[str], tickers: Dict) -> List[str]:
"""
Filters and sorts pairlist and returns the whitelist again.
Called on each bot iteration - please use internal caching if necessary
:param pairlist: pairlist to filter or sort
:param tickers: Tickers (from exchange.get_tickers). May be cached.
:return: new whitelist
"""
marketcap_list = self._marketcap_cache.get('marketcap')
if marketcap_list is None:
data = self._coingekko.get_coins_markets(vs_currency='usd', order='market_cap_desc',
per_page='250', page='1', sparkline='false',
locale='en')
if data:
marketcap_list = [row['symbol'] for row in data]
self._marketcap_cache['marketcap'] = marketcap_list
if marketcap_list:
filtered_pairlist = []
market = self._config['trading_mode']
pair_format = f"{self._stake_currency.upper()}"
if (market == 'futures'):
pair_format += f":{self._stake_currency.upper()}"
top_marketcap = marketcap_list[:self._max_rank:]
for mc_pair in top_marketcap:
test_pair = f"{mc_pair.upper()}/{pair_format}"
if test_pair in pairlist:
filtered_pairlist.append(test_pair)
if len(filtered_pairlist) == self._number_assets:
break
if len(filtered_pairlist) > 0:
return filtered_pairlist
return pairlist
+58 -35
View File
@@ -3,7 +3,6 @@ Volatility pairlist filter
"""
import logging
import sys
from copy import deepcopy
from datetime import timedelta
from typing import Any, Dict, List, Optional
@@ -37,6 +36,7 @@ class VolatilityFilter(IPairList):
self._max_volatility = pairlistconfig.get('max_volatility', sys.maxsize)
self._refresh_period = pairlistconfig.get('refresh_period', 1440)
self._def_candletype = self._config['candle_type_def']
self._sort_direction: Optional[str] = pairlistconfig.get('sort_direction', None)
self._pair_cache: TTLCache = TTLCache(maxsize=1000, ttl=self._refresh_period)
@@ -46,6 +46,9 @@ class VolatilityFilter(IPairList):
if self._days > candle_limit:
raise OperationalException("VolatilityFilter requires lookback_days to not "
f"exceed exchange max request size ({candle_limit})")
if self._sort_direction not in [None, 'asc', 'desc']:
raise OperationalException("VolatilityFilter requires sort_direction to be "
"either None (undefined), 'asc' or 'desc'")
@property
def needstickers(self) -> bool:
@@ -89,6 +92,13 @@ class VolatilityFilter(IPairList):
"description": "Maximum Volatility",
"help": "Maximum volatility a pair must have to be considered.",
},
"sort_direction": {
"type": "option",
"default": None,
"options": ["", "asc", "desc"],
"description": "Sort pairlist",
"help": "Sort Pairlist ascending or descending by volatility.",
},
**IPairList.refresh_period_parameter()
}
@@ -103,50 +113,63 @@ class VolatilityFilter(IPairList):
(p, '1d', self._def_candletype) for p in pairlist if p not in self._pair_cache]
since_ms = dt_ts(dt_floor_day(dt_now()) - timedelta(days=self._days))
# Get all candles
candles = {}
if needed_pairs:
candles = self._exchange.refresh_latest_ohlcv(needed_pairs, since_ms=since_ms,
cache=False)
candles = self._exchange.refresh_ohlcv_with_cache(needed_pairs, since_ms=since_ms)
if self._enabled:
for p in deepcopy(pairlist):
daily_candles = candles[(p, '1d', self._def_candletype)] if (
p, '1d', self._def_candletype) in candles else None
if not self._validate_pair_loc(p, daily_candles):
pairlist.remove(p)
return pairlist
resulting_pairlist: List[str] = []
volatilitys: Dict[str, float] = {}
for p in pairlist:
daily_candles = candles.get((p, '1d', self._def_candletype), None)
def _validate_pair_loc(self, pair: str, daily_candles: Optional[DataFrame]) -> bool:
"""
Validate trading range
:param pair: Pair that's currently validated
:param daily_candles: Downloaded daily candles
:return: True if the pair can stay, false if it should be removed
"""
volatility_avg = self._calculate_volatility(p, daily_candles)
if volatility_avg is not None:
if self._validate_pair_loc(p, volatility_avg):
resulting_pairlist.append(p)
volatilitys[p] = (
volatility_avg if volatility_avg and not np.isnan(volatility_avg) else 0
)
else:
self.log_once(f"Removed {p} from whitelist, no candles found.", logger.info)
if self._sort_direction:
resulting_pairlist = sorted(resulting_pairlist,
key=lambda p: volatilitys[p],
reverse=self._sort_direction == 'desc')
return resulting_pairlist
def _calculate_volatility(self, pair: str, daily_candles: DataFrame) -> Optional[float]:
# Check symbol in cache
cached_res = self._pair_cache.get(pair, None)
if cached_res is not None:
return cached_res
if (volatility_avg := self._pair_cache.get(pair, None)) is not None:
return volatility_avg
result = False
if daily_candles is not None and not daily_candles.empty:
returns = (np.log(daily_candles["close"].shift(1) / daily_candles["close"]))
returns.fillna(0, inplace=True)
volatility_series = returns.rolling(window=self._days).std() * np.sqrt(self._days)
volatility_avg = volatility_series.mean()
self._pair_cache[pair] = volatility_avg
if self._min_volatility <= volatility_avg <= self._max_volatility:
result = True
else:
self.log_once(f"Removed {pair} from whitelist, because volatility "
f"over {self._days} {plural(self._days, 'day')} "
f"is: {volatility_avg:.3f} "
f"which is not in the configured range of "
f"{self._min_volatility}-{self._max_volatility}.",
logger.info)
result = False
self._pair_cache[pair] = result
return volatility_avg
else:
return None
def _validate_pair_loc(self, pair: str, volatility_avg: float) -> bool:
"""
Validate trading range
:param pair: Pair that's currently validated
:param volatility_avg: Average volatility
:return: True if the pair can stay, false if it should be removed
"""
if self._min_volatility <= volatility_avg <= self._max_volatility:
result = True
else:
self.log_once(f"Removed {pair} from whitelist, because volatility "
f"over {self._days} {plural(self._days, 'day')} "
f"is: {volatility_avg:.3f} "
f"which is not in the configured range of "
f"{self._min_volatility}-{self._max_volatility}.",
logger.info)
result = False
return result
+12 -6
View File
@@ -41,6 +41,7 @@ class VolumePairList(IPairList):
self._number_pairs = self._pairlistconfig['number_assets']
self._sort_key: Literal['quoteVolume'] = self._pairlistconfig.get('sort_key', 'quoteVolume')
self._min_value = self._pairlistconfig.get('min_value', 0)
self._max_value = self._pairlistconfig.get("max_value", None)
self._refresh_period = self._pairlistconfig.get('refresh_period', 1800)
self._pair_cache: TTLCache = TTLCache(maxsize=1, ttl=self._refresh_period)
self._lookback_days = self._pairlistconfig.get('lookback_days', 0)
@@ -139,6 +140,12 @@ class VolumePairList(IPairList):
"description": "Minimum value",
"help": "Minimum value to use for filtering the pairlist.",
},
"max_value": {
"type": "number",
"default": None,
"description": "Maximum value",
"help": "Maximum value to use for filtering the pairlist.",
},
**IPairList.refresh_period_parameter(),
"lookback_days": {
"type": "number",
@@ -229,12 +236,8 @@ class VolumePairList(IPairList):
if p not in self._pair_cache
]
# Get all candles
candles = {}
if needed_pairs:
candles = self._exchange.refresh_latest_ohlcv(
needed_pairs, since_ms=since_ms, cache=False
)
candles = self._exchange.refresh_ohlcv_with_cache(needed_pairs, since_ms)
for i, p in enumerate(filtered_tickers):
contract_size = self._exchange.markets[p['symbol']].get('contractSize', 1.0) or 1.0
pair_candles = candles[
@@ -274,6 +277,9 @@ class VolumePairList(IPairList):
if self._min_value > 0:
filtered_tickers = [
v for v in filtered_tickers if v[self._sort_key] > self._min_value]
if self._max_value is not None:
filtered_tickers = [
v for v in filtered_tickers if v[self._sort_key] < self._max_value]
sorted_tickers = sorted(filtered_tickers, reverse=True, key=lambda t: t[self._sort_key])
@@ -2,7 +2,6 @@
Rate of change pairlist filter
"""
import logging
from copy import deepcopy
from datetime import timedelta
from typing import Any, Dict, List, Optional
@@ -32,6 +31,7 @@ class RangeStabilityFilter(IPairList):
self._max_rate_of_change = pairlistconfig.get('max_rate_of_change')
self._refresh_period = pairlistconfig.get('refresh_period', 86400)
self._def_candletype = self._config['candle_type_def']
self._sort_direction: Optional[str] = pairlistconfig.get('sort_direction', None)
self._pair_cache: TTLCache = TTLCache(maxsize=1000, ttl=self._refresh_period)
@@ -41,6 +41,9 @@ class RangeStabilityFilter(IPairList):
if self._days > candle_limit:
raise OperationalException("RangeStabilityFilter requires lookback_days to not "
f"exceed exchange max request size ({candle_limit})")
if self._sort_direction not in [None, 'asc', 'desc']:
raise OperationalException("RangeStabilityFilter requires sort_direction to be "
"either None (undefined), 'asc' or 'desc'")
@property
def needstickers(self) -> bool:
@@ -87,6 +90,13 @@ class RangeStabilityFilter(IPairList):
"description": "Maximum Rate of Change",
"help": "Maximum rate of change to filter pairs.",
},
"sort_direction": {
"type": "option",
"default": None,
"options": ["", "asc", "desc"],
"description": "Sort pairlist",
"help": "Sort Pairlist ascending or descending by rate of change.",
},
**IPairList.refresh_period_parameter()
}
@@ -100,53 +110,65 @@ class RangeStabilityFilter(IPairList):
needed_pairs: ListPairsWithTimeframes = [
(p, '1d', self._def_candletype) for p in pairlist if p not in self._pair_cache]
since_ms = dt_ts(dt_floor_day(dt_now()) - timedelta(days=self._days - 1))
# Get all candles
candles = {}
if needed_pairs:
candles = self._exchange.refresh_latest_ohlcv(needed_pairs, since_ms=since_ms,
cache=False)
since_ms = dt_ts(dt_floor_day(dt_now()) - timedelta(days=self._days + 1))
candles = self._exchange.refresh_ohlcv_with_cache(needed_pairs, since_ms=since_ms)
if self._enabled:
for p in deepcopy(pairlist):
daily_candles = candles[(p, '1d', self._def_candletype)] if (
p, '1d', self._def_candletype) in candles else None
if not self._validate_pair_loc(p, daily_candles):
pairlist.remove(p)
return pairlist
resulting_pairlist: List[str] = []
pct_changes: Dict[str, float] = {}
def _validate_pair_loc(self, pair: str, daily_candles: Optional[DataFrame]) -> bool:
"""
Validate trading range
:param pair: Pair that's currently validated
:param daily_candles: Downloaded daily candles
:return: True if the pair can stay, false if it should be removed
"""
for p in pairlist:
daily_candles = candles.get((p, '1d', self._def_candletype), None)
pct_change = self._calculate_rate_of_change(p, daily_candles)
if pct_change is not None:
if self._validate_pair_loc(p, pct_change):
resulting_pairlist.append(p)
pct_changes[p] = pct_change
else:
self.log_once(f"Removed {p} from whitelist, no candles found.", logger.info)
if self._sort_direction:
resulting_pairlist = sorted(resulting_pairlist,
key=lambda p: pct_changes[p],
reverse=self._sort_direction == 'desc')
return resulting_pairlist
def _calculate_rate_of_change(self, pair: str, daily_candles: DataFrame) -> Optional[float]:
# Check symbol in cache
cached_res = self._pair_cache.get(pair, None)
if cached_res is not None:
return cached_res
result = True
if (pct_change := self._pair_cache.get(pair, None)) is not None:
return pct_change
if daily_candles is not None and not daily_candles.empty:
highest_high = daily_candles['high'].max()
lowest_low = daily_candles['low'].min()
pct_change = ((highest_high - lowest_low) / lowest_low) if lowest_low > 0 else 0
if pct_change < self._min_rate_of_change:
self.log_once(f"Removed {pair} from whitelist, because rate of change "
f"over {self._days} {plural(self._days, 'day')} is {pct_change:.3f}, "
f"which is below the threshold of {self._min_rate_of_change}.",
logger.info)
result = False
if self._max_rate_of_change:
if pct_change > self._max_rate_of_change:
self.log_once(
f"Removed {pair} from whitelist, because rate of change "
f"over {self._days} {plural(self._days, 'day')} is {pct_change:.3f}, "
f"which is above the threshold of {self._max_rate_of_change}.",
logger.info)
result = False
self._pair_cache[pair] = result
self._pair_cache[pair] = pct_change
return pct_change
else:
self.log_once(f"Removed {pair} from whitelist, no candles found.", logger.info)
return None
def _validate_pair_loc(self, pair: str, pct_change: float) -> bool:
"""
Validate trading range
:param pair: Pair that's currently validated
:param pct_change: Rate of change
:return: True if the pair can stay, false if it should be removed
"""
result = True
if pct_change < self._min_rate_of_change:
self.log_once(f"Removed {pair} from whitelist, because rate of change "
f"over {self._days} {plural(self._days, 'day')} is {pct_change:.3f}, "
f"which is below the threshold of {self._min_rate_of_change}.",
logger.info)
result = False
if self._max_rate_of_change:
if pct_change > self._max_rate_of_change:
self.log_once(
f"Removed {pair} from whitelist, because rate of change "
f"over {self._days} {plural(self._days, 'day')} is {pct_change:.3f}, "
f"which is above the threshold of {self._max_rate_of_change}.",
logger.info)
result = False
return result

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