Compare commits

..

624 Commits

Author SHA1 Message Date
Matthias
d6482066ef Merge pull request #6348 from freqtrade/new_release
New release 2022.1
2022-02-03 12:29:04 +01:00
Matthias
2d45163f8f Bump version to 2022.1 2022-02-02 19:46:48 +01:00
Matthias
e95fb7ef3e Merge branch 'stable' into new_release 2022-02-02 19:46:33 +01:00
Matthias
0058abcc2d Fix crash when no min-stake-amount could be determined 2022-02-02 12:20:05 +01:00
Matthias
5aa683006c Merge pull request #6337 from TheJoeSchr/patch-8
docs: fix typo and fix markdown list rendering
2022-02-01 20:45:06 +01:00
Matthias
64d0c75bbb Fix deprecation warnings 2022-02-01 19:11:51 +01:00
Matthias
d96a354a3e Version bump ccxt to 1.71.73
closes #6339
2022-02-01 18:50:22 +01:00
Matthias
479b560549 Add missing entry_tag argument to custom_entry_tag documentation 2022-02-01 06:15:57 +01:00
TheJoeSchr
1a838680e7 docs: fix typo and fix markdown list rendering 2022-01-31 20:23:02 +01:00
Matthias
2c492abc1e Merge pull request #6336 from TheJoeSchr/patch-7
Docs: fix typo in strategy-callbacks.md
2022-01-31 20:22:12 +01:00
TheJoeSchr
f35c6545c1 Update strategy-callbacks.md
fix typo
2022-01-31 20:04:19 +01:00
Matthias
b3e36def34 Merge pull request #6329 from freqtrade/dependabot/pip/develop/prompt-toolkit-3.0.26
Bump prompt-toolkit from 3.0.24 to 3.0.26
2022-01-31 07:00:42 +01:00
Matthias
c53122ed02 Merge pull request #6323 from freqtrade/dependabot/pip/develop/mkdocs-material-8.1.9
Bump mkdocs-material from 8.1.8 to 8.1.9
2022-01-31 07:00:28 +01:00
Matthias
d10d84adf3 Merge pull request #6326 from freqtrade/dependabot/pip/develop/uvicorn-0.17.1
Bump uvicorn from 0.17.0 to 0.17.1
2022-01-31 07:00:12 +01:00
Matthias
aac6d15a1d Merge pull request #6327 from freqtrade/dependabot/pip/develop/pytest-mock-3.7.0
Bump pytest-mock from 3.6.1 to 3.7.0
2022-01-31 06:58:58 +01:00
Matthias
faa23949b5 Merge pull request #6324 from freqtrade/dependabot/pip/develop/nbconvert-6.4.1
Bump nbconvert from 6.4.0 to 6.4.1
2022-01-31 06:56:08 +01:00
Matthias
677a14ddde Merge pull request #6322 from freqtrade/dependabot/pip/develop/ccxt-1.71.46
Bump ccxt from 1.70.45 to 1.71.46
2022-01-31 06:55:43 +01:00
Matthias
d4ca2e5767 Merge pull request #6325 from freqtrade/dependabot/pip/develop/types-python-dateutil-2.8.9
Bump types-python-dateutil from 2.8.8 to 2.8.9
2022-01-31 06:55:13 +01:00
Matthias
ab3c6a7ee4 Merge pull request #6328 from freqtrade/dependabot/pip/develop/arrow-1.2.2
Bump arrow from 1.2.1 to 1.2.2
2022-01-31 06:54:52 +01:00
dependabot[bot]
bc5adc0188 Bump prompt-toolkit from 3.0.24 to 3.0.26
Bumps [prompt-toolkit](https://github.com/prompt-toolkit/python-prompt-toolkit) from 3.0.24 to 3.0.26.
- [Release notes](https://github.com/prompt-toolkit/python-prompt-toolkit/releases)
- [Changelog](https://github.com/prompt-toolkit/python-prompt-toolkit/blob/master/CHANGELOG)
- [Commits](https://github.com/prompt-toolkit/python-prompt-toolkit/compare/3.0.24...3.0.26)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-31 03:01:46 +00:00
dependabot[bot]
65526e9803 Bump arrow from 1.2.1 to 1.2.2
Bumps [arrow](https://github.com/arrow-py/arrow) from 1.2.1 to 1.2.2.
- [Release notes](https://github.com/arrow-py/arrow/releases)
- [Changelog](https://github.com/arrow-py/arrow/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/arrow-py/arrow/compare/1.2.1...1.2.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-31 03:01:42 +00:00
dependabot[bot]
8a9b70cc49 Bump pytest-mock from 3.6.1 to 3.7.0
Bumps [pytest-mock](https://github.com/pytest-dev/pytest-mock) from 3.6.1 to 3.7.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.6.1...v3.7.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-31 03:01:34 +00:00
dependabot[bot]
ab5c1e6c1e Bump uvicorn from 0.17.0 to 0.17.1
Bumps [uvicorn](https://github.com/encode/uvicorn) from 0.17.0 to 0.17.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.17.0...0.17.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-31 03:01:31 +00:00
dependabot[bot]
15dbdfe130 Bump types-python-dateutil from 2.8.8 to 2.8.9
Bumps [types-python-dateutil](https://github.com/python/typeshed) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-31 03:01:27 +00:00
dependabot[bot]
dbf2226841 Bump nbconvert from 6.4.0 to 6.4.1
Bumps [nbconvert](https://github.com/jupyter/nbconvert) from 6.4.0 to 6.4.1.
- [Release notes](https://github.com/jupyter/nbconvert/releases)
- [Commits](https://github.com/jupyter/nbconvert/compare/6.4.0...6.4.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-31 03:01:25 +00:00
dependabot[bot]
7d066d81c1 Bump mkdocs-material from 8.1.8 to 8.1.9
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 8.1.8 to 8.1.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/8.1.8...8.1.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>
2022-01-31 03:01:22 +00:00
dependabot[bot]
287e90af8e Bump ccxt from 1.70.45 to 1.71.46
Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.70.45 to 1.71.46.
- [Release notes](https://github.com/ccxt/ccxt/releases)
- [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg)
- [Commits](https://github.com/ccxt/ccxt/compare/1.70.45...1.71.46)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-31 03:01:18 +00:00
Matthias
cafda31869 Merge pull request #6320 from stash86/pos_adjust
Change new-config message for max_open_trades input
2022-01-30 14:11:20 +01:00
Matthias
7aae9565c7 Only look at buy orders when looking to recalculate trade value 2022-01-30 14:08:42 +01:00
Stefano Ariestasia
da39ca6650 fix new-config 2022-01-30 21:19:05 +09:00
Matthias
aea84dc117 Limit should default to None when calling pair_candles 2022-01-29 20:16:58 +01:00
Matthias
d1d520769e Improve status table for position adjust
don't show "/max" if no maximum is set

closes #6317
2022-01-29 14:29:09 +01:00
Matthias
e7409e74c2 Remove default from position_adjustment (otherwise strategy parameters won't work anymore) 2022-01-28 19:21:42 +01:00
Matthias
fb3c67d86b Accept that keys sometimes are not provided 2022-01-28 17:07:34 +01:00
Matthias
571ddceaf6 Merge pull request #6310 from Verbalinsurection/perso
Fix ETH duplicate in CoinGecko
2022-01-28 12:35:44 +01:00
Verbalinsurection
9ae14f0b56 Merge remote-tracking branch 'Verbalinsurection/perso' into perso 2022-01-28 11:17:27 +01:00
Verbalinsurection
2ba2144df1 Add tests for ETH fiat_convert 2022-01-28 11:17:16 +01:00
Verbalinsurection
660f474ab8 Fix ETH duplicate in CoinGecko 2022-01-28 10:26:31 +01:00
Matthias
e062188a18 Merge pull request #6309 from freqtrade/fix_krakenci
Filter tickers on stake-currency for kraken
2022-01-28 08:08:00 +01:00
Matthias
138e867a68 Filter tickers on stake-currency for kraken 2022-01-28 07:20:47 +01:00
Matthias
9df7014de3 Skip kraken tests temporarily 2022-01-28 06:59:37 +01:00
Matthias
bf8ef58439 Merge pull request #6308 from stash86/pos_adjust
Fix wordings
2022-01-28 06:59:08 +01:00
Stefano Ariestasia
b8f29802e5 another typo 2022-01-28 09:31:36 +09:00
Stefano Ariestasia
cbd213bc0a fix typo 2022-01-28 09:16:56 +09:00
Verbalinsurection
31211a33fd Fix ETH duplicate in CoinGecko 2022-01-27 22:21:16 +01:00
Matthias
82e193d9f0 Merge pull request #6260 from stash86/pos_adjust
Add max_buy_position_adjustment as attribute
2022-01-27 20:13:51 +01:00
Matthias
002226f5fd Update setting to max_entry_position_adjustment 2022-01-27 16:57:50 +01:00
Matthias
18168cba7a Merge pull request #6303 from Wings22Actual/develop
Added note to docker_quickstart.md
2022-01-27 07:36:38 +01:00
Wings22Actual
4a2914d72e Update docs/docker_quickstart.md
Co-authored-by: Matthias <xmatthias@outlook.com>
2022-01-27 07:04:06 +01:00
Wings22Actual
4408f97a00 Added note to docker_quickstart.md
Added note that 'docker run --rm' can be used instead of 'docker-compose run --rm' for some commands (line 129-132)
2022-01-26 06:56:39 +00:00
Matthias
b6943f3bca Merge pull request #6296 from freqtrade/python_37_remove
Update references to remove python 3.7 support
2022-01-25 20:01:14 +01:00
Matthias
12c79967f5 Merge pull request #6258 from italodamato/pass_dimensions_to_generate_estimator
Pass dimensions to generate_estimator
2022-01-25 19:37:22 +01:00
Italo
30b27ae736 explicit dtype 2022-01-25 12:29:55 +00:00
Matthias
2e2b1e2470 Merge pull request #6299 from freqtrade/dependabot/pip/develop/ccxt-1.70.45
Bump ccxt from 1.68.20 to 1.70.45
2022-01-25 13:18:54 +01:00
Italo
f7a5b2cb71 explicit dimensions, added **kwargs, updated docs 2022-01-25 11:43:40 +00:00
dependabot[bot]
6e47d06733 Bump ccxt from 1.68.20 to 1.70.45
Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.68.20 to 1.70.45.
- [Release notes](https://github.com/ccxt/ccxt/releases)
- [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg)
- [Commits](https://github.com/ccxt/ccxt/compare/1.68.20...1.70.45)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-25 05:44:03 +00:00
Matthias
d9347e9900 Update references to remove python 3.7 support 2022-01-25 06:43:36 +01:00
Matthias
eb677ad9b6 Merge pull request #6291 from freqtrade/dependabot/pip/develop/pandas-1.4.0
Bump pandas from 1.3.5 to 1.4.0
2022-01-25 06:43:03 +01:00
Stefano Ariestasia
0fa7986369 Merge branch 'freqtrade:develop' into pos_adjust 2022-01-25 10:30:18 +09:00
Matthias
aacddf64cf Add pandas 3.7 requirements workaround 2022-01-24 19:07:37 +01:00
Matthias
ac71d79364 Merge pull request #6281 from freqtrade/stake_amount_tag
Entry callbacks - provide buy_tag
2022-01-24 09:28:40 +01:00
Matthias
b8377b9e30 Merge pull request #6288 from freqtrade/dependabot/pip/develop/pytest-asyncio-0.17.2
Bump pytest-asyncio from 0.17.1 to 0.17.2
2022-01-24 08:02:01 +01:00
Matthias
2823da977d Merge pull request #6289 from freqtrade/dependabot/pip/develop/sqlalchemy-1.4.31
Bump sqlalchemy from 1.4.29 to 1.4.31
2022-01-24 08:00:46 +01:00
Matthias
45a2298929 Re-add getting buy orders in docs 2022-01-24 07:11:22 +01:00
Matthias
381bda1e4a Update test to add new argument 2022-01-24 07:02:02 +01:00
Matthias
194a5ce3cc Update advanced strategy template with missing methods 2022-01-24 07:02:02 +01:00
Matthias
e252830229 Add entry_tag to "entry" callbacks 2022-01-24 07:02:01 +01:00
Matthias
6d91ceb28c Merge pull request #6282 from xataxxx/develop
Fix missing order time data in backtesting.
2022-01-24 06:59:21 +01:00
Matthias
ce7dff405f Merge pull request #6292 from freqtrade/dependabot/pip/develop/fastapi-0.73.0
Bump fastapi from 0.72.0 to 0.73.0
2022-01-24 06:53:39 +01:00
Matthias
1aa7c193ba Merge pull request #6287 from freqtrade/dependabot/pip/develop/mkdocs-material-8.1.8
Bump mkdocs-material from 8.1.7 to 8.1.8
2022-01-24 06:52:57 +01:00
Matthias
0990e5d472 Merge pull request #6286 from freqtrade/dependabot/pip/develop/types-python-dateutil-2.8.8
Bump types-python-dateutil from 2.8.7 to 2.8.8
2022-01-24 06:22:58 +01:00
Matthias
9ae6bbb8d2 Merge pull request #6290 from freqtrade/dependabot/pip/develop/types-filelock-3.2.5
Bump types-filelock from 3.2.4 to 3.2.5
2022-01-24 06:22:24 +01:00
dependabot[bot]
b8413410d1 Bump fastapi from 0.72.0 to 0.73.0
Bumps [fastapi](https://github.com/tiangolo/fastapi) from 0.72.0 to 0.73.0.
- [Release notes](https://github.com/tiangolo/fastapi/releases)
- [Commits](https://github.com/tiangolo/fastapi/compare/0.72.0...0.73.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-24 03:01:33 +00:00
dependabot[bot]
fd1828c283 Bump pandas from 1.3.5 to 1.4.0
Bumps [pandas](https://github.com/pandas-dev/pandas) from 1.3.5 to 1.4.0.
- [Release notes](https://github.com/pandas-dev/pandas/releases)
- [Changelog](https://github.com/pandas-dev/pandas/blob/main/RELEASE.md)
- [Commits](https://github.com/pandas-dev/pandas/compare/v1.3.5...v1.4.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-24 03:01:30 +00:00
dependabot[bot]
cf79cef7ba Bump types-filelock from 3.2.4 to 3.2.5
Bumps [types-filelock](https://github.com/python/typeshed) from 3.2.4 to 3.2.5.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-24 03:01:24 +00:00
dependabot[bot]
95c7d48684 Bump sqlalchemy from 1.4.29 to 1.4.31
Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 1.4.29 to 1.4.31.
- [Release notes](https://github.com/sqlalchemy/sqlalchemy/releases)
- [Changelog](https://github.com/sqlalchemy/sqlalchemy/blob/main/CHANGES)
- [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>
2022-01-24 03:01:22 +00:00
dependabot[bot]
12fabba784 Bump pytest-asyncio from 0.17.1 to 0.17.2
Bumps [pytest-asyncio](https://github.com/pytest-dev/pytest-asyncio) from 0.17.1 to 0.17.2.
- [Release notes](https://github.com/pytest-dev/pytest-asyncio/releases)
- [Commits](https://github.com/pytest-dev/pytest-asyncio/compare/v0.17.1...v0.17.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-24 03:01:12 +00:00
dependabot[bot]
013262f7e2 Bump mkdocs-material from 8.1.7 to 8.1.8
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 8.1.7 to 8.1.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/8.1.7...8.1.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>
2022-01-24 03:01:07 +00:00
dependabot[bot]
138fd9440a Bump types-python-dateutil from 2.8.7 to 2.8.8
Bumps [types-python-dateutil](https://github.com/python/typeshed) from 2.8.7 to 2.8.8.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-24 03:01:03 +00:00
Reigo Reinmets
451eca51c8 Optimise the multiple usages of the same timestamp. 2022-01-23 20:58:25 +02:00
Reigo Reinmets
e67a54f7a9 Fix missing order time info in backtesting. 2022-01-23 20:52:35 +02:00
Matthias
daee59f4f1 Reorder interface methods 2022-01-23 19:20:31 +01:00
Matthias
57067ce88d Add tests for max_adjustment_buy handling 2022-01-23 19:07:37 +01:00
Matthias
7429f535c1 Imrpove code by reusing available properties 2022-01-23 18:59:09 +01:00
Matthias
62df044618 Merge pull request #6279 from xataxxx/develop
Fix position adjustment not counting older orders because of missing average field value
2022-01-23 17:42:51 +01:00
Matthias
6492e1cd76 Investigate random test failure 2022-01-23 17:42:18 +01:00
Matthias
4b6f9121ca Merge pull request #6275 from freqtrade/extract_timedout_from_ftbot
Extract timedout from ftbot
2022-01-23 17:28:10 +01:00
Reigo Reinmets
6613e3757a Additional fix 2022-01-23 18:09:57 +02:00
Matthias
09db4bcadd Don't use strings, use methods directly 2022-01-23 17:00:00 +01:00
Reigo Reinmets
51b94889b2 Just in case also check for closed to avoid counting in canceled orders. 2022-01-23 17:56:41 +02:00
Reigo Reinmets
8c79d55739 Fix issue #6268 2022-01-23 17:44:16 +02:00
Matthias
821a9d9cdc Add current_time to check_timeout functions for convenience 2022-01-23 08:10:35 +01:00
Stefano Ariestasia
cc3852daf3 Add defaults to constants.py and update docs 2022-01-23 03:54:58 +00:00
Matthias
56daafd6b7 Use realistic date for dry-run orders 2022-01-22 16:31:59 +01:00
Matthias
01b331ee42 Merge pull request #6274 from italodamato/set-stoploss-at-trade-creation
set stoploss at trade creation
2022-01-22 16:17:47 +01:00
Matthias
7bef9a9b3e Extract timeout handling from freqtradebot class 2022-01-22 15:59:10 +01:00
Italo
82f0d4d056 set stoploss at trade creation 2022-01-22 14:03:12 +00:00
Matthias
a35b0b519a Update forcebuy endpoint to support stake-amount
closes #6223
2022-01-22 13:26:02 +01:00
Stefano Ariestasia
fe5f61694b Merge branch 'freqtrade:develop' into pos_adjust 2022-01-22 21:25:16 +09:00
Matthias
1505ad451c Merge pull request #6271 from freqtrade/fix/6267
Allow @informative in webserver mode
2022-01-22 10:33:52 +01:00
Matthias
9ecd7400c8 Allow NaN when calculating digests 2022-01-22 08:10:09 +01:00
Matthias
314a544881 Add Failing test for get_strategy_run_id
Fails because max_open_trades is "inf"
emulates behaviour of `max_open_trades=-1` when loading the
configuration)
2022-01-22 08:09:08 +01:00
Matthias
a43c088448 Allow @informative in webserver mode 2022-01-22 07:11:59 +01:00
Matthias
bb1d8fb54f Improve message when no hyperopt fie is found
closes #6266
2022-01-21 15:24:26 +01:00
Stefano Ariestasia
3249f9fb98 Add max buys on status table 2022-01-21 08:27:54 +00:00
Stefano Ariestasia
f3a152a5a2 Merge branch 'freqtrade:develop' into pos_adjust 2022-01-21 17:11:36 +09:00
Matthias
730d2e3574 Merge pull request #6259 from stash86/fix-docs
add "# Buys" column to status table
2022-01-21 07:49:21 +01:00
Matthias
d02acb21c2 Add simple test for #buy header 2022-01-21 07:04:25 +01:00
Matthias
f4487c7711 Merge pull request #6264 from italodamato/plot-trade-buytag
add buy_tag to trade tooltip in plots
2022-01-21 06:51:44 +01:00
Stefano Ariestasia
748381c5cd Update based on flake8 2022-01-21 00:35:22 +00:00
Italo
e35a1e4a01 fix flake8 2022-01-20 21:00:33 +00:00
Italo
a9f14ac119 show buy_tag only if not None 2022-01-20 18:44:09 +00:00
Italo
8ce5536dd8 fix tests
added buy_tag to first ADA/USDT trade
2022-01-20 18:37:17 +00:00
Italo
4e9f0d89af add buy_tag to trade tooltip in plots 2022-01-20 17:17:58 +00:00
Matthias
d549905856 Api-backtest to test new functionality 2022-01-20 07:11:48 +01:00
Matthias
a6c7f45545 Update webserver backtseting to reuse prior results 2022-01-20 06:51:48 +01:00
Matthias
e9baabce6f Store results when backtesting via API 2022-01-20 06:35:18 +01:00
Stefano Ariestasia
f30580e5f2 Update freqtradebot.py 2022-01-20 11:40:29 +09:00
Stefano Ariestasia
5fb9511556 fix typo 2022-01-20 10:34:35 +09:00
Stefano Ariestasia
62ea1a445e add lines to show_config message 2022-01-20 10:03:26 +09:00
Stefano Ariestasia
2e537df358 Update strategy_resolver.py 2022-01-20 09:07:59 +09:00
Matthias
847e8977ca Merge pull request #6255 from rokups/rk/backtest-result-caching-2
Rework backtesting --no-cahche to --cache=[none, day, week, month].
2022-01-19 21:01:24 +01:00
Matthias
afe46a55f7 Add documentation for --cache backtest option 2022-01-19 20:19:17 +01:00
Matthias
d319204dea Add note about legacy metadata format 2022-01-19 20:08:09 +01:00
Stefano Ariestasia
7c010b3058 Update strategy-callbacks.md 2022-01-19 22:16:42 +09:00
Stefano Ariestasia
ac93eea585 update 2022-01-19 21:58:24 +09:00
Rokas Kupstys
5fffc5033a Rework backtesting --no-cahche to --cache=[none, day, week, month].
Fix an issue where config modification during runtime would prevent use of cached results.
2022-01-19 11:44:35 +02:00
Stefano Ariestasia
5525fdae1a add max_buy_position_adjustment as attribute 2022-01-19 16:50:13 +09:00
Stefano Ariestasia
3925e8a7e3 add "# Buys" column to status table 2022-01-19 16:14:21 +09:00
Italo
a4dbdb549d added type spec 2022-01-19 01:37:47 +00:00
Italo
a6a127f596 Update .gitignore 2022-01-19 01:31:14 +00:00
Italo
407c20412d Pass dimensions to generate_estimator
It's needed in order to create isotropic kernels for the GaussianProcessRegressor
2022-01-19 01:07:41 +00:00
Matthias
301b2e8a0f Merge pull request #6254 from SmartManoj/patch-2
Spreadfilter log % fix
2022-01-18 09:55:02 +01:00
மனோஜ்குமார் பழனிச்சாமி
d918d24f08 Spreadfilter log % fix
Both the following same
print(f'{3:.3%}') 
print(f'{3 *100 :.3}%')
# 300.000%
2022-01-18 14:05:03 +05:30
Matthias
3c06d31bbf Merge pull request #6238 from stash86/fix-docs
Add position adjustment On or Off on startup message
2022-01-17 20:32:27 +01:00
Matthias
d813fef95b Add asyncio_mode setting for pytest 2022-01-17 20:02:59 +01:00
Matthias
9c9c9f0171 Readd trailing white line 2022-01-17 20:00:35 +01:00
Matthias
91236c1876 Merge pull request #6244 from freqtrade/dependabot/pip/develop/uvicorn-0.17.0
Bump uvicorn from 0.16.0 to 0.17.0
2022-01-17 19:01:49 +01:00
Matthias
a156101d5c Merge pull request #6252 from xataxxx/dca
Fix eager-loading trade.orders
2022-01-17 13:17:01 +01:00
Reigo Reinmets
3de843ab2c Add eager-loading for orders. This allows access to trade.orders in all callbacks. 2022-01-17 13:23:37 +02:00
Matthias
8d67caafb3 Merge pull request #6240 from freqtrade/dependabot/pip/develop/time-machine-2.6.0
Bump time-machine from 2.5.0 to 2.6.0
2022-01-17 08:41:35 +01:00
Stefano Ariestasia
f9a935b9a3 Update rpc_manager.py 2022-01-17 16:37:06 +09:00
dependabot[bot]
d0dc9e26b0 Bump uvicorn from 0.16.0 to 0.17.0
Bumps [uvicorn](https://github.com/encode/uvicorn) from 0.16.0 to 0.17.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.16.0...0.17.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-17 07:10:59 +00:00
Matthias
3a5841bc03 Merge pull request #6246 from freqtrade/dependabot/pip/develop/fastapi-0.72.0
Bump fastapi from 0.71.0 to 0.72.0
2022-01-17 08:10:05 +01:00
Matthias
cf41f71f39 Merge pull request #6245 from freqtrade/dependabot/pip/develop/pytest-asyncio-0.17.1
Bump pytest-asyncio from 0.16.0 to 0.17.1
2022-01-17 08:08:52 +01:00
dependabot[bot]
f2984e9d0e Bump time-machine from 2.5.0 to 2.6.0
Bumps [time-machine](https://github.com/adamchainz/time-machine) from 2.5.0 to 2.6.0.
- [Release notes](https://github.com/adamchainz/time-machine/releases)
- [Changelog](https://github.com/adamchainz/time-machine/blob/main/HISTORY.rst)
- [Commits](https://github.com/adamchainz/time-machine/compare/2.5.0...2.6.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>
2022-01-17 07:06:07 +00:00
Matthias
fa605e6a50 Merge pull request #6251 from freqtrade/dependabot/pip/develop/types-python-dateutil-2.8.7
Bump types-python-dateutil from 2.8.6 to 2.8.7
2022-01-17 08:05:20 +01:00
Matthias
0f51192575 Merge pull request #6250 from freqtrade/dependabot/pip/develop/types-cachetools-4.2.9
Bump types-cachetools from 4.2.8 to 4.2.9
2022-01-17 08:04:59 +01:00
Matthias
0629dc866d Merge pull request #6243 from freqtrade/dependabot/pip/develop/jsonschema-4.4.0
Bump jsonschema from 4.3.3 to 4.4.0
2022-01-17 06:40:26 +01:00
dependabot[bot]
da134d3ad1 Bump types-cachetools from 4.2.8 to 4.2.9
Bumps [types-cachetools](https://github.com/python/typeshed) from 4.2.8 to 4.2.9.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-17 05:32:38 +00:00
Matthias
4f9af81b50 Merge pull request #6248 from freqtrade/dependabot/pip/develop/types-requests-2.27.7
Bump types-requests from 2.27.5 to 2.27.7
2022-01-17 06:31:46 +01:00
Matthias
543a561019 Merge pull request #6249 from freqtrade/dependabot/pip/develop/ccxt-1.68.20
Bump ccxt from 1.66.66 to 1.68.20
2022-01-17 06:30:27 +01:00
dependabot[bot]
9092596a1f Bump pytest-asyncio from 0.16.0 to 0.17.1
Bumps [pytest-asyncio](https://github.com/pytest-dev/pytest-asyncio) from 0.16.0 to 0.17.1.
- [Release notes](https://github.com/pytest-dev/pytest-asyncio/releases)
- [Commits](https://github.com/pytest-dev/pytest-asyncio/compare/v0.16.0...v0.17.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-17 05:28:33 +00:00
Matthias
096d2d7313 Merge pull request #6241 from freqtrade/dependabot/pip/develop/numpy-1.22.1
Bump numpy from 1.22.0 to 1.22.1
2022-01-17 06:28:15 +01:00
dependabot[bot]
81b8008047 Bump types-python-dateutil from 2.8.6 to 2.8.7
Bumps [types-python-dateutil](https://github.com/python/typeshed) from 2.8.6 to 2.8.7.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-17 05:27:54 +00:00
Matthias
7073b36421 Merge pull request #6247 from freqtrade/dependabot/pip/develop/flake8-tidy-imports-4.6.0
Bump flake8-tidy-imports from 4.5.0 to 4.6.0
2022-01-17 06:27:39 +01:00
Matthias
108f79ad39 Merge pull request #6242 from freqtrade/dependabot/pip/develop/mkdocs-material-8.1.7
Bump mkdocs-material from 8.1.5 to 8.1.7
2022-01-17 06:27:21 +01:00
dependabot[bot]
d384184784 Bump ccxt from 1.66.66 to 1.68.20
Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.66.66 to 1.68.20.
- [Release notes](https://github.com/ccxt/ccxt/releases)
- [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg)
- [Commits](https://github.com/ccxt/ccxt/compare/1.66.66...1.68.20)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-17 03:02:28 +00:00
dependabot[bot]
9fbb9332f9 Bump types-requests from 2.27.5 to 2.27.7
Bumps [types-requests](https://github.com/python/typeshed) from 2.27.5 to 2.27.7.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-17 03:02:21 +00:00
dependabot[bot]
c403464bb4 Bump flake8-tidy-imports from 4.5.0 to 4.6.0
Bumps [flake8-tidy-imports](https://github.com/adamchainz/flake8-tidy-imports) from 4.5.0 to 4.6.0.
- [Release notes](https://github.com/adamchainz/flake8-tidy-imports/releases)
- [Changelog](https://github.com/adamchainz/flake8-tidy-imports/blob/main/HISTORY.rst)
- [Commits](https://github.com/adamchainz/flake8-tidy-imports/compare/4.5.0...4.6.0)

---
updated-dependencies:
- dependency-name: flake8-tidy-imports
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-17 03:02:17 +00:00
dependabot[bot]
3d9f34b064 Bump fastapi from 0.71.0 to 0.72.0
Bumps [fastapi](https://github.com/tiangolo/fastapi) from 0.71.0 to 0.72.0.
- [Release notes](https://github.com/tiangolo/fastapi/releases)
- [Commits](https://github.com/tiangolo/fastapi/compare/0.71.0...0.72.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-17 03:02:14 +00:00
dependabot[bot]
e4af162f38 Bump jsonschema from 4.3.3 to 4.4.0
Bumps [jsonschema](https://github.com/Julian/jsonschema) from 4.3.3 to 4.4.0.
- [Release notes](https://github.com/Julian/jsonschema/releases)
- [Changelog](https://github.com/Julian/jsonschema/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/Julian/jsonschema/compare/v4.3.3...v4.4.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-17 03:02:02 +00:00
dependabot[bot]
5afa975839 Bump mkdocs-material from 8.1.5 to 8.1.7
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 8.1.5 to 8.1.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/8.1.5...8.1.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>
2022-01-17 03:01:57 +00:00
dependabot[bot]
6be6515ecc Bump numpy from 1.22.0 to 1.22.1
Bumps [numpy](https://github.com/numpy/numpy) from 1.22.0 to 1.22.1.
- [Release notes](https://github.com/numpy/numpy/releases)
- [Changelog](https://github.com/numpy/numpy/blob/main/doc/HOWTO_RELEASE.rst.txt)
- [Commits](https://github.com/numpy/numpy/compare/v1.22.0...v1.22.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-17 03:01:53 +00:00
Stefano Ariestasia
b6ad0f52e9 Merge branch 'freqtrade:develop' into fix-docs 2022-01-17 10:59:16 +09:00
Stefano Ariestasia
edd2ea3699 Update rpc_manager.py 2022-01-17 10:57:10 +09:00
Matthias
3cdb672ac3 Improve test coverage 2022-01-16 19:11:20 +01:00
Matthias
c02497e4b8 Merge pull request #6172 from rokups/rk/backtest-results-caching
Backtest result reuse
2022-01-16 19:09:37 +01:00
Matthias
2bcfc0c90c Add warning about cache problems 2022-01-16 18:01:05 +01:00
Matthias
d08885ed92 Fix empty "/log" endpoint in certain moments 2022-01-16 15:37:00 +01:00
Matthias
69c00db7cd Only show /balance % improvement if trades have been made 2022-01-16 13:39:50 +01:00
Matthias
b96b0f89bd improved unfilledtimeout defaults 2022-01-16 13:17:12 +01:00
Stefano Ariestasia
acda9571d1 Revert "add position_adjustment_enable to config_full.example"
This reverts commit eabeb87ceb.
2022-01-16 19:10:32 +09:00
Matthias
6c4b261469 Convert nan to None in get_signal. 2022-01-16 08:04:39 +01:00
Stefano Ariestasia
eabeb87ceb add position_adjustment_enable to config_full.example 2022-01-16 10:00:13 +09:00
Matthias
39184e1f95 Fix random test-fail around midnight 2022-01-16 00:19:21 +01:00
Matthias
270d7ebbf5 Simplify test strategy 2022-01-15 17:36:42 +01:00
Rokas Kupstys
062d00e8f2 Fix @informative decorator failing with edge. 2022-01-15 17:31:16 +02:00
Rokas Kupstys
2b7405470a Fix timerange check. 2022-01-15 17:30:40 +02:00
Matthias
9becce9897 Update failing test 2022-01-15 17:30:40 +02:00
Rokas Kupstys
526ed7fa9a Add test_backtest_start_multi_strat_caching test flexing backtest result caching. 2022-01-15 17:30:40 +02:00
Rokas Kupstys
16861db653 Implement previous backtest result reuse when config and strategy did not change. 2022-01-15 17:30:40 +02:00
Matthias
6684bff963 Dry-run orders should have filled set correctly 2022-01-15 15:25:16 +01:00
Matthias
caea8967d5 Merge pull request #6079 from xataxxx/dca
Initial position adjustment support (DCA)
2022-01-15 15:24:42 +01:00
Matthias
66a479c26a Small doc improvements 2022-01-15 15:11:13 +01:00
Reigo Reinmets
6c0eef94bb Fix typo. 2022-01-14 21:05:05 +02:00
Reigo Reinmets
9f9e2a8722 Remove references to wallets.get_trade_stake_amount 2022-01-14 20:46:16 +02:00
Reigo Reinmets
93adb436f8 Fix flake8 intention issue. 2022-01-14 20:25:29 +02:00
Reigo Reinmets
766c69734d Merge branch 'dca' of github.com:xataxxx/freqtrade into dca 2022-01-14 20:02:47 +02:00
Reigo Reinmets
320c9ccf90 Unify functions and make it easy to get a list of filled buy orders 2022-01-14 20:02:35 +02:00
Reigo Reinmets
1e324d208e Merge branch 'freqtrade:develop' into dca 2022-01-14 17:09:10 +02:00
Reigo Reinmets
08cae6f067 Fix horrible whitespace mistake. 2022-01-13 20:44:03 +02:00
Reigo Reinmets
7699fde380 Update documentation about trade.nr_of_successful_buys 2022-01-13 20:33:40 +02:00
Reigo Reinmets
ffe69535d8 These could be properties. 2022-01-13 20:31:03 +02:00
Reigo Reinmets
13bc5c5d8f Fine, this does look better. 2022-01-13 20:24:21 +02:00
Reigo Reinmets
678be0b773 Slightly move code. 2022-01-13 20:16:45 +02:00
Matthias
faa35cb167 Small minor fixes 2022-01-13 17:18:07 +01:00
Matthias
13651fd3be Downgrade docker-image to 3.9.9 2022-01-13 09:24:13 +01:00
Reigo Reinmets
c826c9c2b9 Merge branch 'freqtrade:develop' into dca 2022-01-13 10:04:39 +02:00
Matthias
814a343ed3 Merge pull request #6217 from Wings22Actual/patch-1
minor spelling correction to build_config_commands.py
2022-01-13 06:51:52 +01:00
Wings22Actual
a22e1b6500 minor spelling correction
line 89 "Tim"->"Time"
2022-01-13 01:48:38 +00:00
Matthias
33cb9e9002 Fix erroneous import in docs 2022-01-12 09:11:42 +01:00
Reigo Reinmets
ffab70d869 Merge branch 'freqtrade:develop' into dca 2022-01-12 09:22:06 +02:00
Reigo Reinmets
7344f88ad5 Add a note about not being called when there's an open order. 2022-01-12 09:21:52 +02:00
Reigo Reinmets
7cd8448656 Fix documentation example. 2022-01-12 06:09:06 +02:00
Reigo Reinmets
8643b20a0e Improve documentation about /stopbuy command 2022-01-12 06:06:23 +02:00
Reigo Reinmets
af3d220ffc Update example method signature. 2022-01-12 05:09:52 +02:00
Reigo Reinmets
db3483c827 Our example should also set position_adjustment_enable 2022-01-12 05:02:42 +02:00
Matthias
775b1201d2 psutil is always required now.
closes #6208
2022-01-11 15:02:21 +01:00
Reigo Reinmets
e50b07ecb4 Make code compatible. 2022-01-11 12:05:57 +02:00
Reigo Reinmets
fec95277bb Merge branch 'dca' of github.com:xataxxx/freqtrade into dca 2022-01-11 11:48:51 +02:00
Reigo Reinmets
94f2c99989 Temporary fix for lazy loading. Probably we can do it better. 2022-01-11 11:43:32 +02:00
Reigo Reinmets
73840e1d91 Merge branch 'freqtrade:develop' into dca 2022-01-11 10:49:32 +02:00
Matthias
58b77bd15f Merge pull request #6205 from freqtrade/dependabot/pip/develop/ta-lib-0.4.24
Bump ta-lib from 0.4.23 to 0.4.24
2022-01-11 08:05:34 +01:00
Matthias
438a083602 Update TA-lib binary files 2022-01-11 07:16:49 +01:00
Reigo Reinmets
fbf026ac43 Fix sorting of imports. 2022-01-10 20:43:57 +02:00
Reigo Reinmets
3b7167ab07 Fix backtesting missing filled amounts in orders. 2022-01-10 20:30:40 +02:00
Reigo Reinmets
26f2db4777 Fix notify_enter attempting to fetch rate during testing. 2022-01-10 20:30:32 +02:00
Reigo Reinmets
30d293bfec Fix bug with None in backtesting. 2022-01-10 20:16:11 +02:00
Reigo Reinmets
0dc7c389a0 Merge branch 'freqtrade:develop' into dca 2022-01-10 20:15:27 +02:00
Matthias
78921824c6 Merge pull request #6197 from freqtrade/dependabot/pip/develop/progressbar2-4.0.0
Bump progressbar2 from 3.55.0 to 4.0.0
2022-01-10 13:22:40 +01:00
dependabot[bot]
0d00da8dab Bump ta-lib from 0.4.23 to 0.4.24
Bumps [ta-lib](https://github.com/mrjbq7/ta-lib) from 0.4.23 to 0.4.24.
- [Release notes](https://github.com/mrjbq7/ta-lib/releases)
- [Changelog](https://github.com/mrjbq7/ta-lib/blob/TA_Lib-0.4.24/CHANGELOG)
- [Commits](https://github.com/mrjbq7/ta-lib/compare/TA_Lib-0.4.23...TA_Lib-0.4.24)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-10 10:19:41 +00:00
dependabot[bot]
e0b05c4df2 Bump progressbar2 from 3.55.0 to 4.0.0
Bumps [progressbar2](https://github.com/WoLpH/python-progressbar) from 3.55.0 to 4.0.0.
- [Release notes](https://github.com/WoLpH/python-progressbar/releases)
- [Changelog](https://github.com/WoLpH/python-progressbar/blob/develop/CHANGES.rst)
- [Commits](https://github.com/WoLpH/python-progressbar/compare/v3.55.0...v4.0.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-10 10:18:55 +00:00
Matthias
ef06ede3bd Merge pull request #6204 from freqtrade/dependabot/pip/develop/urllib3-1.26.8
Bump urllib3 from 1.26.7 to 1.26.8
2022-01-10 11:17:19 +01:00
Matthias
42b5e8dac6 Merge pull request #6190 from freqtrade/dependabot/pip/develop/mkdocs-material-8.1.5
Bump mkdocs-material from 8.1.4 to 8.1.5
2022-01-10 11:16:56 +01:00
Matthias
11d74da1e0 Merge pull request #6201 from freqtrade/dependabot/pip/develop/types-tabulate-0.8.5
Bump types-tabulate from 0.8.4 to 0.8.5
2022-01-10 11:16:46 +01:00
dependabot[bot]
fc1069cfe0 Bump urllib3 from 1.26.7 to 1.26.8
Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.26.7 to 1.26.8.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/1.26.8/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/1.26.7...1.26.8)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-10 09:27:39 +00:00
dependabot[bot]
1e35f54709 Bump types-tabulate from 0.8.4 to 0.8.5
Bumps [types-tabulate](https://github.com/python/typeshed) from 0.8.4 to 0.8.5.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-10 09:27:22 +00:00
Matthias
d80216ca05 Merge pull request #6192 from freqtrade/dependabot/pip/develop/requests-2.27.1
Bump requests from 2.26.0 to 2.27.1
2022-01-10 10:26:22 +01:00
Matthias
d2c7ff3f0f Merge pull request #6206 from freqtrade/dependabot/pip/develop/mypy-0.931
Bump mypy from 0.930 to 0.931
2022-01-10 10:26:07 +01:00
Matthias
d90745651a Merge pull request #6202 from freqtrade/dependabot/pip/develop/types-filelock-3.2.4
Bump types-filelock from 3.2.1 to 3.2.4
2022-01-10 10:25:46 +01:00
dependabot[bot]
130275faff Bump mypy from 0.930 to 0.931
Bumps [mypy](https://github.com/python/mypy) from 0.930 to 0.931.
- [Release notes](https://github.com/python/mypy/releases)
- [Commits](https://github.com/python/mypy/compare/v0.930...v0.931)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-10 07:28:13 +00:00
dependabot[bot]
29457078e7 Bump requests from 2.26.0 to 2.27.1
Bumps [requests](https://github.com/psf/requests) from 2.26.0 to 2.27.1.
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.26.0...v2.27.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-10 07:27:43 +00:00
dependabot[bot]
8d554585f1 Bump types-filelock from 3.2.1 to 3.2.4
Bumps [types-filelock](https://github.com/python/typeshed) from 3.2.1 to 3.2.4.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-10 07:27:35 +00:00
Matthias
000e29113f Merge pull request #6203 from freqtrade/dependabot/pip/develop/types-python-dateutil-2.8.6
Bump types-python-dateutil from 2.8.4 to 2.8.6
2022-01-10 08:27:02 +01:00
Matthias
8057929817 Merge pull request #6200 from freqtrade/dependabot/pip/develop/types-requests-2.27.5
Bump types-requests from 2.26.3 to 2.27.5
2022-01-10 08:26:22 +01:00
Matthias
858a65e308 Merge pull request #6196 from freqtrade/dependabot/pip/develop/fastapi-0.71.0
Bump fastapi from 0.70.1 to 0.71.0
2022-01-10 06:40:15 +01:00
dependabot[bot]
fe067994e3 Bump types-requests from 2.26.3 to 2.27.5
Bumps [types-requests](https://github.com/python/typeshed) from 2.26.3 to 2.27.5.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-10 05:39:59 +00:00
Matthias
d349d2743a Merge pull request #6191 from freqtrade/dependabot/pip/develop/python-telegram-bot-13.10
Bump python-telegram-bot from 13.9 to 13.10
2022-01-10 06:39:37 +01:00
dependabot[bot]
2d930d081c Bump types-python-dateutil from 2.8.4 to 2.8.6
Bumps [types-python-dateutil](https://github.com/python/typeshed) from 2.8.4 to 2.8.6.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-10 05:38:50 +00:00
Matthias
96f8338496 Merge pull request #6195 from freqtrade/dependabot/pip/develop/types-cachetools-4.2.8
Bump types-cachetools from 4.2.7 to 4.2.8
2022-01-10 06:38:35 +01:00
Matthias
7bc50dff7a Merge pull request #6199 from freqtrade/dependabot/pip/develop/ccxt-1.66.66
Bump ccxt from 1.66.32 to 1.66.66
2022-01-10 06:38:13 +01:00
Matthias
a4d2cf2f06 Merge pull request #6194 from freqtrade/dependabot/pip/develop/nbconvert-6.4.0
Bump nbconvert from 6.3.0 to 6.4.0
2022-01-10 06:37:57 +01:00
dependabot[bot]
af60b9db59 Bump ccxt from 1.66.32 to 1.66.66
Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.66.32 to 1.66.66.
- [Release notes](https://github.com/ccxt/ccxt/releases)
- [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg)
- [Commits](https://github.com/ccxt/ccxt/compare/1.66.32...1.66.66)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-10 03:02:11 +00:00
dependabot[bot]
bc95e1e151 Bump fastapi from 0.70.1 to 0.71.0
Bumps [fastapi](https://github.com/tiangolo/fastapi) from 0.70.1 to 0.71.0.
- [Release notes](https://github.com/tiangolo/fastapi/releases)
- [Commits](https://github.com/tiangolo/fastapi/compare/0.70.1...0.71.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-10 03:01:59 +00:00
dependabot[bot]
626970b32e Bump types-cachetools from 4.2.7 to 4.2.8
Bumps [types-cachetools](https://github.com/python/typeshed) from 4.2.7 to 4.2.8.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-10 03:01:56 +00:00
dependabot[bot]
a0d378fb7e Bump nbconvert from 6.3.0 to 6.4.0
Bumps [nbconvert](https://github.com/jupyter/nbconvert) from 6.3.0 to 6.4.0.
- [Release notes](https://github.com/jupyter/nbconvert/releases)
- [Commits](https://github.com/jupyter/nbconvert/compare/6.3.0...6.4.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-10 03:01:52 +00:00
dependabot[bot]
b8e8a31f84 Bump python-telegram-bot from 13.9 to 13.10
Bumps [python-telegram-bot](https://github.com/python-telegram-bot/python-telegram-bot) from 13.9 to 13.10.
- [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/v13.9...v13.10)

---
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>
2022-01-10 03:01:43 +00:00
dependabot[bot]
3fc44aa1bd Bump mkdocs-material from 8.1.4 to 8.1.5
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 8.1.4 to 8.1.5.
- [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/8.1.4...8.1.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-10 03:01:38 +00:00
Matthias
59ffb98779 Merge pull request #6189 from jay-tau/patch-1
Remove non-decimal numerical separators
2022-01-09 17:06:19 +01:00
Joel Tony
cbd449f710 Remove non-decimal numerical separators 2022-01-09 14:21:57 +05:30
Reigo Reinmets
91b89c8c42 Improve docs, fix telegram message to show current rate. 2022-01-08 21:30:42 +02:00
Reigo Reinmets
0424b44667 Merge branch 'freqtrade:develop' into dca 2022-01-08 17:42:17 +02:00
Reigo Reinmets
195d601b8e Fix notification message showing "Current rate" as the initial buy order desired rate. 2022-01-08 17:41:59 +02:00
Reigo Reinmets
c929d428b2 Remove blank line. 2022-01-08 17:21:32 +02:00
Reigo Reinmets
0bca07a32a Added min_stake, max_stake. Removed pair as its included in trade. 2022-01-08 17:20:02 +02:00
Reigo Reinmets
813a2cd23b Add useful helper methods for adjust_trade_position implementation 2022-01-08 17:18:37 +02:00
Matthias
cf077b15c2 Fix random test failure 2022-01-08 14:54:39 +01:00
Reigo Reinmets
94631c7d64 Add performance warnings for backtesting and implementation. 2022-01-08 15:06:05 +02:00
Reigo Reinmets
8e424f7c73 Merge branch 'freqtrade:develop' into dca 2022-01-08 14:57:15 +02:00
Matthias
43f8087f32 Bitvavo does not support USDT stake 2022-01-08 10:44:07 +01:00
Matthias
827b8d3e4c Don't use test_datadir as userdata dir
use tmpdir
2022-01-07 20:00:20 +01:00
Matthias
04976658da Fix crash when using backtesting-show on a old backtestresult 2022-01-07 17:34:47 +01:00
Matthias
b82e63cb62 Merge pull request #6182 from rokups/rk/fix-6179
Fix #6179
2022-01-07 17:25:09 +01:00
Rokas Kupstys
11ace0f867 Instead of clearing processed dict, store df_analyzed (one with buy/sell signals) dataframe in it.
It still saves memory because this dataframe is kept by DataProvider.
Fixes #6179.
Amends #6133 (a715083fc0).
2022-01-07 12:07:49 +02:00
Matthias
7f20f6834b Merge pull request #6181 from freqtrade/simplify_optimizereports
Simplify optimizereports
2022-01-07 09:43:57 +01:00
Matthias
cd144cdfc9 Add bitvavo to compatibility tests
#6166
2022-01-07 09:30:50 +01:00
Matthias
e540959c27 Remove btdata from generate_strategy_stats 2022-01-07 09:27:07 +01:00
Matthias
1203d08d1e generate_pair_metrics does not need processed dict 2022-01-07 09:27:07 +01:00
Matthias
b77943af0d Merge pull request #6173 from freqtrade/volume_quote_workaround
Selectively convert quote to base volume in volumepairlist
2022-01-07 09:07:16 +01:00
Matthias
560b3d5dbe Merge pull request #6177 from freqtrade/remove_old_bt_format
Remove old bt format
2022-01-07 08:36:26 +01:00
Matthias
d64f9030c1 Remove now unused codesegment 2022-01-07 08:04:01 +01:00
Matthias
9a3d0528a3 Versionbump ccxt to 1.66.32
closes #6166
2022-01-07 07:55:11 +01:00
Matthias
b3a4ecaf77 Remove old backtest format support 2022-01-06 19:49:25 +01:00
Matthias
28011a3907 Update bt_results filename to new.json 2022-01-06 19:28:04 +01:00
Matthias
72f486289a Update Volumepairlist test 2022-01-06 19:07:47 +01:00
Matthias
24ec78b11c Quote-volumelist fix for gateio 2022-01-06 19:07:47 +01:00
Matthias
326e3d1f8e Selectively convert quote to base volume in volumepairlist 2022-01-06 19:07:43 +01:00
Matthias
bb29c44462 Merge pull request #6174 from frosty00/okex-candle-limit
increase okex candle limit
2022-01-06 15:59:59 +01:00
Carlo Revelli
7451b60501 increase okex candle limit 2022-01-06 05:31:23 -08:00
Matthias
a0f9c1bf7b Avoid failure when calculating max-drawdown
occurs if if no winning trade is recorded.
2022-01-06 13:51:15 +01:00
Matthias
e88a1ab209 Improve VolumePairlist behaviour
Filter pairs before downloading ohlcv candles - this will greatly speed up some instances.
2022-01-06 13:49:27 +01:00
Matthias
addba6597a Merge pull request #6165 from freqtrade/drawdown_fixes
Improved drawdown calculation
2022-01-06 09:56:05 +01:00
Matthias
5451972456 Success-messages should use success coloring 2022-01-06 09:29:08 +01:00
Matthias
2a2392fd73 Update parameter name in docstring 2022-01-06 09:15:30 +01:00
Matthias
33d95d245e Fix unbounderror
closes #6169
2022-01-06 08:48:30 +01:00
Matthias
a9a6cf13f8 Add exit_tag to detail-sells
closes #6159
2022-01-06 08:22:15 +01:00
Matthias
4e2b9203d7 Remove no longer used BT_DATA_COLUMNS_MID 2022-01-05 20:40:59 +01:00
Matthias
2ca90577a6 Update strategy-comparison test 2022-01-05 20:29:40 +01:00
Matthias
2ecaf9f8b4 Update backtest-result test-files to latest format 2022-01-05 20:26:24 +01:00
Matthias
6abd6bceb9 Avoid recalculating statistics for comparison line 2022-01-05 20:16:48 +01:00
Matthias
67e4dda5b3 Fix missing DataFrame in advanced docs 2022-01-04 19:54:50 +01:00
Matthias
8373a4e713 Small Adjustments to improve compatibility 2022-01-04 19:17:08 +01:00
Matthias
4d9b4ddc28 Update hyperopt-tools to use account drawdown 2022-01-04 17:43:39 +01:00
Matthias
09fae25c94 Fix some tests after drawdown calculation change 2022-01-04 17:07:31 +01:00
Matthias
7a2b50ce8b Update drawdown calculation to account drawdown 2022-01-04 17:07:31 +01:00
Matthias
42579c0268 Drop hyperopt results legacy mode 2022-01-04 17:06:40 +01:00
Matthias
7bf735dbfc Update deprecated dynamic-whitelist docs with reference to new method 2022-01-04 17:06:40 +01:00
Matthias
937f5e3d0f No longer use legacy mode for tests 2022-01-04 17:06:40 +01:00
Matthias
7ea5b0e359 Simplify hyperopt test setup 2022-01-04 17:06:40 +01:00
Matthias
15cb3792cf Merge pull request #6161 from stash86/fix-docs
Add ignore_buying_expired_candle to config_full example
2022-01-04 15:37:15 +01:00
Stefano Ariestasia
fa620d3f7b Merge branch 'freqtrade:develop' into fix-docs 2022-01-04 13:44:03 +08:00
Matthias
7adb7f90a6 Merge pull request #6154 from freqtrade/dependabot/pip/develop/ta-lib-0.4.23
Bump ta-lib from 0.4.22 to 0.4.23
2022-01-03 19:08:59 +01:00
Matthias
5536410ed0 Update ta-lib wheels 2022-01-03 17:58:53 +01:00
Matthias
de2a7c1956 Merge pull request #6153 from freqtrade/dependabot/pip/develop/psutil-5.9.0
Bump psutil from 5.8.0 to 5.9.0
2022-01-03 17:55:05 +01:00
Matthias
079dbc7997 Remove duplicate psutil dependency 2022-01-03 17:41:55 +01:00
Reigo Reinmets
05ac09b38e Merge branch 'freqtrade:develop' into dca 2022-01-03 11:41:33 +02:00
Matthias
028636b4a9 Merge pull request #6150 from freqtrade/dependabot/pip/develop/types-tabulate-0.8.4
Bump types-tabulate from 0.8.3 to 0.8.4
2022-01-03 09:20:56 +01:00
dependabot[bot]
8ed30fc9c1 Bump psutil from 5.8.0 to 5.9.0
Bumps [psutil](https://github.com/giampaolo/psutil) from 5.8.0 to 5.9.0.
- [Release notes](https://github.com/giampaolo/psutil/releases)
- [Changelog](https://github.com/giampaolo/psutil/blob/master/HISTORY.rst)
- [Commits](https://github.com/giampaolo/psutil/compare/release-5.8.0...release-5.9.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-03 06:32:27 +00:00
dependabot[bot]
2469dc0424 Bump types-tabulate from 0.8.3 to 0.8.4
Bumps [types-tabulate](https://github.com/python/typeshed) from 0.8.3 to 0.8.4.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-03 06:30:53 +00:00
Matthias
33991e8de9 Merge pull request #6157 from freqtrade/dependabot/pip/develop/types-cachetools-4.2.7
Bump types-cachetools from 4.2.6 to 4.2.7
2022-01-03 07:30:04 +01:00
dependabot[bot]
5407a06254 Bump ta-lib from 0.4.22 to 0.4.23
Bumps [ta-lib](https://github.com/mrjbq7/ta-lib) from 0.4.22 to 0.4.23.
- [Release notes](https://github.com/mrjbq7/ta-lib/releases)
- [Changelog](https://github.com/mrjbq7/ta-lib/blob/master/CHANGELOG)
- [Commits](https://github.com/mrjbq7/ta-lib/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-03 06:07:58 +00:00
Matthias
616d5bbaed Merge pull request #6151 from freqtrade/dependabot/pip/develop/jsonschema-4.3.3
Bump jsonschema from 4.3.2 to 4.3.3
2022-01-03 07:07:05 +01:00
dependabot[bot]
3f4c5a7902 Bump types-cachetools from 4.2.6 to 4.2.7
Bumps [types-cachetools](https://github.com/python/typeshed) from 4.2.6 to 4.2.7.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-03 06:06:26 +00:00
Matthias
a253ad5ec1 Merge pull request #6156 from freqtrade/dependabot/pip/develop/mkdocs-material-8.1.4
Bump mkdocs-material from 8.1.3 to 8.1.4
2022-01-03 07:06:09 +01:00
Matthias
ce1780ca3f Merge pull request #6155 from freqtrade/dependabot/pip/develop/types-requests-2.26.3
Bump types-requests from 2.26.2 to 2.26.3
2022-01-03 07:05:46 +01:00
Matthias
6d3747d9e6 Merge pull request #6152 from freqtrade/dependabot/pip/develop/ccxt-1.66.20
Bump ccxt from 1.65.25 to 1.66.20
2022-01-03 07:05:19 +01:00
dependabot[bot]
0da31cff72 Bump mkdocs-material from 8.1.3 to 8.1.4
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 8.1.3 to 8.1.4.
- [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/8.1.3...8.1.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-03 03:01:51 +00:00
dependabot[bot]
f7d3c50213 Bump types-requests from 2.26.2 to 2.26.3
Bumps [types-requests](https://github.com/python/typeshed) from 2.26.2 to 2.26.3.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-03 03:01:47 +00:00
dependabot[bot]
6b0a7a81a9 Bump ccxt from 1.65.25 to 1.66.20
Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.65.25 to 1.66.20.
- [Release notes](https://github.com/ccxt/ccxt/releases)
- [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg)
- [Commits](https://github.com/ccxt/ccxt/compare/1.65.25...1.66.20)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-03 03:01:37 +00:00
dependabot[bot]
8c0f7321c3 Bump jsonschema from 4.3.2 to 4.3.3
Bumps [jsonschema](https://github.com/Julian/jsonschema) from 4.3.2 to 4.3.3.
- [Release notes](https://github.com/Julian/jsonschema/releases)
- [Changelog](https://github.com/Julian/jsonschema/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/Julian/jsonschema/compare/v4.3.2...v4.3.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-03 03:01:28 +00:00
Matthias
fac6956eeb Fix test failure after merge 2022-01-02 22:25:40 +01:00
Matthias
711a6a6dbc Merge branch 'develop' into pr/xataxxx/6079 2022-01-02 22:21:41 +01:00
Matthias
2116b0729f Integration-test for DCA order 2022-01-02 20:20:56 +01:00
Matthias
209ecc8732 Fix typo in bt_progress 2022-01-02 19:38:03 +01:00
Matthias
f3784f2149 Merge pull request #6147 from freqtrade/plot_parallel
Plot parallel and underwater
2022-01-01 19:27:42 +01:00
Matthias
08ba5b0451 Update docs to include underwaterplot 2022-01-01 16:55:08 +01:00
Matthias
fb06a673e0 Add Underwater plot 2022-01-01 14:40:20 +01:00
Matthias
78ba2d3fc7 Add underwaterplot calculation to btanalysis 2022-01-01 14:39:58 +01:00
Matthias
a2d97eecfe Add trade parallelism plot
closes #6142
2022-01-01 14:11:51 +01:00
Matthias
45a02beea8 Merge pull request #6145 from freqtrade/test_evict_cache
Fix CI failures
2022-01-01 14:07:21 +01:00
Matthias
8b49bec649 Use Version-dependent requirements
Bump numpy to 1.22.0
2022-01-01 13:51:13 +01:00
Matthias
c29469decf Version bump numpy to 1.22.0 2022-01-01 10:43:45 +01:00
Matthias
9becd20f20 Improve "Missing data" messages 2022-01-01 10:37:58 +01:00
Matthias
713b884d9b Fix failing monthly test 2022-01-01 10:37:43 +01:00
Matthias
515e1040c2 Merge pull request #6144 from freqtrade/asyncio_warns
Remove asyncio warnings due to deprecation on 3.10
2022-01-01 10:04:27 +01:00
Matthias
670aed06bf Remove loop for hyperopt. 2021-12-31 17:35:08 +01:00
Matthias
0277d93a64 don't use deprecated asyncio.get_event_loop() 2021-12-31 17:27:42 +01:00
Matthias
c9296dc9a0 Uvloop_helper should use "get_running_loop" 2021-12-31 17:27:42 +01:00
Matthias
550a1eef91 Reduce "cleanup" slowdown in telegram 2021-12-31 12:54:15 +01:00
Matthias
880ee016a4 Merge pull request #6133 from rokups/rk/reduce-memory-usage
Reduce memory usage by not holding on to no longer needed data
2021-12-31 11:36:52 +01:00
Matthias
39f8c5719b Fix exception on exchange shutdown 2021-12-31 11:24:56 +01:00
Rokas Kupstys
a715083fc0 Reduce memory usage by not holding on to no longer needed data. 2021-12-31 12:10:01 +02:00
Matthias
78ccaae318 Merge pull request #6140 from freqtrade/sell_before_roi
Change sequence of ROI/sell signal to favor sell-signal
2021-12-31 10:32:19 +01:00
Matthias
ee774f12bd Merge pull request #5677 from freqtrade/python_10
Update CI to run on python 3.10
2021-12-31 10:23:52 +01:00
Matthias
b1b2eebd11 Change sequence of ROI/sell signal to favor sell-signal 2021-12-30 20:00:58 +01:00
Matthias
b63491fb9c Update ROI_if_buy_signal tests to not use sell signal 2021-12-30 19:51:49 +01:00
Matthias
1bc2c71757 Update documentation with support for python 3.10 2021-12-30 16:33:01 +01:00
Matthias
6b22f84d30 Add windows ta wheel 2021-12-30 16:25:57 +01:00
Matthias
505d4bacd5 Update dockerfile to 3.10 2021-12-30 11:10:51 +01:00
Matthias
5b2a1b9e7a Update CI to run on python 3.10 2021-12-30 11:10:38 +01:00
Matthias
8edc84bf25 Exclude virtual environment from isort fixing 2021-12-30 10:19:06 +01:00
Matthias
bd98637ae9 Fail gracefully from plot-profit when no data is provided
closes #6132
2021-12-30 10:14:45 +01:00
Matthias
77afb7b5e2 Merge pull request #6114 from cdimauro/reduce_kucoin_logs
Reduce kucoin logs
2021-12-29 17:33:21 +01:00
Matthias
2b94fbfa74 Avoid using singleton where not necessary 2021-12-29 17:05:53 +01:00
Matthias
b530600718 Merge pull request #6134 from freqtrade/new_release
New release 2021.12
2021-12-29 17:00:19 +01:00
Matthias
043218cc7e Version bump to 2021.12 2021-12-29 16:18:14 +01:00
Matthias
c3e9ef27f6 Merge branch 'stable' into new_release 2021-12-29 16:17:56 +01:00
Reigo Reinmets
3d336a736e Improve documentation. 2021-12-29 14:51:57 +02:00
Matthias
24807515c1 Fix random test failure 2021-12-28 09:04:14 +01:00
Matthias
5a546855e6 Import TTLCache from cachetools
Importing from cachetools.ttl is deprecated, and will be removed in 5.0
2021-12-27 19:30:17 +01:00
Reigo Reinmets
f965e9177c Fix title 2021-12-27 19:56:45 +02:00
Reigo Reinmets
4b654b2713 Reduce logging. 2021-12-27 19:48:18 +02:00
Reigo Reinmets
093f98d368 Merge branch 'freqtrade:develop' into dca 2021-12-27 19:41:47 +02:00
Reigo Reinmets
2a728c676e Improve documentation. Fix bug. 2021-12-27 19:41:33 +02:00
Matthias
05a488a7a0 Further reduce log verbosity for kucoin 429000 exception 2021-12-27 17:15:30 +01:00
Matthias
bb65621134 Simplify test, simplify "log_*" selection 2021-12-27 17:14:59 +01:00
Matthias
df53873dab Merge pull request #6119 from freqtrade/dependabot/pip/develop/scikit-learn-1.0.2
Bump scikit-learn from 1.0.1 to 1.0.2
2021-12-27 16:48:48 +01:00
Matthias
ef2b326262 Reduce retrier message repetition
by combining messages, we can provide the same information in fewer log messages
2021-12-27 16:47:34 +01:00
Matthias
54858a0bbb Simplify test to only initialize and mock once. 2021-12-27 16:39:47 +01:00
Matthias
314e10596b Remove checking against logger_name in num_log_has 2021-12-27 16:39:31 +01:00
Reigo Reinmets
53ef37d5fc Merge branch 'freqtrade:develop' into dca 2021-12-27 17:12:26 +02:00
Matthias
17f037cec6 Extract order_fee handling from update_trade_state 2021-12-27 16:07:43 +01:00
Matthias
1b739acc08 Merge pull request #6118 from freqtrade/dependabot/pip/develop/sqlalchemy-1.4.29
Bump sqlalchemy from 1.4.28 to 1.4.29
2021-12-27 10:03:06 +01:00
dependabot[bot]
3804a17775 Bump scikit-learn from 1.0.1 to 1.0.2
Bumps [scikit-learn](https://github.com/scikit-learn/scikit-learn) from 1.0.1 to 1.0.2.
- [Release notes](https://github.com/scikit-learn/scikit-learn/releases)
- [Commits](https://github.com/scikit-learn/scikit-learn/compare/1.0.1...1.0.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-27 08:45:22 +00:00
Matthias
c8253790b6 Merge pull request #6125 from freqtrade/dependabot/pip/develop/filelock-3.4.2
Bump filelock from 3.4.0 to 3.4.2
2021-12-27 09:44:48 +01:00
Matthias
a215e29d2a Merge pull request #6117 from freqtrade/dependabot/pip/develop/ccxt-1.65.25
Bump ccxt from 1.64.44 to 1.65.25
2021-12-27 09:42:40 +01:00
Matthias
d58ed0e242 Merge pull request #6122 from freqtrade/dependabot/pip/develop/types-python-dateutil-2.8.4
Bump types-python-dateutil from 2.8.3 to 2.8.4
2021-12-27 08:45:01 +01:00
Matthias
2ab8f467dd Merge pull request #6121 from freqtrade/dependabot/pip/develop/jsonschema-4.3.2
Bump jsonschema from 4.3.1 to 4.3.2
2021-12-27 08:44:11 +01:00
Matthias
c1ec368c0c Merge pull request #6123 from freqtrade/dependabot/pip/develop/mypy-0.930
Bump mypy from 0.920 to 0.930
2021-12-27 08:43:54 +01:00
Matthias
29fff65598 Merge pull request #6120 from freqtrade/dependabot/pip/develop/plotly-5.5.0
Bump plotly from 5.4.0 to 5.5.0
2021-12-27 08:43:36 +01:00
dependabot[bot]
3cba405b2e Bump filelock from 3.4.0 to 3.4.2
Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.4.0 to 3.4.2.
- [Release notes](https://github.com/tox-dev/py-filelock/releases)
- [Changelog](https://github.com/tox-dev/py-filelock/blob/main/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/py-filelock/compare/3.4.0...3.4.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-27 03:54:13 +00:00
dependabot[bot]
24d16d7dab Bump mypy from 0.920 to 0.930
Bumps [mypy](https://github.com/python/mypy) from 0.920 to 0.930.
- [Release notes](https://github.com/python/mypy/releases)
- [Commits](https://github.com/python/mypy/compare/v0.920...v0.930)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-27 03:54:06 +00:00
dependabot[bot]
2e84b8f0d5 Bump types-python-dateutil from 2.8.3 to 2.8.4
Bumps [types-python-dateutil](https://github.com/python/typeshed) from 2.8.3 to 2.8.4.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-27 03:54:03 +00:00
dependabot[bot]
470ef7c160 Bump jsonschema from 4.3.1 to 4.3.2
Bumps [jsonschema](https://github.com/Julian/jsonschema) from 4.3.1 to 4.3.2.
- [Release notes](https://github.com/Julian/jsonschema/releases)
- [Changelog](https://github.com/Julian/jsonschema/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/Julian/jsonschema/compare/v4.3.1...v4.3.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-27 03:53:58 +00:00
dependabot[bot]
1093f22b80 Bump plotly from 5.4.0 to 5.5.0
Bumps [plotly](https://github.com/plotly/plotly.py) from 5.4.0 to 5.5.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.4.0...v5.5.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-27 03:53:54 +00:00
dependabot[bot]
e085058621 Bump sqlalchemy from 1.4.28 to 1.4.29
Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 1.4.28 to 1.4.29.
- [Release notes](https://github.com/sqlalchemy/sqlalchemy/releases)
- [Changelog](https://github.com/sqlalchemy/sqlalchemy/blob/main/CHANGES)
- [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>
2021-12-27 03:53:47 +00:00
dependabot[bot]
81b383fe5c Bump ccxt from 1.64.44 to 1.65.25
Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.64.44 to 1.65.25.
- [Release notes](https://github.com/ccxt/ccxt/releases)
- [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg)
- [Commits](https://github.com/ccxt/ccxt/compare/1.64.44...1.65.25)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-27 03:53:41 +00:00
cdimauro
f77b8cbb7a Reduce KuCoin logs only for 429000 error
Only KuCoin messages for 429000 error code are logged once.

Logs functions are also simplified and optimized.

test_remove_logs_for_pairs_already_in_blacklist is simplified as well.
2021-12-26 21:09:25 +01:00
Reigo Reinmets
bc8fc3ab09 We can actually call recalc_open_trade_value less since it's being called eventually anyway. 2021-12-26 20:09:18 +02:00
Reigo Reinmets
bd5520bee2 Adjust comments, fix stoploss_on_exchange for slower closed orders. 2021-12-26 20:03:10 +02:00
Reigo Reinmets
099dc07baf No longer needed since recalc_trade_from_orders always calls it. 2021-12-26 20:02:20 +02:00
Reigo Reinmets
817a65b656 This is not needed since backtesting does not have open orders. 2021-12-26 20:01:48 +02:00
Matthias
045225beef Slightly improve doc formatting 2021-12-26 15:34:37 +01:00
Matthias
d3f3c49b13 Fix minor "gotchas" 2021-12-26 15:29:10 +01:00
cdimauro
6509c38717 Introduce new test functions to check logs
New functions log_contains, num_log_contains, num_log_has and num_log_has_re
are introduced in the conftest module to help and simplify checking:
- if logs contain a string;
- count how many messages contain a string;
- count how many messages are the given string;
- count how many messages matchs a regex.

A couple of existing tests are changed using the new functions.
2021-12-26 09:49:14 +01:00
cdimauro
fbaf46901e Reduce more KuCoin logs on retrier decorator
More logs are reduced, for KuCoin, on the retrier_async decorator:

_async_get_candle_history() returned exception
retrying _async_get_candle_history() still for
Giving up retrying: _async_get_candle_history()
Applying DDosProtection backoff delay
2021-12-26 09:06:26 +01:00
cdimauro
96fbf63d0b Reduce KuCoin logs on DDosProtection error messages
KuCoin APIs generate A LOT of error messages.
Consequently, logs are flooded with lines like:
2021-12-25 22:30:23 freqtrade.exchange.common: WARNING -
_async_get_candle_history() returned exception:
"kucoin GET https://openapi-v2.kucoin.com/api/v1/market/candles?
symbol=PDEX-USDT&type=5min&startAt=1640317818&endAt=1640467818
429 Too Many Requests {"code":"429000","msg":"Too Many Requests"}"
2021-12-25 22:30:23 freqtrade.exchange.common: WARNING -
retrying _async_get_candle_history() still for 3 times
2021-12-25 22:30:23 freqtrade.exchange.common: WARNING -
Kucoin 429 error, avoid triggering DDosProtection backoff delay.
2 tries left before giving up
2021-12-25 22:30:24 freqtrade.exchange.common: WARNING -
_async_get_candle_history() returned exception:
"kucoin GET https://openapi-v2.kucoin.com/api/v1/market/candles?
symbol=UBX-USDT&type=5min&startAt=1640317821&endAt=1640467821
429 Too Many Requests {"code":"429000","msg":"Too Many Requests"}"

Messages like:
Kucoin 429 error, avoid triggering DDosProtection backoff delay.
are logged only once for a certain period of time (default is 3600 seconds).
2021-12-25 22:32:22 +01:00
Reigo Reinmets
aa54592ec7 Merge branch 'freqtrade:develop' into dca 2021-12-25 21:06:26 +02:00
Matthias
2917cc1f2e Bitpanda's "fetch_my_trades" requires "to" argument
closes #4938
2021-12-25 14:28:22 +01:00
Matthias
6fdad8c6bd Prevent exception, ensure deletion occurs 2021-12-25 14:03:44 +01:00
Matthias
356b2d3d91 Reestablish backward compatibility 2021-12-25 13:47:28 +01:00
Matthias
b1feb69ca9 Use Pathlib to delete testfile 2021-12-25 10:30:59 +01:00
Matthias
49aa34c6f3 Merge pull request #6112 from xataxxx/develop
Fix test not running when user_data contains historical data.
2021-12-25 10:11:15 +01:00
Reigo Reinmets
ea79eb55e9 Remove this test change from DCA branch. 2021-12-25 10:43:25 +02:00
Reigo Reinmets
d11a8928d4 Fix test not running when user_data contains historical data. 2021-12-25 10:39:27 +02:00
Reigo Reinmets
3cbb2ff31f Fix up documentation. 2021-12-25 10:35:08 +02:00
Stefano Ariestasia
e3181748dc Add ignore_buying_expired_candle to config_full example 2021-12-25 12:24:12 +09:00
Reigo Reinmets
f61aaa8c0d Improve documentation example 2021-12-24 19:02:39 +02:00
Reigo Reinmets
ad247b2f07 Merge branch 'freqtrade:develop' into dca 2021-12-24 12:39:09 +02:00
Reigo Reinmets
de79d25caf Refactoring to use strategy based configuration 2021-12-24 12:38:43 +02:00
Matthias
58663180e0 Merge pull request #6107 from freqtrade/remove_slack
Update CI to notify on discord only
2021-12-23 21:50:49 +01:00
Matthias
98f6d2d722 Update CI to notify on discord only 2021-12-23 21:27:30 +01:00
Matthias
110e48c541 Remove travis config file
Travisci seems to no longer offer a free plan for open source
repositories, and other repositories report the need to get in touch
with support again and again.

This complication is not necessary with github actions, which covers our
CI needs well.
2021-12-23 20:38:07 +01:00
Matthias
61dbb6206f Slightly reduce verbosity when reload_conf is issued
part of #6095
2021-12-23 20:33:13 +01:00
Reigo Reinmets
ac690e9215 Remove unnecessary returns. 2021-12-23 18:49:11 +02:00
Matthias
9a9cc31d83 Update docs regarding multiarch builds 2021-12-23 17:01:44 +01:00
Reigo Reinmets
0c4664e8f4 Lock file is not always left behind so handle it. 2021-12-23 17:39:43 +02:00
Reigo Reinmets
bc60139ae3 I really should make this flake8 / isort check automatic before commit. 2021-12-23 16:40:47 +02:00
Reigo Reinmets
8393c99b62 Whoops, missing a line. 2021-12-23 16:25:27 +02:00
Reigo Reinmets
8bf1001b33 Fix test failing when user_data already contains data... 2021-12-23 12:41:37 +02:00
Reigo Reinmets
ace0a83c0c Allow forcebuy to also buy more when trade is already open. 2021-12-23 11:57:53 +02:00
Reigo Reinmets
2e23e88fc1 Re-add back the log i accidentally removed. 2021-12-22 11:49:43 +02:00
Reigo Reinmets
d70ddeef9a Remove whitespace. Darn IntelliJ. 2021-12-22 11:43:48 +02:00
Reigo Reinmets
e439ae1fea Update wallet balance on every order close, not only trade close 2021-12-22 11:20:03 +02:00
Reigo Reinmets
da2e07b7fe Unittest base_stake_amount_ratio 2021-12-22 02:42:44 +02:00
Reigo Reinmets
76e7bf6cd2 Merge branch 'freqtrade:develop' into dca 2021-12-22 02:24:21 +02:00
Reigo Reinmets
7df3e7ada4 Add base_stake_amount_ratio config param to support unlimited stakes. 2021-12-22 02:19:11 +02:00
Reigo Reinmets
fa01cbf546 iSort 2021-12-21 22:23:01 +02:00
Matthias
f88b6af26f Merge pull request #6070 from cdimauro/suppress_logs
Suppress additional logs for pairs in blacklist
2021-12-21 21:07:15 +01:00
Matthias
e5aaef6440 Fix CI failure 2021-12-21 19:20:09 +01:00
cdimauro
6ba8b17fdd Use LoggingMixin.log_once to remove/reduce logs on pairlists 2021-12-21 09:11:57 +01:00
Reigo Reinmets
4862cdb296 Improve documentation. 2021-12-21 00:11:01 +02:00
Reigo Reinmets
c9243fb4f6 Use buy side for price since mostly used for DCA. 2021-12-20 22:45:46 +02:00
Reigo Reinmets
f6d36ce56b Fix the dca order not being counted bug. 2021-12-20 22:07:42 +02:00
Reigo Reinmets
d9f5694965 Merge branch 'freqtrade:develop' into dca 2021-12-20 22:05:58 +02:00
Matthias
40036bc710 Force dry-run for webserver backtest mode
closes #6094
2021-12-20 19:41:33 +01:00
Matthias
afad9be53f Merge pull request #6093 from freqtrade/dependabot/pip/develop/cryptography-36.0.1
Bump cryptography from 36.0.0 to 36.0.1
2021-12-20 09:01:25 +01:00
Matthias
6fe09b6dee Merge pull request #6090 from freqtrade/dependabot/pip/develop/mypy-0.920
Bump mypy from 0.910 to 0.920
2021-12-20 07:16:21 +01:00
dependabot[bot]
21da01f777 Bump cryptography from 36.0.0 to 36.0.1
Bumps [cryptography](https://github.com/pyca/cryptography) from 36.0.0 to 36.0.1.
- [Release notes](https://github.com/pyca/cryptography/releases)
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/36.0.0...36.0.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-20 05:39:04 +00:00
Matthias
260c627e99 Merge pull request #6091 from freqtrade/dependabot/pip/develop/time-machine-2.5.0
Bump time-machine from 2.4.1 to 2.5.0
2021-12-20 06:38:46 +01:00
Matthias
d47167c9c4 Merge pull request #6087 from freqtrade/dependabot/pip/develop/numpy-1.21.5
Bump numpy from 1.21.4 to 1.21.5
2021-12-20 06:38:25 +01:00
dependabot[bot]
b6f8765d3b Bump mypy from 0.910 to 0.920
Bumps [mypy](https://github.com/python/mypy) from 0.910 to 0.920.
- [Release notes](https://github.com/python/mypy/releases)
- [Commits](https://github.com/python/mypy/compare/v0.910...v0.920)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-20 05:38:03 +00:00
Matthias
5b608c9005 Merge pull request #6088 from freqtrade/dependabot/pip/develop/ccxt-1.64.44
Bump ccxt from 1.63.65 to 1.64.44
2021-12-20 06:37:59 +01:00
Matthias
cfad873ea7 Merge pull request #6092 from freqtrade/dependabot/pip/develop/jsonschema-4.3.1
Bump jsonschema from 4.2.1 to 4.3.1
2021-12-20 06:37:41 +01:00
Matthias
480eb55721 Merge pull request #6086 from freqtrade/dependabot/pip/develop/mkdocs-material-8.1.3
Bump mkdocs-material from 8.1.0 to 8.1.3
2021-12-20 06:37:23 +01:00
Matthias
e754cc09fc Merge pull request #6089 from freqtrade/dependabot/pip/develop/types-requests-2.26.2
Bump types-requests from 2.26.1 to 2.26.2
2021-12-20 06:36:46 +01:00
dependabot[bot]
cde35509db Bump jsonschema from 4.2.1 to 4.3.1
Bumps [jsonschema](https://github.com/Julian/jsonschema) from 4.2.1 to 4.3.1.
- [Release notes](https://github.com/Julian/jsonschema/releases)
- [Changelog](https://github.com/Julian/jsonschema/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/Julian/jsonschema/compare/v4.2.1...v4.3.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-20 03:01:52 +00:00
dependabot[bot]
5a3a5e98d6 Bump time-machine from 2.4.1 to 2.5.0
Bumps [time-machine](https://github.com/adamchainz/time-machine) from 2.4.1 to 2.5.0.
- [Release notes](https://github.com/adamchainz/time-machine/releases)
- [Changelog](https://github.com/adamchainz/time-machine/blob/main/HISTORY.rst)
- [Commits](https://github.com/adamchainz/time-machine/compare/2.4.1...2.5.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>
2021-12-20 03:01:47 +00:00
dependabot[bot]
44ac002cf0 Bump types-requests from 2.26.1 to 2.26.2
Bumps [types-requests](https://github.com/python/typeshed) from 2.26.1 to 2.26.2.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-20 03:01:41 +00:00
dependabot[bot]
56d96d6cff Bump ccxt from 1.63.65 to 1.64.44
Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.63.65 to 1.64.44.
- [Release notes](https://github.com/ccxt/ccxt/releases)
- [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg)
- [Commits](https://github.com/ccxt/ccxt/compare/1.63.65...1.64.44)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-20 03:01:40 +00:00
dependabot[bot]
36632b48c7 Bump numpy from 1.21.4 to 1.21.5
Bumps [numpy](https://github.com/numpy/numpy) from 1.21.4 to 1.21.5.
- [Release notes](https://github.com/numpy/numpy/releases)
- [Changelog](https://github.com/numpy/numpy/blob/main/doc/HOWTO_RELEASE.rst.txt)
- [Commits](https://github.com/numpy/numpy/compare/v1.21.4...v1.21.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-20 03:01:34 +00:00
dependabot[bot]
1b3aaffef4 Bump mkdocs-material from 8.1.0 to 8.1.3
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 8.1.0 to 8.1.3.
- [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/8.1.0...8.1.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-20 03:01:26 +00:00
Reigo Reinmets
b8b5e93000 Merge branch 'freqtrade:develop' into dca 2021-12-19 18:25:53 +02:00
Reigo Reinmets
f28d95ffb5 Add test for position adjust 2021-12-19 12:27:17 +02:00
Reigo Reinmets
5da38f3613 Fix typo. Make sure trade is market open. 2021-12-19 10:36:47 +02:00
Matthias
1cbc4da72b Merge pull request #6082 from Rikj000/docs/hyperopt-import-any
📝 Docs - Added `Any` import
2021-12-19 07:54:49 +01:00
Rik Helsen
58c3d69d14 📝 Docs - Added Any import 2021-12-18 23:29:55 +01:00
Reigo Reinmets
3aca3a7133 Use parentheses instead of backslash 2021-12-18 18:55:47 +02:00
Reigo Reinmets
1eb83f9a62 Fix documentation formatting. 2021-12-18 18:55:12 +02:00
Reigo Reinmets
db2f0660fa Some more compatibility fixes. 2021-12-18 11:15:59 +02:00
Reigo Reinmets
b094430c26 Restructure for less complexity. Flake8 2021-12-18 11:01:06 +02:00
Reigo Reinmets
30673f84f9 Flake8 compatibility 2021-12-18 11:00:25 +02:00
Reigo Reinmets
cc28f73d7f Hopefully fix orders being left lingering and trade not updating once they are complete 2021-12-17 22:29:41 +02:00
Reigo Reinmets
d10fb95fce Fix typo 2021-12-17 22:27:10 +02:00
Reigo Reinmets
cea023399e Merge branch 'freqtrade:develop' into dca 2021-12-17 21:59:58 +02:00
Reigo Reinmets
462270bc5a Fix a case where the amount was not recalculated. Added additional temporary logging. 2021-12-16 22:57:56 +02:00
Matthias
ea38b58081 Add base_currency to allowed webhook fields
closes #6075
2021-12-16 20:18:01 +01:00
Reigo Reinmets
337af44901 Merge branch 'freqtrade:develop' into dca 2021-12-16 20:02:14 +02:00
Matthias
b2fc3e814e Merge pull request #6055 from freqtrade/blacklist_delete
Add Blacklist delete
2021-12-16 13:41:18 +01:00
Matthias
39f0a17e62 Fix formatting 2021-12-16 07:11:35 +01:00
Reigo Reinmets
7200659b35 Merge branch 'freqtrade:develop' into dca 2021-12-15 23:17:44 +02:00
Matthias
f9aa36f291 Don't hard-fail when executing emergency sell fails
closes #6068
2021-12-15 19:37:35 +01:00
Matthias
b80b5ed1ad Improve uri_logging test
part of #6069
2021-12-15 19:25:30 +01:00
Reigo Reinmets
a7c67e8c7c Merge branch 'freqtrade:develop' into dca 2021-12-15 08:32:12 +02:00
cdimauro
9d8646072c Add test case for checking removal of logs for pains in blacklist 2021-12-14 06:23:40 +01:00
Matthias
dda302eea2 Merge pull request #6026 from freqtrade/dependabot/pip/develop/ta-lib-0.4.22
Bump ta-lib from 0.4.21 to 0.4.22
2021-12-13 19:44:46 +01:00
Reigo Reinmets
9be29c6e92 Theoretically fix second order timeout/canceling deleting the whole order. 2021-12-13 20:44:18 +02:00
Reigo Reinmets
468076cf54 This has to be reset since otherwise it will not handle live limit orders after first buy. 2021-12-13 20:32:13 +02:00
Matthias
793d090561 Improve log message wording for rejected stake amounts
closes #6064
2021-12-13 19:29:07 +01:00
Matthias
95949bd466 Update windows wheels to ta-lib 0.4.22 2021-12-13 19:05:35 +01:00
Reigo Reinmets
d4b31263ca Fix open rate being None formatting error. 2021-12-13 13:54:01 +02:00
Reigo Reinmets
6f6e7467f5 Fix potential problem. 2021-12-13 11:17:24 +02:00
Matthias
1d0af074ac Merge pull request #6061 from freqtrade/dependabot/pip/develop/ccxt-1.63.65
Bump ccxt from 1.63.55 to 1.63.65
2021-12-13 06:51:17 +01:00
Matthias
f2d55a91cd Merge pull request #6063 from freqtrade/dependabot/pip/develop/fastapi-0.70.1
Bump fastapi from 0.70.0 to 0.70.1
2021-12-13 06:51:05 +01:00
Matthias
5371458c99 Merge pull request #6062 from freqtrade/dependabot/pip/develop/pandas-1.3.5
Bump pandas from 1.3.4 to 1.3.5
2021-12-13 06:50:32 +01:00
dependabot[bot]
884a04c7fe Bump fastapi from 0.70.0 to 0.70.1
Bumps [fastapi](https://github.com/tiangolo/fastapi) from 0.70.0 to 0.70.1.
- [Release notes](https://github.com/tiangolo/fastapi/releases)
- [Commits](https://github.com/tiangolo/fastapi/compare/0.70.0...0.70.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-13 03:01:49 +00:00
dependabot[bot]
172b9383c0 Bump pandas from 1.3.4 to 1.3.5
Bumps [pandas](https://github.com/pandas-dev/pandas) from 1.3.4 to 1.3.5.
- [Release notes](https://github.com/pandas-dev/pandas/releases)
- [Changelog](https://github.com/pandas-dev/pandas/blob/master/RELEASE.md)
- [Commits](https://github.com/pandas-dev/pandas/compare/v1.3.4...v1.3.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-13 03:01:44 +00:00
dependabot[bot]
ec4a24649c Bump ccxt from 1.63.55 to 1.63.65
Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.63.55 to 1.63.65.
- [Release notes](https://github.com/ccxt/ccxt/releases)
- [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg)
- [Commits](https://github.com/ccxt/ccxt/compare/1.63.55...1.63.65)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-13 03:01:38 +00:00
Reigo Reinmets
1362bd9626 Fix potential problem. 2021-12-13 02:46:37 +02:00
Reigo Reinmets
2c3e5fa080 Remove extra logging. 2021-12-13 02:30:29 +02:00
Reigo Reinmets
1017b68af9 Fix some unit-tests. Use common trade entry code. 2021-12-13 02:27:09 +02:00
Reigo Reinmets
98255c18cf Merge branch 'freqtrade:develop' into dca 2021-12-13 02:10:13 +02:00
Matthias
3398469e55 Update PerformanceFilter to have min_profit as ratio again.
closes #6056
2021-12-12 13:21:36 +01:00
cdimauro
8dd3128ed4 Add type annotation to new logs suppression code 2021-12-12 12:32:09 +01:00
cdimauro
5b998aeca7 Remove unused import
Remove the import from copy, since deepcopy() isn't used anymore
(list.copy() is used instead).
2021-12-12 10:21:54 +01:00
cdimauro
878e16545d Suppress additional logs for pairs in blacklist
Every time that there's freqtrade "ticks", pairs in the blacklist are
checked and a warning message is displayed.
So, the logs are continuously flooded with the same warnings.

For example:
2021-07-26 06:24:45 freqtrade.plugins.pairlistmanager: WARNING -
Pair XTZUP/USDT in your blacklist. Removing it from whitelist...
2021-07-26 06:24:45 freqtrade.plugins.pairlistmanager: WARNING -
Pair SUSHIUP/USDT in your blacklist. Removing it from whitelist...
2021-07-26 06:24:45 freqtrade.plugins.pairlistmanager: WARNING -
Pair XTZDOWN/USDT in your blacklist. Removing it from whitelist...
2021-07-26 06:24:50 freqtrade.plugins.pairlistmanager: WARNING -
Pair XTZUP/USDT in your blacklist. Removing it from whitelist...
2021-07-26 06:24:50 freqtrade.plugins.pairlistmanager: WARNING -
Pair SUSHIUP/USDT in your blacklist. Removing it from whitelist...
2021-07-26 06:24:50 freqtrade.plugins.pairlistmanager: WARNING -
Pair XTZDOWN/USDT in your blacklist. Removing it from whitelist...

This patch shows the warning only the first time, by keeping track
of which pairs in the blacklist were already logged.
2021-12-12 10:20:08 +01:00
Reigo Reinmets
c6256aba35 Improve documentation. 2021-12-12 08:37:03 +02:00
Reigo Reinmets
8dacd987b9 Merge branch 'freqtrade:develop' into dca 2021-12-12 08:31:38 +02:00
Matthias
c12f2378db Merge pull request #6045 from freqtrade/trade_fee_fallback_value
Add unknown_fee_rate parameter
2021-12-11 20:00:01 +01:00
Matthias
1a4b403792 Merge pull request #6047 from freqtrade/dependabot/pip/develop/uvicorn-0.16.0
Bump uvicorn from 0.15.0 to 0.16.0
2021-12-11 19:50:18 +01:00
Matthias
b90c5e56fb Fix webserver schema bug when running in webserver mode 2021-12-11 19:46:35 +01:00
Matthias
8fdef2900e Increment API version to let clients know this is now available 2021-12-11 19:41:30 +01:00
Matthias
2918032dac Merge pull request #6046 from freqtrade/dependabot/pip/develop/python-telegram-bot-13.9
Bump python-telegram-bot from 13.8.1 to 13.9
2021-12-11 19:41:14 +01:00
Reigo Reinmets
64558e60d3 Fix bug in example. 2021-12-11 19:45:30 +02:00
Reigo Reinmets
2e13893341 Merge branch 'freqtrade:develop' into dca 2021-12-11 18:28:05 +02:00
Matthias
06bd8a1540 Merge pull request #6052 from freqtrade/dependabot/github_actions/develop/peter-evans/dockerhub-description-2.4.3
Bump peter-evans/dockerhub-description from 2.1.0 to 2.4.3
2021-12-11 17:26:43 +01:00
Reigo Reinmets
9176e2f1f6 Merge branch 'freqtrade:develop' into dca 2021-12-11 18:26:11 +02:00
Reigo Reinmets
71147d2899 Attempt to support limit orders for position adjustment. 2021-12-11 18:25:05 +02:00
dependabot[bot]
58cd91bd80 Bump python-telegram-bot from 13.8.1 to 13.9
Bumps [python-telegram-bot](https://github.com/python-telegram-bot/python-telegram-bot) from 13.8.1 to 13.9.
- [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/v13.8.1...v13.9)

---
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>
2021-12-11 16:00:35 +00:00
Matthias
dbe97bcdb1 Merge pull request #6053 from freqtrade/dependabot/github_actions/develop/crazy-max/ghaction-docker-buildx-3.3.1
Bump crazy-max/ghaction-docker-buildx from 1 to 3.3.1
2021-12-11 16:59:56 +01:00
Matthias
843eec63f0 Merge pull request #6051 from freqtrade/dependabot/pip/develop/sqlalchemy-1.4.28
Bump sqlalchemy from 1.4.27 to 1.4.28
2021-12-11 16:59:42 +01:00
Matthias
0df8786af6 Merge pull request #6048 from freqtrade/dependabot/pip/develop/prompt-toolkit-3.0.24
Bump prompt-toolkit from 3.0.23 to 3.0.24
2021-12-11 16:35:07 +01:00
Matthias
b4ed90788b Merge pull request #6050 from freqtrade/dependabot/pip/develop/ccxt-1.63.55
Bump ccxt from 1.63.1 to 1.63.55
2021-12-11 16:34:36 +01:00
Matthias
c871e51dcc Merge pull request #6049 from freqtrade/dependabot/pip/develop/mkdocs-material-8.1.0
Bump mkdocs-material from 8.0.4 to 8.1.0
2021-12-11 16:34:28 +01:00
Matthias
857f4ec125 Remove exception-handlers which catch exceptions that are never raised 2021-12-11 16:20:09 +01:00
Reigo Reinmets
7d42f42405 Merge branch 'freqtrade:develop' into dca 2021-12-11 17:14:50 +02:00
Reigo Reinmets
f11a40f144 Improve documentation on adjust_trade_position and position_adjustment_enable 2021-12-11 17:14:04 +02:00
dependabot[bot]
783ee633aa Bump crazy-max/ghaction-docker-buildx from 1 to 3.3.1
Bumps [crazy-max/ghaction-docker-buildx](https://github.com/crazy-max/ghaction-docker-buildx) from 1 to 3.3.1.
- [Release notes](https://github.com/crazy-max/ghaction-docker-buildx/releases)
- [Changelog](https://github.com/crazy-max/ghaction-docker-buildx/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crazy-max/ghaction-docker-buildx/compare/v1...v3.3.1)

---
updated-dependencies:
- dependency-name: crazy-max/ghaction-docker-buildx
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-11 15:13:10 +00:00
dependabot[bot]
fb134c67a9 Bump peter-evans/dockerhub-description from 2.1.0 to 2.4.3
Bumps [peter-evans/dockerhub-description](https://github.com/peter-evans/dockerhub-description) from 2.1.0 to 2.4.3.
- [Release notes](https://github.com/peter-evans/dockerhub-description/releases)
- [Commits](https://github.com/peter-evans/dockerhub-description/compare/v2.1.0...v2.4.3)

---
updated-dependencies:
- dependency-name: peter-evans/dockerhub-description
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-11 15:13:08 +00:00
Matthias
849ca1ec06 Dependabot - use correct branch 2021-12-11 16:12:36 +01:00
Matthias
8da79d0ab2 Add blacklist-control to telegram 2021-12-11 16:12:24 +01:00
dependabot[bot]
aaf5f4ce39 Bump sqlalchemy from 1.4.27 to 1.4.28
Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 1.4.27 to 1.4.28.
- [Release notes](https://github.com/sqlalchemy/sqlalchemy/releases)
- [Changelog](https://github.com/sqlalchemy/sqlalchemy/blob/main/CHANGES)
- [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>
2021-12-11 14:55:27 +00:00
dependabot[bot]
ae92bf56bf Bump ccxt from 1.63.1 to 1.63.55
Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.63.1 to 1.63.55.
- [Release notes](https://github.com/ccxt/ccxt/releases)
- [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg)
- [Commits](https://github.com/ccxt/ccxt/compare/1.63.1...1.63.55)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-11 14:55:21 +00:00
dependabot[bot]
f47cfbd2a9 Bump mkdocs-material from 8.0.4 to 8.1.0
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 8.0.4 to 8.1.0.
- [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/8.0.4...8.1.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-11 14:55:13 +00:00
dependabot[bot]
c9c683f2b0 Bump prompt-toolkit from 3.0.23 to 3.0.24
Bumps [prompt-toolkit](https://github.com/prompt-toolkit/python-prompt-toolkit) from 3.0.23 to 3.0.24.
- [Release notes](https://github.com/prompt-toolkit/python-prompt-toolkit/releases)
- [Changelog](https://github.com/prompt-toolkit/python-prompt-toolkit/blob/master/CHANGELOG)
- [Commits](https://github.com/prompt-toolkit/python-prompt-toolkit/compare/3.0.23...3.0.24)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-11 14:55:09 +00:00
dependabot[bot]
81cafd090d Bump uvicorn from 0.15.0 to 0.16.0
Bumps [uvicorn](https://github.com/encode/uvicorn) from 0.15.0 to 0.16.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.15.0...0.16.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-11 14:55:05 +00:00
Matthias
671b9903d7 Add github-actions dependabot 2021-12-11 15:54:09 +01:00
Matthias
cc96db76f0 Add possibility to delete pairs from the pairlist via api 2021-12-11 15:53:44 +01:00
Matthias
e729fad99c Add unknown_fee_rate parameter 2021-12-11 15:26:08 +01:00
Matthias
e9c3f0cbbd Add note about python3.10 not being supported
closes #6041
2021-12-11 10:01:55 +01:00
Reigo Reinmets
f97662e816 Add position_adjustment_enable config keyword to enable it. 2021-12-11 00:28:12 +02:00
Reigo Reinmets
b7bf3247b8 Only adjust stoploss if it's set. 2021-12-10 23:17:12 +02:00
Reigo Reinmets
1e3fc5e984 Slight code touchup 2021-12-10 22:48:00 +02:00
Reigo Reinmets
c179951cca Expect stake_amount, not actual amount of pair from strategy for DCA. 2021-12-10 20:42:24 +02:00
Reigo Reinmets
b2c2852f86 Initial backtesting support. This does make it rather slow. 2021-12-09 23:21:35 +02:00
Reigo Reinmets
00366c5c88 Additional unit-tests 2021-12-09 20:03:41 +02:00
Reigo Reinmets
28d0b5165a Add unit-test 2021-12-09 19:47:24 +02:00
Reigo Reinmets
fde6779873 Some code improvements. Still some bugs. 2021-12-09 14:47:44 +02:00
Reigo Reinmets
88792852e4 Merge branch 'develop' of github.com:freqtrade/freqtrade into dca 2021-12-09 14:33:14 +02:00
Matthias
be6b1f6f83 Import from enums, not submodules 2021-12-09 06:18:21 +01:00
Matthias
b79f2f2981 Merge pull request #6035 from freqtrade/revert-6034-dependabot/docker/python-3.10.1-slim-bullseye
Revert "Bump python from 3.9.9-slim-bullseye to 3.10.1-slim-bullseye"
2021-12-09 06:18:11 +01:00
Matthias
facb5b3991 Revert "Bump python from 3.9.9-slim-bullseye to 3.10.1-slim-bullseye" 2021-12-09 06:17:56 +01:00
Matthias
79a87649b9 Merge pull request #6034 from freqtrade/dependabot/docker/python-3.10.1-slim-bullseye
Bump python from 3.9.9-slim-bullseye to 3.10.1-slim-bullseye
2021-12-09 06:15:16 +01:00
dependabot[bot]
7848e17a49 Bump python from 3.9.9-slim-bullseye to 3.10.1-slim-bullseye
Bumps python from 3.9.9-slim-bullseye to 3.10.1-slim-bullseye.

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-09 03:01:59 +00:00
Reigo Reinmets
fd875786fd Initial very crude DCA implementation attempt. Very alpha.
No backtesting support.
2021-12-07 11:16:11 +02:00
Matthias
decaa24f81 Merge pull request #6028 from freqtrade/dependabot/pip/develop/mkdocs-material-8.0.4
Bump mkdocs-material from 8.0.1 to 8.0.4
2021-12-06 07:03:50 +01:00
Matthias
f9529c1fb6 Merge pull request #6027 from freqtrade/dependabot/pip/develop/ccxt-1.63.1
Bump ccxt from 1.62.42 to 1.63.1
2021-12-06 06:22:58 +01:00
dependabot[bot]
3dda0ef2ef Bump mkdocs-material from 8.0.1 to 8.0.4
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 8.0.1 to 8.0.4.
- [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/8.0.1...8.0.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-06 03:01:35 +00:00
dependabot[bot]
50a6eaea22 Bump ccxt from 1.62.42 to 1.63.1
Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.62.42 to 1.63.1.
- [Release notes](https://github.com/ccxt/ccxt/releases)
- [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg)
- [Commits](https://github.com/ccxt/ccxt/compare/1.62.42...1.63.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-06 03:01:30 +00:00
dependabot[bot]
61211a1194 Bump ta-lib from 0.4.21 to 0.4.22
Bumps [ta-lib](https://github.com/mrjbq7/ta-lib) from 0.4.21 to 0.4.22.
- [Release notes](https://github.com/mrjbq7/ta-lib/releases)
- [Changelog](https://github.com/mrjbq7/ta-lib/blob/master/CHANGELOG)
- [Commits](https://github.com/mrjbq7/ta-lib/compare/TA_Lib-0.4.21...TA_Lib-0.4.22)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-06 03:01:18 +00:00
Matthias
fbd64d757d Improve doc wording 2021-12-05 09:26:44 +01:00
Matthias
4278c5a24a add note about arm64 installation
closes #6025
2021-12-05 09:24:40 +01:00
Matthias
243e59cabb Merge pull request #5929 from dvdmchl/develop
Telegram and log prints strategy version.
2021-12-04 15:16:42 +01:00
Matthias
210202a797 Merge pull request #5756 from GluTbl/patch-1
add custom entry/exit price support to backtesting
2021-12-04 15:16:15 +01:00
Matthias
c981cc335d Remove wrong comment 2021-12-04 14:51:55 +01:00
Matthias
d0467b30ba Add strategy_version to API response 2021-12-04 14:49:45 +01:00
Matthias
e3190cf8a8 Update documentation 2021-12-04 14:44:03 +01:00
Matthias
848a2d5383 Merge branch 'develop' into pr/dvdmchl/5929 2021-12-04 14:40:15 +01:00
Matthias
2080bf0952 Fix some formatting errors, add test for strategy version 2021-12-04 14:40:05 +01:00
Matthias
68ac8008ec Call custom_exit_price only for sell_signal and custom_sell 2021-12-04 14:14:22 +01:00
Matthias
84ad176287 Improve documentation wording 2021-12-04 13:33:38 +01:00
Matthias
86910b58dc Bracket entry/exit prices to low/high of the candle 2021-12-03 17:44:53 +01:00
Matthias
d1209fe415 Merge branch 'develop' into pr/GluTbl/5756 2021-12-03 17:37:44 +01:00
Matthias
d09a30cc67 OrderTypeValues should be in enums 2021-12-03 15:34:28 +01:00
Matthias
ad5c8f601c Simplify datahandler classes by exploiting commonalities 2021-12-02 20:19:22 +01:00
Matthias
d3ad4fb52e Don't crash dry-run if orderbook side is empty
closes #6018
2021-12-02 19:17:47 +01:00
Matthias
294c98ed5e Document exchange.uid
part of #6016
2021-12-02 06:55:08 +01:00
Matthias
c1fed8a077 Merge pull request #6014 from freqtrade/double_notifications
Double notifications
2021-12-02 06:39:18 +01:00
Matthias
0375a08302 use to_hdf instead of HDFStore 2021-12-01 20:32:23 +01:00
Matthias
5ce1eeecf5 Reorder messages to be sent in correct order
buy first, then buy fill,
sell first, then sell fill.
2021-12-01 19:57:24 +01:00
Matthias
c22f381dfe Fix Schema issue
closes #6010
2021-11-30 20:46:47 +01:00
Matthias
542963c7a6 Reduce code complexity by combining buy and buy_fill methods 2021-11-30 19:45:20 +01:00
Matthias
f0abe218a2 Batch ohlcv requests to not overwelm ccxt's async throttler
closes #6003
2021-11-30 07:10:12 +01:00
Matthias
231b1e2f57 Improve Async error message content 2021-11-30 07:10:12 +01:00
Matthias
de7e1e6bf7 Merge pull request #5980 from incrementby1/ShuffleFilterDetectLiveMode
Shuffle filter use seed only in backtesting mode
2021-11-30 06:37:35 +01:00
incrementby1
85b1f6f6b3 Update pairlists.md 2021-11-29 20:44:51 +01:00
incrementby1
60eca8b1f1 revert to random object 2021-11-29 20:35:43 +01:00
Matthias
06d8217e62 Merge pull request #5983 from PostmanSpat/webhook-raw-retry
Added raw config and retry config to webhook
2021-11-29 20:30:06 +01:00
Matthias
dfb148f8d7 Fix formatting 2021-11-29 19:54:54 +01:00
Matthias
f8cb3d2901 Restore openAPI functioning 2021-11-29 19:52:40 +01:00
Matthias
bd8348451e Merge pull request #5999 from freqtrade/dependabot/pip/develop/mkdocs-material-8.0.1
Bump mkdocs-material from 7.3.6 to 8.0.1
2021-11-29 19:50:59 +01:00
Matthias
0f15340269 Merge pull request #5995 from freqtrade/dependabot/pip/develop/aiofiles-0.8.0
Bump aiofiles from 0.7.0 to 0.8.0
2021-11-29 19:32:45 +01:00
Matthias
2e51477455 Update mkdocs file to 8.0 2021-11-29 19:32:16 +01:00
Spat
018407852a Added missing webhook config params to constants 2021-11-29 18:17:59 +11:00
Matthias
56b4457a9c Merge pull request #5996 from freqtrade/dependabot/pip/develop/time-machine-2.4.1
Bump time-machine from 2.4.0 to 2.4.1
2021-11-29 08:07:30 +01:00
Matthias
2db064d8f7 Merge pull request #6000 from stash86/fix-docs
Add few sentences to make clear about backtest + pairlist handlers
2021-11-29 07:09:36 +01:00
Matthias
f0bf9b51dc Merge pull request #5992 from freqtrade/dependabot/pip/develop/ccxt-1.62.42
Bump ccxt from 1.61.92 to 1.62.42
2021-11-29 07:08:46 +01:00
dependabot[bot]
57e55eb938 Bump time-machine from 2.4.0 to 2.4.1
Bumps [time-machine](https://github.com/adamchainz/time-machine) from 2.4.0 to 2.4.1.
- [Release notes](https://github.com/adamchainz/time-machine/releases)
- [Changelog](https://github.com/adamchainz/time-machine/blob/main/HISTORY.rst)
- [Commits](https://github.com/adamchainz/time-machine/compare/2.4.0...2.4.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>
2021-11-29 06:00:32 +00:00
Matthias
5ee5600cb9 Merge pull request #5993 from freqtrade/dependabot/pip/develop/scipy-1.7.3
Bump scipy from 1.7.2 to 1.7.3
2021-11-29 06:59:51 +01:00
Matthias
828ab874c1 Merge pull request #5991 from freqtrade/dependabot/pip/develop/prompt-toolkit-3.0.23
Bump prompt-toolkit from 3.0.22 to 3.0.23
2021-11-29 06:59:30 +01:00
Matthias
90892e5a89 Merge pull request #5997 from freqtrade/dependabot/pip/develop/types-python-dateutil-2.8.3
Bump types-python-dateutil from 2.8.2 to 2.8.3
2021-11-29 06:59:14 +01:00
Matthias
180df0514f Merge pull request #5998 from freqtrade/dependabot/pip/develop/types-requests-2.26.1
Bump types-requests from 2.26.0 to 2.26.1
2021-11-29 06:58:55 +01:00
Matthias
731208936f Merge pull request #5994 from freqtrade/dependabot/pip/develop/types-cachetools-4.2.6
Bump types-cachetools from 4.2.5 to 4.2.6
2021-11-29 06:58:41 +01:00
Stefano Ariestasia
3b4051488f Merge branch 'fix-docs' of https://github.com/stash86/freqtrade into fix-docs 2021-11-29 14:32:37 +09:00
Stefano Ariestasia
c126d2530a Add few sentences on docs
- Add warning that PrecisionFilter can't be used on backtest that use multiple strategies
- Add note that not all pairlist handlers can be used on backtest
2021-11-29 14:32:33 +09:00
dependabot[bot]
24997fb36f Bump mkdocs-material from 7.3.6 to 8.0.1
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 7.3.6 to 8.0.1.
- [Release notes](https://github.com/squidfunk/mkdocs-material/releases)
- [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG)
- [Upgrade guide](https://github.com/squidfunk/mkdocs-material/blob/master/docs/upgrade.md)
- [Commits](https://github.com/squidfunk/mkdocs-material/compare/7.3.6...8.0.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-29 03:01:50 +00:00
dependabot[bot]
b81d768eb3 Bump types-requests from 2.26.0 to 2.26.1
Bumps [types-requests](https://github.com/python/typeshed) from 2.26.0 to 2.26.1.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-29 03:01:45 +00:00
dependabot[bot]
39c3175b69 Bump types-python-dateutil from 2.8.2 to 2.8.3
Bumps [types-python-dateutil](https://github.com/python/typeshed) from 2.8.2 to 2.8.3.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-29 03:01:43 +00:00
dependabot[bot]
b0b2fdba70 Bump aiofiles from 0.7.0 to 0.8.0
Bumps [aiofiles](https://github.com/Tinche/aiofiles) from 0.7.0 to 0.8.0.
- [Release notes](https://github.com/Tinche/aiofiles/releases)
- [Commits](https://github.com/Tinche/aiofiles/compare/v0.7.0...v0.8.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-29 03:01:38 +00:00
dependabot[bot]
c2a7b1930b Bump types-cachetools from 4.2.5 to 4.2.6
Bumps [types-cachetools](https://github.com/python/typeshed) from 4.2.5 to 4.2.6.
- [Release notes](https://github.com/python/typeshed/releases)
- [Commits](https://github.com/python/typeshed/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-29 03:01:36 +00:00
dependabot[bot]
589c9f55e0 Bump scipy from 1.7.2 to 1.7.3
Bumps [scipy](https://github.com/scipy/scipy) from 1.7.2 to 1.7.3.
- [Release notes](https://github.com/scipy/scipy/releases)
- [Commits](https://github.com/scipy/scipy/compare/v1.7.2...v1.7.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-29 03:01:33 +00:00
dependabot[bot]
e9e8023d73 Bump ccxt from 1.61.92 to 1.62.42
Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.61.92 to 1.62.42.
- [Release notes](https://github.com/ccxt/ccxt/releases)
- [Changelog](https://github.com/ccxt/ccxt/blob/master/exchanges.cfg)
- [Commits](https://github.com/ccxt/ccxt/compare/1.61.92...1.62.42)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-29 03:01:27 +00:00
dependabot[bot]
df09fe5df6 Bump prompt-toolkit from 3.0.22 to 3.0.23
Bumps [prompt-toolkit](https://github.com/prompt-toolkit/python-prompt-toolkit) from 3.0.22 to 3.0.23.
- [Release notes](https://github.com/prompt-toolkit/python-prompt-toolkit/releases)
- [Changelog](https://github.com/prompt-toolkit/python-prompt-toolkit/blob/master/CHANGELOG)
- [Commits](https://github.com/prompt-toolkit/python-prompt-toolkit/compare/3.0.22...3.0.23)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-29 03:01:20 +00:00
Spat
29180a1d2b Moved retry config to constants 2021-11-29 10:48:35 +11:00
Spat
0fa5bf54cd Changed comment 2021-11-29 10:30:41 +11:00
Matthias
cf5ff9257d Add plotconfig as property documentation and sample 2021-11-28 19:39:43 +01:00
incrementby1
c7d10e2c7e delete unneeded comment 2021-11-28 19:05:02 +01:00
Matthias
2414c0bd9f Merge pull request #5982 from stash86/fix-docs
add weekly and monthly to valid keys
2021-11-28 08:28:13 +01:00
Spat
fb6ae174b9 Added raw config and retry config to webhook 2021-11-28 11:42:57 +11:00
Stefano Ariestasia
fd9bf2adb0 add weekly and monthly to valid keys 2021-11-28 08:23:02 +09:00
Matthias
6429205d39 Improve Notebook documentation to include Dataprovider
fix #5975
2021-11-27 19:53:37 +01:00
Matthias
2b3e7eeb21 Use Enum values within bot code 2021-11-27 19:41:36 +01:00
Matthias
409a801763 Fix caching problem in refresh_ohlcv
closes #5978
2021-11-27 19:31:39 +01:00
incrementby1
b90303c9a3 Update ShuffleFilter.py
random.Random() is deprecated since 3.9
2021-11-27 18:26:30 +01:00
Matthias
cb95b362ec Merge pull request #5976 from freqtrade/forcebuy
allow force options with ordertype
2021-11-27 17:01:18 +01:00
incrementby1
62d248d182 Merge pull request #2 from incrementby1/ShuffleFilterDetectLiveModes
Update pairlists.md
2021-11-27 16:30:46 +01:00
incrementby1
2f0f576fce Update pairlists.md
ShuffleFilter will automatically detect runmodes and apply the `seed` only for backtesting modes - if ad `seed` value is set.
2021-11-27 16:28:41 +01:00
incrementby1
8c52ba3360 ShuffleFilterDetectLiveMode
# Apply seed in backtesting mode to get comparable results,
        # but not in live modes to get a non-repeating order of pairs during live modes.
2021-11-27 16:21:23 +01:00
Matthias
bc52b3db56 Properly handle None values via API 2021-11-27 09:26:14 +01:00
Matthias
80ed5283b2 Add forcesell market/limit distinction 2021-11-27 09:10:18 +01:00
Matthias
338fe333a9 Allow forcebuy to specify order_type 2021-11-24 20:20:58 +01:00
Dardon
d4fd13bf50 Telegram and log prints strategy version. 2021-11-20 16:26:07 +00:00
GluTbl
00406ea7d5 Update backtesting.py
Support for custom entry-prices and exit-prices during backtesting.
2021-10-19 17:15:45 +05:30
149 changed files with 3605 additions and 1184 deletions

View File

@@ -5,9 +5,17 @@ updates:
schedule:
interval: daily
open-pull-requests-limit: 10
- package-ecosystem: pip
directory: "/"
schedule:
interval: weekly
open-pull-requests-limit: 10
target-branch: develop
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
target-branch: develop

View File

@@ -3,9 +3,9 @@ name: Freqtrade CI
on:
push:
branches:
- master
- stable
- develop
- ci/*
tags:
release:
types: [published]
@@ -20,7 +20,7 @@ jobs:
strategy:
matrix:
os: [ ubuntu-18.04, ubuntu-20.04 ]
python-version: [3.7, 3.8, 3.9]
python-version: ["3.8", "3.9", "3.10"]
steps:
- uses: actions/checkout@v2
@@ -39,7 +39,7 @@ jobs:
- name: pip cache (linux)
uses: actions/cache@v2
if: startsWith(matrix.os, 'ubuntu')
if: runner.os == 'Linux'
with:
path: ~/.cache/pip
key: test-${{ matrix.os }}-${{ matrix.python-version }}-pip
@@ -50,8 +50,9 @@ jobs:
cd build_helpers && ./install_ta-lib.sh ${HOME}/dependencies/; cd ..
- name: Installation - *nix
if: runner.os == 'Linux'
run: |
python -m pip install --upgrade pip
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
@@ -69,7 +70,7 @@ jobs:
if: matrix.python-version == '3.9'
- name: Coveralls
if: (startsWith(matrix.os, 'ubuntu-20') && matrix.python-version == '3.8')
if: (runner.os == 'Linux' && matrix.python-version == '3.8')
env:
# Coveralls token. Not used as secret due to github not providing secrets to forked repositories
COVERALLS_REPO_TOKEN: 6D1m0xupS3FgutfuGao8keFf9Hc0FpIXu
@@ -101,23 +102,20 @@ jobs:
run: |
mypy freqtrade scripts
- name: Slack Notification
uses: lazy-actions/slatify@v3.0.0
- name: Discord notification
uses: rjstone/discord-webhook-notify@v1
if: failure() && ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false)
with:
type: ${{ job.status }}
job_name: '*Freqtrade CI ${{ matrix.os }}*'
mention: 'here'
mention_if: 'failure'
channel: '#notifications'
url: ${{ secrets.SLACK_WEBHOOK }}
severity: error
details: Freqtrade CI failed on ${{ matrix.os }}
webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
build_macos:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ macos-latest ]
python-version: [3.7, 3.8, 3.9]
python-version: ["3.8", "3.9", "3.10"]
steps:
- uses: actions/checkout@v2
@@ -136,7 +134,7 @@ jobs:
- name: pip cache (macOS)
uses: actions/cache@v2
if: startsWith(matrix.os, 'macOS')
if: runner.os == 'macOS'
with:
path: ~/Library/Caches/pip
key: test-${{ matrix.os }}-${{ matrix.python-version }}-pip
@@ -147,10 +145,11 @@ jobs:
cd build_helpers && ./install_ta-lib.sh ${HOME}/dependencies/; cd ..
- name: Installation - macOS
if: runner.os == 'macOS'
run: |
brew update
brew install hdf5 c-blosc
python -m pip install --upgrade pip
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
@@ -162,7 +161,7 @@ jobs:
pytest --random-order --cov=freqtrade --cov-config=.coveragerc
- name: Coveralls
if: (startsWith(matrix.os, 'ubuntu-20') && matrix.python-version == '3.8')
if: (runner.os == 'Linux' && matrix.python-version == '3.8')
env:
# Coveralls token. Not used as secret due to github not providing secrets to forked repositories
COVERALLS_REPO_TOKEN: 6D1m0xupS3FgutfuGao8keFf9Hc0FpIXu
@@ -194,17 +193,13 @@ jobs:
run: |
mypy freqtrade scripts
- name: Slack Notification
uses: lazy-actions/slatify@v3.0.0
- name: Discord notification
uses: rjstone/discord-webhook-notify@v1
if: failure() && ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false)
with:
type: ${{ job.status }}
job_name: '*Freqtrade CI ${{ matrix.os }}*'
mention: 'here'
mention_if: 'failure'
channel: '#notifications'
url: ${{ secrets.SLACK_WEBHOOK }}
severity: info
details: Test Succeeded!
webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
build_windows:
@@ -212,7 +207,7 @@ jobs:
strategy:
matrix:
os: [ windows-latest ]
python-version: [3.7, 3.8]
python-version: ["3.8", "3.9", "3.10"]
steps:
- uses: actions/checkout@v2
@@ -224,7 +219,6 @@ jobs:
- name: Pip cache (Windows)
uses: actions/cache@preview
if: startsWith(runner.os, 'Windows')
with:
path: ~\AppData\Local\pip\Cache
key: ${{ matrix.os }}-${{ matrix.python-version }}-pip
@@ -257,16 +251,13 @@ jobs:
run: |
mypy freqtrade scripts
- name: Slack Notification
uses: lazy-actions/slatify@v3.0.0
- name: Discord notification
uses: rjstone/discord-webhook-notify@v1
if: failure() && ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false)
with:
type: ${{ job.status }}
job_name: '*Freqtrade CI windows*'
mention: 'here'
mention_if: 'failure'
channel: '#notifications'
url: ${{ secrets.SLACK_WEBHOOK }}
severity: error
details: Test Failed
webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
docs_check:
runs-on: ubuntu-20.04
@@ -288,14 +279,13 @@ jobs:
pip install mkdocs
mkdocs build
- name: Slack Notification
uses: lazy-actions/slatify@v3.0.0
- name: Discord notification
uses: rjstone/discord-webhook-notify@v1
if: failure() && ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false)
with:
type: ${{ job.status }}
job_name: '*Freqtrade Docs*'
channel: '#notifications'
url: ${{ secrets.SLACK_WEBHOOK }}
severity: error
details: Freqtrade doc test failed!
webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
cleanup-prior-runs:
runs-on: ubuntu-20.04
@@ -306,7 +296,7 @@ jobs:
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
# Notify on slack only once - when CI completes (and after deploy) in case it's successfull
# Notify only once - when CI completes (and after deploy) in case it's successfull
notify-complete:
needs: [ build_linux, build_macos, build_windows, docs_check ]
runs-on: ubuntu-20.04
@@ -320,14 +310,13 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Slack Notification
uses: lazy-actions/slatify@v3.0.0
- name: Discord notification
uses: rjstone/discord-webhook-notify@v1
if: always() && steps.check.outputs.has-permission && ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false)
with:
type: ${{ job.status }}
job_name: '*Freqtrade CI*'
channel: '#notifications'
url: ${{ secrets.SLACK_WEBHOOK }}
severity: info
details: Test Completed!
webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
deploy:
needs: [ build_linux, build_macos, build_windows, docs_check ]
@@ -385,7 +374,7 @@ jobs:
- name: Set up Docker Buildx
id: buildx
uses: crazy-max/ghaction-docker-buildx@v1
uses: crazy-max/ghaction-docker-buildx@v3.3.1
with:
buildx-version: latest
qemu-version: latest
@@ -400,17 +389,13 @@ jobs:
run: |
build_helpers/publish_docker_multi.sh
- name: Slack Notification
uses: lazy-actions/slatify@v3.0.0
- name: Discord notification
uses: rjstone/discord-webhook-notify@v1
if: always() && ( github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false)
with:
type: ${{ job.status }}
job_name: '*Freqtrade CI Deploy*'
mention: 'here'
mention_if: 'failure'
channel: '#notifications'
url: ${{ secrets.SLACK_WEBHOOK }}
severity: info
details: Deploy Succeeded!
webhookUrl: ${{ secrets.DISCORD_WEBHOOK }}
deploy_arm:

View File

@@ -10,7 +10,7 @@ jobs:
steps:
- uses: actions/checkout@v1
- name: Docker Hub Description
uses: peter-evans/dockerhub-description@v2.1.0
uses: peter-evans/dockerhub-description@v2.4.3
env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKER_USERNAME }}
DOCKERHUB_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}

View File

@@ -1,55 +0,0 @@
os:
- linux
dist: bionic
language: python
python:
- 3.8
services:
- docker
env:
global:
- IMAGE_NAME=freqtradeorg/freqtrade
install:
- cd build_helpers && ./install_ta-lib.sh ${HOME}/dependencies; cd ..
- 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 .
jobs:
include:
- stage: tests
script:
- pytest --random-order --cov=freqtrade --cov-config=.coveragerc
# Allow failure for coveralls
# - coveralls || true
name: pytest
- script:
- cp config_examples/config_bittrex.example.json config.json
- freqtrade create-userdir --userdir user_data
- freqtrade backtesting --datadir tests/testdata --strategy SampleStrategy
name: backtest
- script:
- cp config_examples/config_bittrex.example.json config.json
- freqtrade create-userdir --userdir user_data
- freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt-loss SharpeHyperOptLossDaily
name: hyperopt
- script: flake8
name: flake8
- script:
# Test Documentation boxes -
# !!! <TYPE>: is not allowed!
# !!! <TYPE> "title" - Title needs to be quoted!
- grep -Er '^!{3}\s\S+:|^!{3}\s\S+\s[^"]' docs/*; test $? -ne 0
name: doc syntax
- script: mypy freqtrade scripts
name: mypy
notifications:
slack:
secure: bKLXmOrx8e2aPZl7W8DA5BdPAXWGpI5UzST33oc1G/thegXcDVmHBTJrBs4sZak6bgAclQQrdZIsRd2eFYzHLalJEaw6pk7hoAw8SvLnZO0ZurWboz7qg2+aZZXfK4eKl/VUe4sM9M4e/qxjkK+yWG7Marg69c4v1ypF7ezUi1fPYILYw8u0paaiX0N5UX8XNlXy+PBlga2MxDjUY70MuajSZhPsY2pDUvYnMY1D/7XN3cFW0g+3O8zXjF0IF4q1Z/1ASQe+eYjKwPQacE+O8KDD+ZJYoTOFBAPllrtpO1jnOPFjNGf3JIbVMZw4bFjIL0mSQaiSUaUErbU3sFZ5Or79rF93XZ81V7uEZ55vD8KMfR2CB1cQJcZcj0v50BxLo0InkFqa0Y8Nra3sbpV4fV5Oe8pDmomPJrNFJnX6ULQhQ1gTCe0M5beKgVms5SITEpt4/Y0CmLUr6iHDT0CUiyMIRWAXdIgbGh1jfaWOMksybeRevlgDsIsNBjXmYI1Sw2ZZR2Eo2u4R6zyfyjOMLwYJ3vgq9IrACv2w5nmf0+oguMWHf6iWi2hiOqhlAN1W74+3HsYQcqnuM3LGOmuCnPprV1oGBqkPXjIFGpy21gNx4vHfO1noLUyJnMnlu2L7SSuN1CdLsnjJ1hVjpJjPfqB4nn8g12x87TqM1bOm+3Q=
cache:
pip: True
directories:
- $HOME/dependencies

View File

@@ -49,7 +49,7 @@ Please find the complete documentation on the [freqtrade website](https://www.fr
## Features
- [x] **Based on Python 3.7+**: For botting on any operating system - Windows, macOS and Linux.
- [x] **Based on Python 3.8+**: For botting on any operating system - Windows, macOS and Linux.
- [x] **Persistence**: Persistence is achieved through sqlite.
- [x] **Dry-run**: Run the bot without paying money.
- [x] **Backtesting**: Run a simulation of your buy/sell strategy.
@@ -197,7 +197,7 @@ To run this bot we recommend you a cloud instance with a minimum of:
### Software requirements
- [Python 3.7.x](http://docs.python-guide.org/en/latest/starting/installation/)
- [Python >= 3.8](http://docs.python-guide.org/en/latest/starting/installation/)
- [pip](https://pip.pypa.io/en/stable/installing/)
- [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
- [TA-Lib](https://mrjbq7.github.io/ta-lib/install.html)

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,19 +1,18 @@
# Downloads don't work automatically, since the URL is regenerated via javascript.
# Downloaded from https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib
python -m pip install --upgrade pip
python -m pip install --upgrade pip wheel
$pyv = python -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')"
if ($pyv -eq '3.7') {
pip install build_helpers\TA_Lib-0.4.21-cp37-cp37m-win_amd64.whl
}
if ($pyv -eq '3.8') {
pip install build_helpers\TA_Lib-0.4.21-cp38-cp38-win_amd64.whl
pip install build_helpers\TA_Lib-0.4.24-cp38-cp38-win_amd64.whl
}
if ($pyv -eq '3.9') {
pip install build_helpers\TA_Lib-0.4.21-cp39-cp39-win_amd64.whl
pip install build_helpers\TA_Lib-0.4.24-cp39-cp39-win_amd64.whl
}
if ($pyv -eq '3.10') {
pip install build_helpers\TA_Lib-0.4.24-cp310-cp310-win_amd64.whl
}
pip install -r requirements-dev.txt
pip install -e .

View File

@@ -9,7 +9,9 @@
"cancel_open_orders_on_exit": false,
"unfilledtimeout": {
"buy": 10,
"sell": 30
"sell": 10,
"exit_timeout_count": 0,
"unit": "minutes"
},
"bid_strategy": {
"ask_last_balance": 0.0,

View File

@@ -9,7 +9,9 @@
"cancel_open_orders_on_exit": false,
"unfilledtimeout": {
"buy": 10,
"sell": 30
"sell": 10,
"exit_timeout_count": 0,
"unit": "minutes"
},
"bid_strategy": {
"use_order_book": true,

View File

@@ -9,7 +9,9 @@
"cancel_open_orders_on_exit": false,
"unfilledtimeout": {
"buy": 10,
"sell": 30
"sell": 10,
"exit_timeout_count": 0,
"unit": "minutes"
},
"bid_strategy": {
"ask_last_balance": 0.0,

View File

@@ -18,6 +18,7 @@
"sell_profit_only": false,
"sell_profit_offset": 0.0,
"ignore_roi_if_buy_signal": false,
"ignore_buying_expired_candle_after": 300,
"minimal_roi": {
"40": 0.0,
"30": 0.01,
@@ -27,7 +28,7 @@
"stoploss": -0.10,
"unfilledtimeout": {
"buy": 10,
"sell": 30,
"sell": 10,
"exit_timeout_count": 0,
"unit": "minutes"
},

View File

@@ -9,7 +9,9 @@
"cancel_open_orders_on_exit": false,
"unfilledtimeout": {
"buy": 10,
"sell": 30
"sell": 10,
"exit_timeout_count": 0,
"unit": "minutes"
},
"bid_strategy": {
"use_order_book": true,

View File

@@ -13,7 +13,7 @@ A sample of this can be found below, which is identical to the Default Hyperopt
``` python
from datetime import datetime
from typing import Dict
from typing import Any, Dict
from pandas import DataFrame
@@ -105,7 +105,7 @@ You can define your own estimator for Hyperopt by implementing `generate_estimat
```python
class MyAwesomeStrategy(IStrategy):
class HyperOpt:
def generate_estimator():
def generate_estimator(dimensions: List['Dimension'], **kwargs):
return "RF"
```
@@ -119,13 +119,34 @@ Example for `ExtraTreesRegressor` ("ET") with additional parameters:
```python
class MyAwesomeStrategy(IStrategy):
class HyperOpt:
def generate_estimator():
def generate_estimator(dimensions: List['Dimension'], **kwargs):
from skopt.learning import ExtraTreesRegressor
# Corresponds to "ET" - but allows additional parameters.
return ExtraTreesRegressor(n_estimators=100)
```
The `dimensions` parameter is the list of `skopt.space.Dimension` objects corresponding to the parameters to be optimized. It can be used to create isotropic kernels for the `skopt.learning.GaussianProcessRegressor` estimator. Here's an example:
```python
class MyAwesomeStrategy(IStrategy):
class HyperOpt:
def generate_estimator(dimensions: List['Dimension'], **kwargs):
from skopt.utils import cook_estimator
from skopt.learning.gaussian_process.kernels import (Matern, ConstantKernel)
kernel_bounds = (0.0001, 10000)
kernel = (
ConstantKernel(1.0, kernel_bounds) *
Matern(length_scale=np.ones(len(dimensions)), length_scale_bounds=[kernel_bounds for d in dimensions], nu=2.5)
)
kernel += (
ConstantKernel(1.0, kernel_bounds) *
Matern(length_scale=np.ones(len(dimensions)), length_scale_bounds=[kernel_bounds for d in dimensions], nu=1.5)
)
return cook_estimator("GP", space=dimensions, kernel=kernel, n_restarts_optimizer=2)
```
!!! Note
While custom estimators can be provided, it's up to you as User to do research on possible parameters and analyze / understand which ones should be used.
If you're unsure about this, best use one of the Defaults (`"ET"` has proven to be the most versatile) without further parameters.

View File

@@ -176,12 +176,15 @@ Log messages are send to `syslog` with the `user` facility. So you can see them
On many systems `syslog` (`rsyslog`) fetches data from `journald` (and vice versa), so both `--logfile syslog` or `--logfile journald` can be used and the messages be viewed with both `journalctl` and a syslog viewer utility. You can combine this in any way which suites you better.
For `rsyslog` the messages from the bot can be redirected into a separate dedicated log file. To achieve this, add
```
if $programname startswith "freqtrade" then -/var/log/freqtrade.log
```
to one of the rsyslog configuration files, for example at the end of the `/etc/rsyslog.d/50-default.conf`.
For `syslog` (`rsyslog`), the reduction mode can be switched on. This will reduce the number of repeating messages. For instance, multiple bot Heartbeat messages will be reduced to a single message when nothing else happens with the bot. To achieve this, set in `/etc/rsyslog.conf`:
```
# Filter duplicated messages
$RepeatedMsgReduction on

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

After

Width:  |  Height:  |  Size: 143 KiB

View File

@@ -22,6 +22,7 @@ usage: freqtrade backtesting [-h] [-v] [--logfile FILE] [-V] [-c PATH]
[--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]]
[--export {none,trades}] [--export-filename PATH]
[--breakdown {day,week,month} [{day,week,month} ...]]
[--cache {none,day,week,month}]
optional arguments:
-h, --help show this help message and exit
@@ -76,6 +77,9 @@ optional arguments:
_today.json`
--breakdown {day,week,month} [{day,week,month} ...]
Show backtesting breakdown per [day, week, month].
--cache {none,day,week,month}
Load a cached backtest result no older than specified
age (default: day).
Common arguments:
-v, --verbose Verbose mode (-vv for more, -vvv to get all messages).
@@ -115,7 +119,7 @@ The result of backtesting will confirm if your bot has better odds of making a p
All profit calculations include fees, and freqtrade will use the exchange's default fees for the calculation.
!!! Warning "Using dynamic pairlists for backtesting"
Using dynamic pairlists is possible, however it relies on the current market conditions - which will not reflect the historic status of the pairlist.
Using dynamic pairlists is possible (not all of the handlers are allowed to be used in backtest mode), however it relies on the current market conditions - which will not reflect the historic status of the pairlist.
Also, when using pairlists other than StaticPairlist, reproducibility of backtesting-results cannot be guaranteed.
Please read the [pairlists documentation](plugins.md#pairlists) for more information.
@@ -312,7 +316,7 @@ A backtesting result will look like that:
| | |
| Min balance | 0.00945123 BTC |
| Max balance | 0.01846651 BTC |
| Drawdown | 50.63% |
| Drawdown (Account) | 13.33% |
| Drawdown | 0.0015 BTC |
| Drawdown high | 0.0013 BTC |
| Drawdown low | -0.0002 BTC |
@@ -399,7 +403,7 @@ It contains some useful key metrics about performance of your strategy on backte
| | |
| Min balance | 0.00945123 BTC |
| Max balance | 0.01846651 BTC |
| Drawdown | 50.63% |
| Drawdown (Account) | 13.33% |
| Drawdown | 0.0015 BTC |
| Drawdown high | 0.0013 BTC |
| Drawdown low | -0.0002 BTC |
@@ -426,7 +430,8 @@ It contains some useful key metrics about performance of your strategy on backte
- `Avg. Duration Winners` / `Avg. Duration Loser`: Average durations for winning and losing trades.
- `Rejected Buy signals`: Buy signals that could not be acted upon due to max_open_trades being reached.
- `Min balance` / `Max balance`: Lowest and Highest Wallet balance during the backtest period.
- `Drawdown`: Maximum drawdown experienced. For example, the value of 50% means that from highest to subsequent lowest point, a 50% drop was experienced).
- `Drawdown (Account)`: Maximum Account Drawdown experienced. Calculated as $(Absolute Drawdown) / (DrawdownHigh + startingBalance)$.
- `Drawdown`: Maximum, absolute drawdown experienced. Difference between Drawdown High and Subsequent Low point.
- `Drawdown high` / `Drawdown low`: Profit at the beginning and end of the largest drawdown period. A negative low value means initial capital lost.
- `Drawdown Start` / `Drawdown End`: Start and end datetime for this largest drawdown (can also be visualized via the `plot-dataframe` sub-command).
- `Market change`: Change of the market during the backtest period. Calculated as average of all pairs changes from the first to the last candle using the "close" column.
@@ -456,6 +461,14 @@ freqtrade backtesting --strategy MyAwesomeStrategy --breakdown day month
The output will show a table containing the realized absolute Profit (in stake currency) for the given timeperiod, as well as wins, draws and losses that materialized (closed) on this day.
### Backtest result caching
To save time, by default backtest will reuse a cached result from within the last day when the backtested strategy and config match that of a previous backtest. To force a new backtest despite existing result for an identical run specify `--cache none` parameter.
!!! Warning
Caching is automatically disabled for open-ended timeranges (`--timerange 20210101-`), as freqtrade cannot ensure reliably that the underlying data didn't change. It can also use cached results where it shouldn't if the original backtest had missing data at the end, which was fixed by downloading more data.
In this instance, please use `--cache none` once to force a fresh backtest.
### Further backtest-result analysis
To further analyze your backtest results, you can [export the trades](#exporting-trades-to-file).
@@ -484,8 +497,8 @@ Since backtesting lacks some detailed information about what happens within a ca
- ROI applies before trailing-stop, ensuring profits are "top-capped" at ROI if both ROI and trailing stop applies
- Sell-reason does not explain if a trade was positive or negative, just what triggered the sell (this can look odd if negative ROI values are used)
- Evaluation sequence (if multiple signals happen on the same candle)
- ROI (if not stoploss)
- Sell-signal
- ROI (if not stoploss)
- Stoploss
Taking these assumptions, backtesting tries to mirror real trading as closely as possible. However, backtesting will **never** replace running a strategy in dry-run mode.

View File

@@ -38,6 +38,7 @@ By default, loop runs every few seconds (`internals.process_throttle_secs`) and
* Considers stoploss, ROI and sell-signal, `custom_sell()` and `custom_stoploss()`.
* Determine sell-price based on `ask_strategy` configuration setting or by using the `custom_exit_price()` callback.
* Before a sell order is placed, `confirm_trade_exit()` strategy callback is called.
* Check position adjustments for open trades if enabled by calling `adjust_trade_position()` and place additional order if required.
* Check if trade-slots are still available (if `max_open_trades` is reached).
* Verifies buy signal trying to enter new positions.
* Determine buy-price based on `bid_strategy` configuration setting, or by using the `custom_entry_price()` callback.
@@ -56,7 +57,11 @@ This loop will be repeated again and again until the bot is stopped.
* Calculate buy / sell signals (calls `populate_buy_trend()` and `populate_sell_trend()` once per pair).
* Loops per candle simulating entry and exit points.
* Confirm trade buy / sell (calls `confirm_trade_entry()` and `confirm_trade_exit()` if implemented in the strategy).
* Call `custom_entry_price()` (if implemented in the strategy) to determine entry price (Prices are moved to be within the opening candle).
* 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 `custom_stoploss()` and `custom_sell()` to find custom exit points.
* For sells based on sell-signal and custom-sell: Call `custom_exit_price()` to determine exit price (Prices are moved to be within the closing candle).
* Generate backtest report output
!!! Note

View File

@@ -126,14 +126,16 @@ Mandatory parameters are marked as **Required**, which means that they are requi
| `exchange.key` | API key to use for the exchange. Only required when you are in production mode.<br>**Keep it in secret, do not disclose publicly.** <br> **Datatype:** String
| `exchange.secret` | API secret to use for the exchange. Only required when you are in production mode.<br>**Keep it in secret, do not disclose publicly.** <br> **Datatype:** String
| `exchange.password` | API password to use for the exchange. Only required when you are in production mode and for exchanges that use password for API requests.<br>**Keep it in secret, do not disclose publicly.** <br> **Datatype:** String
| `exchange.uid` | API uid to use for the exchange. Only required when you are in production mode and for exchanges that use uid for API requests.<br>**Keep it in secret, do not disclose publicly.** <br> **Datatype:** String
| `exchange.pair_whitelist` | List of pairs to use by the bot for trading and to check for potential trades during backtesting. Supports regex pairs as `.*/BTC`. Not used by VolumePairList. [More information](plugins.md#pairlists-and-pairlist-handlers). <br> **Datatype:** List
| `exchange.pair_blacklist` | List of pairs the bot must absolutely avoid for trading and backtesting. [More information](plugins.md#pairlists-and-pairlist-handlers). <br> **Datatype:** List
| `exchange.ccxt_config` | Additional CCXT parameters passed to both ccxt instances (sync and async). This is usually the correct place for ccxt configurations. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation) <br> **Datatype:** Dict
| `exchange.ccxt_config` | Additional CCXT parameters passed to both ccxt instances (sync and async). This is usually the correct place for additional ccxt configurations. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation). Please avoid adding exchange secrets here (use the dedicated fields instead), as they may be contained in logs. <br> **Datatype:** Dict
| `exchange.ccxt_sync_config` | Additional CCXT parameters passed to the regular (sync) ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation) <br> **Datatype:** Dict
| `exchange.ccxt_async_config` | Additional CCXT parameters passed to the async ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation) <br> **Datatype:** Dict
| `exchange.markets_refresh_interval` | The interval in minutes in which markets are reloaded. <br>*Defaults to `60` minutes.* <br> **Datatype:** Positive Integer
| `exchange.skip_pair_validation` | Skip pairlist validation on startup.<br>*Defaults to `false`<br> **Datatype:** Boolean
| `exchange.skip_open_order_update` | Skips open order updates on startup should the exchange cause problems. Only relevant in live conditions.<br>*Defaults to `false`<br> **Datatype:** Boolean
| `exchange.unknown_fee_rate` | Fallback value to use when calculating trading fees. This can be useful for exchanges which have fees in non-tradable currencies. The value provided here will be multiplied with the "fee cost".<br>*Defaults to `None`<br> **Datatype:** float
| `exchange.log_responses` | Log relevant exchange responses. For debug mode only - use with care.<br>*Defaults to `false`<br> **Datatype:** Boolean
| `edge.*` | Please refer to [edge configuration document](edge.md) for detailed explanation.
| `experimental.block_bad_exchanges` | Block exchanges known to not work with freqtrade. Leave on default unless you want to test if that exchange works now. <br>*Defaults to `true`.* <br> **Datatype:** Boolean
@@ -170,6 +172,8 @@ Mandatory parameters are marked as **Required**, which means that they are requi
| `user_data_dir` | Directory containing user data. <br> *Defaults to `./user_data/`*. <br> **Datatype:** String
| `dataformat_ohlcv` | Data format to use to store historical candle (OHLCV) data. <br> *Defaults to `json`*. <br> **Datatype:** String
| `dataformat_trades` | Data format to use to store historical trades data. <br> *Defaults to `jsongz`*. <br> **Datatype:** String
| `position_adjustment_enable` | Enables the strategy to use position adjustments (additional buys or sells). [More information here](strategy-callbacks.md#adjust-trade-position). <br> [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `false`.*<br> **Datatype:** Boolean
| `max_entry_position_adjustment` | Maximum additional order(s) for each open trade on top of the first entry Order. Set it to `-1` for unlimited additional orders. [More information here](strategy-callbacks.md#adjust-trade-position). <br> [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `-1`.*<br> **Datatype:** Positive Integer or -1
### Parameters in the strategy
@@ -194,6 +198,8 @@ Values set in the configuration file always overwrite values set in the strategy
* `sell_profit_offset`
* `ignore_roi_if_buy_signal`
* `ignore_buying_expired_candle_after`
* `position_adjustment_enable`
* `max_entry_position_adjustment`
### Configuring amount per trade
@@ -300,6 +306,15 @@ To allow the bot to trade all the available `stake_currency` in your account (mi
When using `"stake_amount" : "unlimited",` in combination with Dry-Run, Backtesting or Hyperopt, the balance will be simulated starting with a stake of `dry_run_wallet` which will evolve.
It is therefore important to set `dry_run_wallet` to a sensible value (like 0.05 or 0.01 for BTC and 1000 or 100 for USDT, for example), otherwise, it may simulate trades with 100 BTC (or more) or 0.05 USDT (or less) at once - which may not correspond to your real available balance or is less than the exchange minimal limit for the order amount for the stake currency.
#### Dynamic stake amount with position adjustment
When you want to use position adjustment with unlimited stakes, you must also implement `custom_stake_amount` to a return a value depending on your strategy.
Typical value would be in the range of 25% - 50% of the proposed stakes, but depends highly on your strategy and how much you wish to leave into the wallet as position adjustment buffer.
For example if your position adjustment assumes it can do 2 additional buys with the same stake amounts then your buffer should be 66.6667% of the initially proposed unlimited stake amount.
Or another example if your position adjustment assumes it can do 1 additional buy with 3x the original stake amount then `custom_stake_amount` should return 25% of proposed stake amount and leave 75% for possible later position adjustments.
--8<-- "includes/pricing.md"
### Understand minimal_roi

View File

@@ -50,19 +50,22 @@ Repetitive tasks | Shell scripts
Data analysis & visualization | Notebook
1. Use the CLI to
* download historical data
* run a backtest
* run with real-time data
* export results
1. Collect these actions in shell scripts
* save complicated commands with arguments
* execute multi-step operations
* automate testing strategies and preparing data for analysis
1. Use a notebook to
* visualize data
* munge and plot to generate insights
* mangle and plot to generate insights
## Example utility snippets

View File

@@ -15,8 +15,8 @@ This command line option was deprecated in 2019.7-dev (develop branch) and remov
### The **--dynamic-whitelist** command line option
This command line option was deprecated in 2018 and removed freqtrade 2019.6-dev (develop branch)
and in freqtrade 2019.7.
This command line option was deprecated in 2018 and removed freqtrade 2019.6-dev (develop branch) and in freqtrade 2019.7.
Please refer to [pairlists](plugins.md#pairlists-and-pairlist-handlers) instead.
### the `--live` command line option

View File

@@ -324,9 +324,8 @@ jupyter nbconvert --ClearOutputPreprocessor.enabled=True --to markdown freqtrade
This documents some decisions taken for the CI Pipeline.
* CI runs on all OS variants, Linux (ubuntu), macOS and Windows.
* Docker images are build for the branches `stable` and `develop`.
* Docker images are build for the branches `stable` and `develop`, and are built as multiarch builds, supporting multiple platforms via the same tag.
* Docker images containing Plot dependencies are also available as `stable_plot` and `develop_plot`.
* Raspberry PI Docker images are postfixed with `_pi` - so tags will be `:stable_pi` and `develop_pi`.
* Docker images contain a file, `/freqtrade/freqtrade_commit` containing the commit this image is based of.
* Full docker image rebuilds are run once a week via schedule.
* Deployments run on ubuntu.

View File

@@ -126,6 +126,12 @@ All freqtrade arguments will be available by running `docker-compose run --rm fr
!!! Note "`docker-compose run --rm`"
Including `--rm` will remove the container after completion, and is highly recommended for all modes except trading mode (running with `freqtrade trade` command).
??? Note "Using docker without docker-compose"
"`docker-compose run --rm`" will require a compose file to be provided.
Some freqtrade commands that don't require authentication such as `list-pairs` can be run with "`docker run --rm`" instead.
For example `docker run --rm freqtradeorg/freqtrade:stable list-pairs --exchange binance --quote BTC --print-json`.
This can be useful for fetching exchange information to add to your `config.json` without affecting your running containers.
#### Example: Download data with docker-compose
Download backtesting data for 5 days for the pair ETH/BTC and 1h timeframe from Binance. The data will be stored in the directory `user_data/data/` on the host.

View File

@@ -199,6 +199,11 @@ OKEX requires a passphrase for each api key, you will therefore need to add this
!!! Warning
OKEX only provides 100 candles per api call. Therefore, the strategy will only have a pretty low amount of data available in backtesting mode.
## Gate.io
Gate.io allows the use of `POINT` to pay for fees. As this is not a tradable currency (no regular market available), automatic fee calculations will fail (and default to a fee of 0).
The configuration parameter `exchange.unknown_fee_rate` can be used to specify the exchange rate between Point and the stake currency. Obviously, changing the stake-currency will also require changes to this value.
## All exchanges
Should you experience constant errors with Nonce (like `InvalidNonce`), it is best to regenerate the API keys. Resetting Nonce is difficult and it's usually easier to regenerate the API keys.

View File

@@ -188,12 +188,12 @@ There is however nothing preventing you from using GPU-enabled indicators within
Per default Hyperopt called without the `-e`/`--epochs` command line option will only
run 100 epochs, means 100 evaluations of your triggers, guards, ... Too few
to find a great result (unless if you are very lucky), so you probably
have to run it for 10.000 or more. But it will take an eternity to
have to run it for 10000 or more. But it will take an eternity to
compute.
Since hyperopt uses Bayesian search, running for too many epochs may not produce greater results.
It's therefore recommended to run between 500-1000 epochs over and over until you hit at least 10.000 epochs in total (or are satisfied with the result). You can best judge by looking at the results - if the bot keeps discovering better strategies, it's best to keep on going.
It's therefore recommended to run between 500-1000 epochs over and over until you hit at least 10000 epochs in total (or are satisfied with the result). You can best judge by looking at the results - if the bot keeps discovering better strategies, it's best to keep on going.
```bash
freqtrade hyperopt --hyperopt-loss SharpeHyperOptLossDaily --strategy SampleStrategy -e 1000
@@ -217,9 +217,9 @@ already 8\*10^9\*10 evaluations. A roughly total of 80 billion evaluations.
Did you run 100 000 evaluations? Congrats, you've done roughly 1 / 100 000 th
of the search space, assuming that the bot never tests the same parameters more than once.
* The time it takes to run 1000 hyperopt epochs depends on things like: The available cpu, hard-disk, ram, timeframe, timerange, indicator settings, indicator count, amount of coins that hyperopt test strategies on and the resulting trade count - which can be 650 trades in a year or 10.0000 trades depending if the strategy aims for big profits by trading rarely or for many low profit trades.
* The time it takes to run 1000 hyperopt epochs depends on things like: The available cpu, hard-disk, ram, timeframe, timerange, indicator settings, indicator count, amount of coins that hyperopt test strategies on and the resulting trade count - which can be 650 trades in a year or 100000 trades depending if the strategy aims for big profits by trading rarely or for many low profit trades.
Example: 4% profit 650 times vs 0,3% profit a trade 10.000 times in a year. If we assume you set the --timerange to 365 days.
Example: 4% profit 650 times vs 0,3% profit a trade 10000 times in a year. If we assume you set the --timerange to 365 days.
Example:
`freqtrade --config config.json --strategy SampleStrategy --hyperopt SampleHyperopt -e 1000 --timerange 20190601-20200601`

View File

@@ -196,7 +196,7 @@ Trade count is used as a tie breaker.
You can use the `minutes` parameter to only consider performance of the past X minutes (rolling window).
Not defining this parameter (or setting it to 0) will use all-time performance.
The optional `min_profit` parameter defines the minimum profit a pair must have to be considered.
The optional `min_profit` (as ratio -> a setting of `0.01` corresponds to 1%) parameter defines the minimum profit a pair must have to be considered.
Pairs below this level will be filtered out.
Using this parameter without `minutes` is highly discouraged, as it can lead to an empty pairlist without a way to recover.
@@ -206,7 +206,7 @@ Using this parameter without `minutes` is highly discouraged, as it can lead to
{
"method": "PerformanceFilter",
"minutes": 1440, // rolling 24h
"min_profit": 0.01
"min_profit": 0.01 // minimal profit 1%
}
],
```
@@ -220,6 +220,9 @@ 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.
!!! Warning "Backtesting"
`PrecisionFilter` does not support backtesting mode using multiple strategies.
#### PriceFilter
The `PriceFilter` allows filtering of pairs by price. Currently the following price filters are supported:
@@ -257,7 +260,7 @@ Min price precision for SHITCOIN/BTC is 8 decimals. If its price is 0.00000011 -
Shuffles (randomizes) pairs in the pairlist. It can be used for preventing the bot from trading some of the pairs more frequently then others when you want all pairs be treated with the same priority.
!!! Tip
You may set the `seed` value for this Pairlist to obtain reproducible results, which can be useful for repeated backtesting sessions. If `seed` is not set, the pairs are shuffled in the non-repeatable random order.
You may set the `seed` value for this Pairlist to obtain reproducible results, which can be useful for repeated backtesting sessions. If `seed` is not set, the pairs are shuffled in the non-repeatable random order. ShuffleFilter will automatically detect runmodes and apply the `seed` only for backtesting modes - if a `seed` value is set.
#### SpreadFilter

View File

@@ -11,7 +11,7 @@
## Introduction
Freqtrade is a crypto-currency algorithmic trading software developed in python (3.7+) and supported on Windows, macOS and Linux.
Freqtrade is a crypto-currency algorithmic trading software developed in python (3.8+) and supported on Windows, macOS and Linux.
!!! Danger "DISCLAIMER"
This software is for educational purposes only. Do not risk money which you are afraid to lose. USE THE SOFTWARE AT YOUR OWN RISK. THE AUTHORS AND ALL AFFILIATES ASSUME NO RESPONSIBILITY FOR YOUR TRADING RESULTS.
@@ -67,7 +67,7 @@ To run this bot we recommend you a linux cloud instance with a minimum of:
Alternatively
- Python 3.7+
- Python 3.8+
- pip (pip3)
- git
- TA-Lib

View File

@@ -36,9 +36,13 @@ The easiest way to install and run Freqtrade is to clone the bot Github reposito
These requirements apply to both [Script Installation](#script-installation) and [Manual Installation](#manual-installation).
!!! Note "ARM64 systems"
If you are running an ARM64 system (like a MacOS M1 or an Oracle VM), please use [docker](docker_quickstart.md) to run freqtrade.
While native installation is possible with some manual effort, this is not supported at the moment.
### Install guide
* [Python >= 3.7.x](http://docs.python-guide.org/en/latest/starting/installation/)
* [Python >= 3.8.x](http://docs.python-guide.org/en/latest/starting/installation/)
* [pip](https://pip.pypa.io/en/stable/installing/)
* [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
* [virtualenv](https://virtualenv.pypa.io/en/stable/installation.html) (Recommended)
@@ -416,16 +420,3 @@ open /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10
```
If this file is inexistent, then you're probably on a different version of MacOS, so you may need to consult the internet for specific resolution details.
### MacOS installation error with python 3.9
When using python 3.9 on macOS, it's currently necessary to install some os-level modules to allow dependencies to compile.
The errors you'll see happen during installation and are related to the installation of `tables` or `blosc`.
You can install the necessary libraries with the following command:
```bash
brew install hdf5 c-blosc
```
After this, please run the installation (script) again.

View File

@@ -164,7 +164,7 @@ The resulting plot will have the following elements:
An advanced plot configuration can be specified in the strategy in the `plot_config` parameter.
Additional features when using plot_config include:
Additional features when using `plot_config` include:
* Specify colors per indicator
* Specify additional subplots
@@ -174,6 +174,7 @@ The sample plot configuration below specifies fixed colors for the indicators. O
It also allows multiple subplots to display both MACD and RSI at the same time.
Plot type can be configured using `type` key. Possible types are:
* `scatter` corresponding to `plotly.graph_objects.Scatter` class (default).
* `bar` corresponding to `plotly.graph_objects.Bar` class.
@@ -182,40 +183,89 @@ Extra parameters to `plotly.graph_objects.*` constructor can be specified in `pl
Sample configuration with inline comments explaining the process:
``` python
plot_config = {
'main_plot': {
# Configuration for main plot indicators.
# Specifies `ema10` to be red, and `ema50` to be a shade of gray
'ema10': {'color': 'red'},
'ema50': {'color': '#CCCCCC'},
# By omitting color, a random color is selected.
'sar': {},
# fill area between senkou_a and senkou_b
'senkou_a': {
'color': 'green', #optional
'fill_to': 'senkou_b',
'fill_label': 'Ichimoku Cloud', #optional
'fill_color': 'rgba(255,76,46,0.2)', #optional
},
# plot senkou_b, too. Not only the area to it.
'senkou_b': {}
@property
def plot_config(self):
"""
There are a lot of solutions how to build the return dictionary.
The only important point is the return value.
Example:
plot_config = {'main_plot': {}, 'subplots': {}}
"""
plot_config = {}
plot_config['main_plot'] = {
# Configuration for main plot indicators.
# Assumes 2 parameters, emashort and emalong to be specified.
f'ema_{self.emashort.value}': {'color': 'red'},
f'ema_{self.emalong.value}': {'color': '#CCCCCC'},
# By omitting color, a random color is selected.
'sar': {},
# fill area between senkou_a and senkou_b
'senkou_a': {
'color': 'green', #optional
'fill_to': 'senkou_b',
'fill_label': 'Ichimoku Cloud', #optional
'fill_color': 'rgba(255,76,46,0.2)', #optional
},
'subplots': {
# Create subplot MACD
"MACD": {
'macd': {'color': 'blue', 'fill_to': 'macdhist'},
'macdsignal': {'color': 'orange'},
'macdhist': {'type': 'bar', 'plotly': {'opacity': 0.9}}
},
# Additional subplot RSI
"RSI": {
'rsi': {'color': 'red'}
}
# plot senkou_b, too. Not only the area to it.
'senkou_b': {}
}
plot_config['subplots'] = {
# Create subplot MACD
"MACD": {
'macd': {'color': 'blue', 'fill_to': 'macdhist'},
'macdsignal': {'color': 'orange'},
'macdhist': {'type': 'bar', 'plotly': {'opacity': 0.9}}
},
# Additional subplot RSI
"RSI": {
'rsi': {'color': 'red'}
}
}
return plot_config
```
??? Note "As attribute (former method)"
Assigning plot_config is also possible as Attribute (this used to be the default way).
This has the disadvantage that strategy parameters are not available, preventing certain configurations from working.
``` python
plot_config = {
'main_plot': {
# Configuration for main plot indicators.
# Specifies `ema10` to be red, and `ema50` to be a shade of gray
'ema10': {'color': 'red'},
'ema50': {'color': '#CCCCCC'},
# By omitting color, a random color is selected.
'sar': {},
# fill area between senkou_a and senkou_b
'senkou_a': {
'color': 'green', #optional
'fill_to': 'senkou_b',
'fill_label': 'Ichimoku Cloud', #optional
'fill_color': 'rgba(255,76,46,0.2)', #optional
},
# plot senkou_b, too. Not only the area to it.
'senkou_b': {}
},
'subplots': {
# Create subplot MACD
"MACD": {
'macd': {'color': 'blue', 'fill_to': 'macdhist'},
'macdsignal': {'color': 'orange'},
'macdhist': {'type': 'bar', 'plotly': {'opacity': 0.9}}
},
# Additional subplot RSI
"RSI": {
'rsi': {'color': 'red'}
}
}
}
```
!!! Note
The above configuration assumes that `ema10`, `ema50`, `senkou_a`, `senkou_b`,
`macd`, `macdsignal`, `macdhist` and `rsi` are columns in the DataFrame created by the strategy.
@@ -223,6 +273,9 @@ Sample configuration with inline comments explaining the process:
!!! Warning
`plotly` arguments are only supported with plotly library and will not work with freq-ui.
!!! Note "Trade position adjustments"
If `position_adjustment_enable` / `adjust_trade_position()` is used, the trade initial buy price is averaged over multiple orders and the trade start price will most likely appear outside the candle range.
## Plot profit
![plot-profit](assets/plot-profit.png)
@@ -233,6 +286,8 @@ The `plot-profit` subcommand shows an interactive graph with three plots:
* The summarized profit made by backtesting.
Note that this is not the real-world profit, but more of an estimate.
* Profit for each individual pair.
* Parallelism of trades.
* Underwater (Periods of drawdown).
The first graph is good to get a grip of how the overall market progresses.
@@ -242,6 +297,8 @@ This graph will also highlight the start (and end) of the Max drawdown period.
The third graph can be useful to spot outliers, events in pairs that cause profit spikes.
The forth graph can help you analyze trade parallelism, showing how often max_open_trades have been maxed out.
Possible options for the `freqtrade plot-profit` subcommand:
```

View File

@@ -1,4 +1,4 @@
mkdocs==1.2.3
mkdocs-material==7.3.6
mkdocs-material==8.1.9
mdx_truly_sane_lists==1.2
pymdown-extensions==9.1

View File

@@ -127,6 +127,21 @@ The provided exit-tag is then used as sell-reason - and shown as such in backtes
!!! Note
`sell_reason` is limited to 100 characters, remaining data will be truncated.
## Strategy version
You can implement custom strategy versioning by using the "version" method, and returning the version you would like this strategy to have.
``` python
def version(self) -> str:
"""
Returns version of the strategy.
"""
return "1.1"
```
!!! Note
You should make sure to implement proper version control (like a git repository) alongside this, as freqtrade will not keep historic versions of your strategy, so it's up to the user to be able to eventually roll back to a prior version of the strategy.
## Derived strategies
The strategies can be derived from other strategies. This avoids duplication of your custom strategy code. You can use this technique to override small parts of your main strategy, leaving the rest untouched:
@@ -207,9 +222,9 @@ should be rewritten to
```python
frames = [dataframe]
for val in self.buy_ema_short.range:
frames.append({
frames.append(DataFrame({
f'ema_short_{val}': ta.EMA(dataframe, timeperiod=val)
})
}))
# Append columns to existing dataframe
merged_frame = pd.concat(frames, axis=1)

View File

@@ -15,6 +15,7 @@ Currently available callbacks:
* [`check_buy_timeout()` and `check_sell_timeout()](#custom-order-timeout-rules)
* [`confirm_trade_entry()`](#trade-entry-buy-order-confirmation)
* [`confirm_trade_exit()`](#trade-exit-sell-order-confirmation)
* [`adjust_trade_position()`](#adjust-trade-position)
!!! Tip "Callback calling sequence"
You can find the callback calling sequence in [bot-basics](bot-basics.md#bot-execution-logic)
@@ -53,7 +54,7 @@ Called before entering a trade, makes it possible to manage your position size w
class AwesomeStrategy(IStrategy):
def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
proposed_stake: float, min_stake: float, max_stake: float,
**kwargs) -> float:
entry_tag: Optional[str], **kwargs) -> float:
dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe)
current_candle = dataframe.iloc[-1].squeeze()
@@ -73,7 +74,7 @@ class AwesomeStrategy(IStrategy):
Freqtrade will fall back to the `proposed_stake` value should your code raise an exception. The exception itself will be logged.
!!! Tip
You do not _have_ to ensure that `min_stake <= returned_value <= max_stake`. Trades will succeed as the returned value will be clamped to supported range and this acton will be logged.
You do not _have_ to ensure that `min_stake <= returned_value <= max_stake`. Trades will succeed as the returned value will be clamped to supported range and this action will be logged.
!!! Tip
Returning `0` or `None` will prevent trades from being placed.
@@ -361,8 +362,8 @@ class AwesomeStrategy(IStrategy):
# ... populate_* methods
def custom_entry_price(self, pair: str, current_time: datetime,
proposed_rate, **kwargs) -> float:
def custom_entry_price(self, pair: str, current_time: datetime, proposed_rate: float,
entry_tag: Optional[str], **kwargs) -> float:
dataframe, last_updated = self.dp.get_analyzed_dataframe(pair=pair,
timeframe=self.timeframe)
@@ -387,8 +388,10 @@ class AwesomeStrategy(IStrategy):
**Example**:
If the new_entryprice is 97, the proposed_rate is 100 and the `custom_price_max_distance_ratio` is set to 2%, The retained valid custom entry price will be 98, which is 2% below the current (proposed) rate.
!!! Warning "No backtesting support"
Custom entry-prices are currently not supported during backtesting.
!!! Warning "Backtesting"
While Custom prices are supported in backtesting (starting with 2021.12), prices will be moved to within the candle's high/low prices.
This behavior is currently being tested, and might be changed at a later point.
`custom_exit_price()` is only called for sells of type Sell_signal and Custom sell. All other sell-types will use regular backtesting prices.
## Custom order timeout rules
@@ -410,7 +413,7 @@ It applies a tight timeout for higher priced assets, while allowing more time to
The function must return either `True` (cancel order) or `False` (keep order alive).
``` python
from datetime import datetime, timedelta, timezone
from datetime import datetime, timedelta
from freqtrade.persistence import Trade
class AwesomeStrategy(IStrategy):
@@ -423,22 +426,24 @@ class AwesomeStrategy(IStrategy):
'sell': 60 * 25
}
def check_buy_timeout(self, pair: str, trade: 'Trade', order: dict, **kwargs) -> bool:
if trade.open_rate > 100 and trade.open_date_utc < datetime.now(timezone.utc) - timedelta(minutes=5):
def check_buy_timeout(self, pair: str, trade: 'Trade', order: dict,
current_time: datetime, **kwargs) -> bool:
if trade.open_rate > 100 and trade.open_date_utc < current_time - timedelta(minutes=5):
return True
elif trade.open_rate > 10 and trade.open_date_utc < datetime.now(timezone.utc) - timedelta(minutes=3):
elif trade.open_rate > 10 and trade.open_date_utc < current_time - timedelta(minutes=3):
return True
elif trade.open_rate < 1 and trade.open_date_utc < datetime.now(timezone.utc) - timedelta(hours=24):
elif trade.open_rate < 1 and trade.open_date_utc < current_time - timedelta(hours=24):
return True
return False
def check_sell_timeout(self, pair: str, trade: 'Trade', order: dict, **kwargs) -> bool:
if trade.open_rate > 100 and trade.open_date_utc < datetime.now(timezone.utc) - timedelta(minutes=5):
def check_sell_timeout(self, pair: str, trade: Trade, order: dict,
current_time: datetime, **kwargs) -> bool:
if trade.open_rate > 100 and trade.open_date_utc < current_time - timedelta(minutes=5):
return True
elif trade.open_rate > 10 and trade.open_date_utc < datetime.now(timezone.utc) - timedelta(minutes=3):
elif trade.open_rate > 10 and trade.open_date_utc < current_time - timedelta(minutes=3):
return True
elif trade.open_rate < 1 and trade.open_date_utc < datetime.now(timezone.utc) - timedelta(hours=24):
elif trade.open_rate < 1 and trade.open_date_utc < current_time - timedelta(hours=24):
return True
return False
```
@@ -497,7 +502,8 @@ class AwesomeStrategy(IStrategy):
# ... populate_* methods
def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float,
time_in_force: str, current_time: datetime, **kwargs) -> bool:
time_in_force: str, current_time: datetime, entry_tag: Optional[str],
**kwargs) -> bool:
"""
Called right before placing a buy order.
Timing for this function is critical, so avoid doing heavy computations or
@@ -566,3 +572,110 @@ class AwesomeStrategy(IStrategy):
return True
```
## Adjust trade position
The `position_adjustment_enable` strategy property enables the usage of `adjust_trade_position()` callback in the strategy.
For performance reasons, it's disabled by default and freqtrade will show a warning message on startup if enabled.
`adjust_trade_position()` can be used to perform additional orders, for example to manage risk with DCA (Dollar Cost Averaging).
`max_entry_position_adjustment` property is used to limit the number of additional buys per trade (on top of the first buy) that the bot can execute. By default, the value is -1 which means the bot have no limit on number of adjustment buys.
The strategy is expected to return a stake_amount (in stake currency) between `min_stake` and `max_stake` if and when an additional buy order should be made (position is increased).
If there are not enough funds in the wallet (the return value is above `max_stake`) then the signal will be ignored.
Additional orders also result in additional fees and those orders don't count towards `max_open_trades`.
This callback is **not** called when there is an open order (either buy or sell) waiting for execution, or when you have reached the maximum amount of extra buys that you have set on `max_entry_position_adjustment`.
`adjust_trade_position()` is called very frequently for the duration of a trade, so you must keep your implementation as performant as possible.
!!! Note "About stake size"
Using fixed stake size means it will be the amount used for the first order, just like without position adjustment.
If you wish to buy additional orders with DCA, then make sure to leave enough funds in the wallet for that.
Using 'unlimited' stake amount with DCA orders requires you to also implement the `custom_stake_amount()` callback to avoid allocating all funds to the initial order.
!!! Warning
Stoploss is still calculated from the initial opening price, not averaged price.
!!! Warning "/stopbuy"
While `/stopbuy` command stops the bot from entering new trades, the position adjustment feature will continue buying new orders on existing trades.
!!! Warning "Backtesting"
During backtesting this callback is called for each candle in `timeframe` or `timeframe_detail`, so performance will be affected.
``` python
from freqtrade.persistence import Trade
class DigDeeperStrategy(IStrategy):
position_adjustment_enable = True
# Attempts to handle large drops with DCA. High stoploss is required.
stoploss = -0.30
# ... populate_* methods
# Example specific variables
max_entry_position_adjustment = 3
# This number is explained a bit further down
max_dca_multiplier = 5.5
# This is called when placing the initial order (opening trade)
def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
proposed_stake: float, min_stake: float, max_stake: float,
entry_tag: Optional[str], **kwargs) -> float:
# We need to leave most of the funds for possible further DCA orders
# This also applies to fixed stakes
return proposed_stake / self.max_dca_multiplier
def adjust_trade_position(self, trade: Trade, current_time: datetime,
current_rate: float, current_profit: float, min_stake: float,
max_stake: float, **kwargs):
"""
Custom trade adjustment logic, returning the stake amount that a trade should be increased.
This means extra buy orders with additional fees.
:param trade: trade object.
:param current_time: datetime object, containing the current datetime
:param current_rate: Current buy rate.
:param current_profit: Current profit (as ratio), calculated based on current_rate.
:param min_stake: Minimal stake size allowed by exchange.
:param max_stake: Balance available for trading.
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
:return float: Stake amount to adjust your trade
"""
if current_profit > -0.05:
return None
# Obtain pair dataframe (just to show how to access it)
dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe)
# Only buy when not actively falling price.
last_candle = dataframe.iloc[-1].squeeze()
previous_candle = dataframe.iloc[-2].squeeze()
if last_candle['close'] < previous_candle['close']:
return None
filled_buys = trade.select_filled_orders('buy')
count_of_buys = trade.nr_of_successful_buys
# Allow up to 3 additional increasingly larger buys (4 in total)
# Initial buy is 1x
# If that falls to -5% profit, we buy 1.25x more, average profit should increase to roughly -2.2%
# If that falls down to -5% again, we buy 1.5x more
# If that falls once again down to -5%, we buy 1.75x more
# Total stake for this trade would be 1 + 1.25 + 1.5 + 1.75 = 5.5x of the initial allowed stake.
# That is why max_dca_multiplier is 5.5
# Hope you have a deep wallet!
try:
# This returns first order stake size
stake_amount = filled_buys[0].cost
# This then calculates current safety order size
stake_amount = stake_amount * (1 + (count_of_buys * 0.25))
return stake_amount
except Exception as exception:
return None
return None
```

View File

@@ -838,7 +838,7 @@ In some situations it may be confusing to deal with stops relative to current ra
from datetime import datetime
from freqtrade.persistence import Trade
from freqtrade.strategy import IStrategy, stoploss_from_open
from freqtrade.strategy import IStrategy, stoploss_from_absolute
class AwesomeStrategy(IStrategy):

View File

@@ -50,7 +50,9 @@ candles.head()
```python
# Load strategy using values set above
from freqtrade.resolvers import StrategyResolver
from freqtrade.data.dataprovider import DataProvider
strategy = StrategyResolver.load_strategy(config)
strategy.dp = DataProvider(config, None, None)
# Generate buy/sell signals using strategy
df = strategy.analyze_ticker(candles, {'pair': pair})
@@ -228,7 +230,7 @@ graph = generate_candlestick_graph(pair=pair,
# Show graph inline
# graph.show()
# Render graph in a separate window
# Render graph in a seperate window
graph.show(renderer="browser")
```

View File

@@ -59,7 +59,7 @@ $ freqtrade new-config --config config_binance.json
? Do you want to enable Dry-run (simulated trades)? Yes
? Please insert your stake currency: BTC
? Please insert your stake amount: 0.05
? Please insert max_open_trades (Integer or 'unlimited'): 3
? Please insert max_open_trades (Integer or -1 for unlimited open trades): 3
? Please insert your desired timeframe (e.g. 5m): 5m
? Please insert your display Currency (for reporting): USD
? Select exchange binance

View File

@@ -50,7 +50,7 @@ Sample configuration (tested using IFTTT).
The url in `webhook.url` should point to the correct url for your webhook. If you're using [IFTTT](https://ifttt.com) (as shown in the sample above) please insert your event and key to the url.
You can set the POST body format to Form-Encoded (default) or JSON-Encoded. Use `"format": "form"` or `"format": "json"` respectively. Example configuration for Mattermost Cloud integration:
You can set the POST body format to Form-Encoded (default), JSON-Encoded, or raw data. Use `"format": "form"`, `"format": "json"`, or `"format": "raw"` respectively. Example configuration for Mattermost Cloud integration:
```json
"webhook": {
@@ -63,7 +63,36 @@ You can set the POST body format to Form-Encoded (default) or JSON-Encoded. Use
},
```
The result would be POST request with e.g. `{"text":"Status: running"}` body and `Content-Type: application/json` header which results `Status: running` message in the Mattermost channel.
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:
```json
"webhook": {
"enabled": true,
"url": "https://<YOURHOOKURL>",
"format": "raw",
"webhookstatus": {
"data": "Status: {status}"
}
},
```
The result would be a POST request with e.g. `Status: running` body and `Content-Type: text/plain` header.
Optional parameters are available to enable automatic retries for webhook messages. The `webhook.retries` parameter can be set for the maximum number of retries the webhook request should attempt if it is unsuccessful (i.e. HTTP response status is not 200). By default this is set to `0` which is disabled. An additional `webhook.retry_delay` parameter can be set to specify the time in seconds between retry attempts. By default this is set to `0.1` (i.e. 100ms). Note that increasing the number of retries or retry delay may slow down the trader if there are connectivity issues with the webhook. Example configuration for retries:
```json
"webhook": {
"enabled": true,
"url": "https://<YOURHOOKURL>",
"retries": 3,
"retry_delay": 0.2,
"webhookstatus": {
"status": "Status: {status}"
}
},
```
Different payloads can be configured for different events. Not all fields are necessary, but you should configure at least one of the dicts, otherwise the webhook will never be called.
@@ -75,11 +104,13 @@ Possible parameters are:
* `trade_id`
* `exchange`
* `pair`
* `limit`
* ~~`limit` # Deprecated - should no longer be used.~~
* `open_rate`
* `amount`
* `open_date`
* `stake_amount`
* `stake_currency`
* `base_currency`
* `fiat_currency`
* `order_type`
* `current_rate`
@@ -98,6 +129,7 @@ Possible parameters are:
* `open_date`
* `stake_amount`
* `stake_currency`
* `base_currency`
* `fiat_currency`
* `order_type`
* `current_rate`
@@ -116,7 +148,10 @@ Possible parameters are:
* `open_date`
* `stake_amount`
* `stake_currency`
* `base_currency`
* `fiat_currency`
* `order_type`
* `current_rate`
* `buy_tag`
### Webhooksell
@@ -134,6 +169,7 @@ Possible parameters are:
* `profit_amount`
* `profit_ratio`
* `stake_currency`
* `base_currency`
* `fiat_currency`
* `sell_reason`
* `order_type`
@@ -156,6 +192,7 @@ Possible parameters are:
* `profit_amount`
* `profit_ratio`
* `stake_currency`
* `base_currency`
* `fiat_currency`
* `sell_reason`
* `order_type`
@@ -178,6 +215,7 @@ Possible parameters are:
* `profit_amount`
* `profit_ratio`
* `stake_currency`
* `base_currency`
* `fiat_currency`
* `sell_reason`
* `order_type`

View File

@@ -23,9 +23,9 @@ git clone https://github.com/freqtrade/freqtrade.git
Install ta-lib according to the [ta-lib documentation](https://github.com/mrjbq7/ta-lib#windows).
As compiling from source on windows has heavy dependencies (requires a partial visual studio installation), there is also a repository of unofficial pre-compiled windows Wheels [here](https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib), which need to be downloaded and installed using `pip install TA_Lib-0.4.21-cp38-cp38-win_amd64.whl` (make sure to use the version matching your python version).
As compiling from source on windows has heavy dependencies (requires a partial visual studio installation), there is also a repository of unofficial pre-compiled windows Wheels [here](https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib), which need to be downloaded and installed using `pip install TA_Lib-0.4.24-cp38-cp38-win_amd64.whl` (make sure to use the version matching your python version).
Freqtrade provides these dependencies for the latest 2 Python versions (3.7 and 3.8) and for 64bit Windows.
Freqtrade provides these dependencies for the latest 3 Python versions (3.8, 3.9 and 3.10) and for 64bit Windows.
Other versions must be downloaded from the above link.
``` powershell

View File

@@ -4,7 +4,7 @@ channels:
# - defaults
dependencies:
# 1/4 req main
- python>=3.7,<3.9
- python>=3.8,<=3.10
- numpy
- pandas
- pip
@@ -25,9 +25,12 @@ dependencies:
- fastapi
- uvicorn
- pyjwt
- aiofiles
- psutil
- colorama
- questionary
- prompt-toolkit
- python-dateutil
# ============================

View File

@@ -1,5 +1,5 @@
""" Freqtrade bot """
__version__ = '2021.11'
__version__ = '2022.1'
if __version__ == 'develop':

View File

@@ -3,7 +3,7 @@
__main__.py for Freqtrade
To launch Freqtrade as a module
> python -m freqtrade (with Python >= 3.7)
> python -m freqtrade (with Python >= 3.8)
"""
from freqtrade import main

View File

@@ -24,7 +24,7 @@ ARGS_COMMON_OPTIMIZE = ["timeframe", "timerange", "dataformat_ohlcv",
ARGS_BACKTEST = ARGS_COMMON_OPTIMIZE + ["position_stacking", "use_max_market_positions",
"enable_protections", "dry_run_wallet", "timeframe_detail",
"strategy_list", "export", "exportfilename",
"backtest_breakdown"]
"backtest_breakdown", "backtest_cache"]
ARGS_HYPEROPT = ARGS_COMMON_OPTIMIZE + ["hyperopt", "hyperopt_path",
"position_stacking", "use_max_market_positions",

View File

@@ -76,17 +76,14 @@ def ask_user_config() -> Dict[str, Any]:
{
"type": "text",
"name": "max_open_trades",
"message": f"Please insert max_open_trades (Integer or '{UNLIMITED_STAKE_AMOUNT}'):",
"message": "Please insert max_open_trades (Integer or -1 for unlimited open trades):",
"default": "3",
"validate": lambda val: val == UNLIMITED_STAKE_AMOUNT or validate_is_int(val),
"filter": lambda val: '"' + UNLIMITED_STAKE_AMOUNT + '"'
if val == UNLIMITED_STAKE_AMOUNT
else val
"validate": lambda val: validate_is_int(val)
},
{
"type": "select",
"name": "timeframe_in_config",
"message": "Tim",
"message": "Time",
"choices": ["Have the strategy define timeframe.", "Override in configuration."]
},
{

View File

@@ -205,6 +205,12 @@ AVAILABLE_CLI_OPTIONS = {
nargs='+',
choices=constants.BACKTEST_BREAKDOWNS
),
"backtest_cache": Arg(
'--cache',
help='Load a cached backtest result no older than specified age (default: %(default)s).',
default=constants.BACKTEST_CACHE_DEFAULT,
choices=constants.BACKTEST_CACHE_AGE,
),
# Edge
"stoploss_range": Arg(
'--stoplosses',

View File

@@ -1,6 +1,6 @@
from datetime import datetime, timezone
from cachetools.ttl import TTLCache
from cachetools import TTLCache
class PeriodicCache(TTLCache):

View File

@@ -276,6 +276,9 @@ class Configuration:
self._args_to_config(config, argname='backtest_breakdown',
logstring='Parameter --breakdown detected ...')
self._args_to_config(config, argname='backtest_cache',
logstring='Parameter --cache={} detected ...')
self._args_to_config(config, argname='disableparamexport',
logstring='Parameter --disableparamexport detected: {} ...')

View File

@@ -34,6 +34,8 @@ AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList',
AVAILABLE_PROTECTIONS = ['CooldownPeriod', 'LowProfitPairs', 'MaxDrawdown', 'StoplossGuard']
AVAILABLE_DATAHANDLERS = ['json', 'jsongz', 'hdf5']
BACKTEST_BREAKDOWNS = ['day', 'week', 'month']
BACKTEST_CACHE_AGE = ['none', 'day', 'week', 'month']
BACKTEST_CACHE_DEFAULT = 'day'
DRY_RUN_WALLET = 1000
DATETIME_PRINT_FORMAT = '%Y-%m-%d %H:%M:%S'
MATH_CLOSE_PREC = 1e-14 # Precision used for float comparisons
@@ -50,6 +52,8 @@ USERPATH_STRATEGIES = 'strategies'
USERPATH_NOTEBOOKS = 'notebooks'
TELEGRAM_SETTING_OPTIONS = ['on', 'off', 'silent']
WEBHOOK_FORMAT_OPTIONS = ['form', 'json', 'raw']
ENV_VAR_PREFIX = 'FREQTRADE__'
NON_OPEN_EXCHANGE_STATES = ('cancelled', 'canceled', 'closed', 'expired')
@@ -312,10 +316,16 @@ CONF_SCHEMA = {
'type': 'object',
'properties': {
'enabled': {'type': 'boolean'},
'url': {'type': 'string'},
'format': {'type': 'string', 'enum': WEBHOOK_FORMAT_OPTIONS, 'default': 'form'},
'retries': {'type': 'integer', 'minimum': 0},
'retry_delay': {'type': 'number', 'minimum': 0},
'webhookbuy': {'type': 'object'},
'webhookbuycancel': {'type': 'object'},
'webhookbuyfill': {'type': 'object'},
'webhooksell': {'type': 'object'},
'webhooksellcancel': {'type': 'object'},
'webhooksellfill': {'type': 'object'},
'webhookstatus': {'type': 'object'},
},
},
@@ -361,7 +371,9 @@ CONF_SCHEMA = {
'type': 'string',
'enum': AVAILABLE_DATAHANDLERS,
'default': 'jsongz'
}
},
'position_adjustment_enable': {'type': 'boolean'},
'max_entry_position_adjustment': {'type': ['integer', 'number'], 'minimum': -1},
},
'definitions': {
'exchange': {
@@ -387,6 +399,7 @@ CONF_SCHEMA = {
},
'uniqueItems': True
},
'unknown_fee_rate': {'type': 'number'},
'outdated_offset': {'type': 'integer', 'minimum': 1},
'markets_refresh_interval': {'type': 'integer'},
'ccxt_config': {'type': 'object'},

View File

@@ -2,6 +2,8 @@
Helpers when analyzing backtest data
"""
import logging
from copy import copy
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple, Union
@@ -9,21 +11,13 @@ import numpy as np
import pandas as pd
from freqtrade.constants import LAST_BT_RESULT_FN
from freqtrade.misc import json_load
from freqtrade.exceptions import OperationalException
from freqtrade.misc import get_backtest_metadata_filename, json_load
from freqtrade.persistence import LocalTrade, Trade, init_db
logger = logging.getLogger(__name__)
# Old format - maybe remove?
BT_DATA_COLUMNS_OLD = ["pair", "profit_percent", "open_date", "close_date", "index",
"trade_duration", "open_rate", "close_rate", "open_at_end", "sell_reason"]
# Mid-term format, created by BacktestResult Named Tuple
BT_DATA_COLUMNS_MID = ['pair', 'profit_percent', 'open_date', 'close_date', 'trade_duration',
'open_rate', 'close_rate', 'open_at_end', 'sell_reason', 'fee_open',
'fee_close', 'amount', 'profit_abs', 'profit_ratio']
# Newest format
BT_DATA_COLUMNS = ['pair', 'stake_amount', 'amount', 'open_date', 'close_date',
'open_rate', 'close_rate',
@@ -106,10 +100,30 @@ def get_latest_hyperopt_file(directory: Union[Path, str], predef_filename: str =
if isinstance(directory, str):
directory = Path(directory)
if predef_filename:
if Path(predef_filename).is_absolute():
raise OperationalException(
"--hyperopt-filename expects only the filename, not an absolute path.")
return directory / predef_filename
return directory / get_latest_hyperopt_filename(directory)
def load_backtest_metadata(filename: Union[Path, str]) -> Dict[str, Any]:
"""
Read metadata dictionary from backtest results file without reading and deserializing entire
file.
:param filename: path to backtest results file.
:return: metadata dict or None if metadata is not present.
"""
filename = get_backtest_metadata_filename(filename)
try:
with filename.open() as fp:
return json_load(fp)
except FileNotFoundError:
return {}
except Exception as e:
raise OperationalException('Unexpected error while loading backtest metadata.') from e
def load_backtest_stats(filename: Union[Path, str]) -> Dict[str, Any]:
"""
Load backtest statistics file.
@@ -126,9 +140,80 @@ def load_backtest_stats(filename: Union[Path, str]) -> Dict[str, Any]:
with filename.open() as file:
data = json_load(file)
# Legacy list format does not contain metadata.
if isinstance(data, dict):
data['metadata'] = load_backtest_metadata(filename)
return data
def _load_and_merge_backtest_result(strategy_name: str, filename: Path, results: Dict[str, Any]):
bt_data = load_backtest_stats(filename)
for k in ('metadata', 'strategy'):
results[k][strategy_name] = bt_data[k][strategy_name]
comparison = bt_data['strategy_comparison']
for i in range(len(comparison)):
if comparison[i]['key'] == strategy_name:
results['strategy_comparison'].append(comparison[i])
break
def find_existing_backtest_stats(dirname: Union[Path, str], run_ids: Dict[str, str],
min_backtest_date: datetime = None) -> Dict[str, Any]:
"""
Find existing backtest stats that match specified run IDs and load them.
:param dirname: pathlib.Path object, or string pointing to the file.
:param run_ids: {strategy_name: id_string} dictionary.
:param min_backtest_date: do not load a backtest older than specified date.
:return: results dict.
"""
# Copy so we can modify this dict without affecting parent scope.
run_ids = copy(run_ids)
dirname = Path(dirname)
results: Dict[str, Any] = {
'metadata': {},
'strategy': {},
'strategy_comparison': [],
}
# Weird glob expression here avoids including .meta.json files.
for filename in reversed(sorted(dirname.glob('backtest-result-*-[0-9][0-9].json'))):
metadata = load_backtest_metadata(filename)
if not metadata:
# Files are sorted from newest to oldest. When file without metadata is encountered it
# is safe to assume older files will also not have any metadata.
break
for strategy_name, run_id in list(run_ids.items()):
strategy_metadata = metadata.get(strategy_name, None)
if not strategy_metadata:
# This strategy is not present in analyzed backtest.
continue
if min_backtest_date is not None:
try:
backtest_date = strategy_metadata['backtest_start_time']
except KeyError:
# TODO: this can be removed starting from feb 2022
# The metadata-file without start_time was only available in develop
# and was never included in an official release.
# Older metadata format without backtest time, too old to consider.
return results
backtest_date = datetime.fromtimestamp(backtest_date, tz=timezone.utc)
if backtest_date < min_backtest_date:
# Do not use a cached result for this strategy as first result is too old.
del run_ids[strategy_name]
continue
if strategy_metadata['run_id'] == run_id:
del run_ids[strategy_name]
_load_and_merge_backtest_result(strategy_name, filename, results)
if len(run_ids) == 0:
break
return results
def load_backtest_data(filename: Union[Path, str], strategy: Optional[str] = None) -> pd.DataFrame:
"""
Load backtest data file.
@@ -167,23 +252,9 @@ def load_backtest_data(filename: Union[Path, str], strategy: Optional[str] = Non
)
else:
# old format - only with lists.
df = pd.DataFrame(data, columns=BT_DATA_COLUMNS_OLD)
if not df.empty:
df['open_date'] = pd.to_datetime(df['open_date'],
unit='s',
utc=True,
infer_datetime_format=True
)
df['close_date'] = pd.to_datetime(df['close_date'],
unit='s',
utc=True,
infer_datetime_format=True
)
# Create compatibility with new format
df['profit_abs'] = df['close_rate'] - df['open_rate']
raise OperationalException(
"Backtest-results with only trades data are no longer supported.")
if not df.empty:
if 'profit_ratio' not in df.columns:
df['profit_ratio'] = df['profit_percent']
df = df.sort_values("open_date").reset_index(drop=True)
return df
@@ -325,6 +396,7 @@ def combine_dataframes_with_mean(data: Dict[str, pd.DataFrame],
:param column: Column in the original dataframes to use
:return: DataFrame with the column renamed to the dict key, and a column
named mean, containing the mean of all pairs.
:raise: ValueError if no data is provided.
"""
df_comb = pd.concat([data[pair].set_index('date').rename(
{column: pair}, axis=1)[pair] for pair in data], axis=1)
@@ -360,9 +432,19 @@ def create_cum_profit(df: pd.DataFrame, trades: pd.DataFrame, col_name: str,
return df
def calculate_max_drawdown(trades: pd.DataFrame, *, date_col: str = 'close_date',
value_col: str = 'profit_ratio'
) -> Tuple[float, pd.Timestamp, pd.Timestamp, float, float]:
def _calc_drawdown_series(profit_results: pd.DataFrame, *, date_col: str, value_col: str
) -> pd.DataFrame:
max_drawdown_df = pd.DataFrame()
max_drawdown_df['cumulative'] = profit_results[value_col].cumsum()
max_drawdown_df['high_value'] = max_drawdown_df['cumulative'].cummax()
max_drawdown_df['drawdown'] = max_drawdown_df['cumulative'] - max_drawdown_df['high_value']
max_drawdown_df['date'] = profit_results.loc[:, date_col]
return max_drawdown_df
def calculate_underwater(trades: pd.DataFrame, *, date_col: str = 'close_date',
value_col: str = 'profit_ratio'
):
"""
Calculate max drawdown and the corresponding close dates
:param trades: DataFrame containing trades (requires columns close_date and profit_ratio)
@@ -375,10 +457,29 @@ def calculate_max_drawdown(trades: pd.DataFrame, *, date_col: str = 'close_date'
if len(trades) == 0:
raise ValueError("Trade dataframe empty.")
profit_results = trades.sort_values(date_col).reset_index(drop=True)
max_drawdown_df = pd.DataFrame()
max_drawdown_df['cumulative'] = profit_results[value_col].cumsum()
max_drawdown_df['high_value'] = max_drawdown_df['cumulative'].cummax()
max_drawdown_df['drawdown'] = max_drawdown_df['cumulative'] - max_drawdown_df['high_value']
max_drawdown_df = _calc_drawdown_series(profit_results, date_col=date_col, value_col=value_col)
return max_drawdown_df
def calculate_max_drawdown(trades: pd.DataFrame, *, date_col: str = 'close_date',
value_col: str = 'profit_abs', starting_balance: float = 0
) -> Tuple[float, pd.Timestamp, pd.Timestamp, float, float, float]:
"""
Calculate max drawdown and the corresponding close dates
:param trades: DataFrame containing trades (requires columns close_date and profit_ratio)
:param date_col: Column in DataFrame to use for dates (defaults to 'close_date')
:param value_col: Column in DataFrame to use for values (defaults to 'profit_abs')
:param starting_balance: Portfolio starting balance - properly calculate relative drawdown.
:return: Tuple (float, highdate, lowdate, highvalue, lowvalue, relative_drawdown)
with absolute max drawdown, high and low time and high and low value,
and the relative account drawdown
:raise: ValueError if trade-dataframe was found empty.
"""
if len(trades) == 0:
raise ValueError("Trade dataframe empty.")
profit_results = trades.sort_values(date_col).reset_index(drop=True)
max_drawdown_df = _calc_drawdown_series(profit_results, date_col=date_col, value_col=value_col)
idxmin = max_drawdown_df['drawdown'].idxmin()
if idxmin == 0:
@@ -388,7 +489,18 @@ def calculate_max_drawdown(trades: pd.DataFrame, *, date_col: str = 'close_date'
high_val = max_drawdown_df.loc[max_drawdown_df.iloc[:idxmin]
['high_value'].idxmax(), 'cumulative']
low_val = max_drawdown_df.loc[idxmin, 'cumulative']
return abs(min(max_drawdown_df['drawdown'])), high_date, low_date, high_val, low_val
max_drawdown_rel = 0.0
if high_val + starting_balance != 0:
max_drawdown_rel = (high_val - low_val) / (high_val + starting_balance)
return (
abs(min(max_drawdown_df['drawdown'])),
high_date,
low_date,
high_val,
low_val,
max_drawdown_rel
)
def calculate_csum(trades: pd.DataFrame, starting_balance: float = 0) -> Tuple[float, float]:

View File

@@ -6,7 +6,6 @@ from typing import List, Optional
import numpy as np
import pandas as pd
from freqtrade import misc
from freqtrade.configuration import TimeRange
from freqtrade.constants import (DEFAULT_DATAFRAME_COLUMNS, DEFAULT_TRADES_COLUMNS,
ListPairsWithTimeframes, TradeList)
@@ -61,10 +60,10 @@ class HDF5DataHandler(IDataHandler):
filename = self._pair_data_filename(self._datadir, pair, timeframe)
ds = pd.HDFStore(filename, mode='a', complevel=9, complib='blosc')
ds.put(key, _data.loc[:, self._columns], format='table', data_columns=['date'])
ds.close()
_data.loc[:, self._columns].to_hdf(
filename, key, mode='a', complevel=9, complib='blosc',
format='table', data_columns=['date']
)
def _ohlcv_load(self, pair: str, timeframe: str,
timerange: Optional[TimeRange] = None) -> pd.DataFrame:
@@ -99,19 +98,6 @@ class HDF5DataHandler(IDataHandler):
'low': 'float', 'close': 'float', 'volume': 'float'})
return pairdata
def ohlcv_purge(self, pair: str, timeframe: str) -> bool:
"""
Remove data for this pair
:param pair: Delete data for this pair.
:param timeframe: Timeframe (e.g. "5m")
:return: True when deleted, false if file did not exist.
"""
filename = self._pair_data_filename(self._datadir, pair, timeframe)
if filename.exists():
filename.unlink()
return True
return False
def ohlcv_append(self, pair: str, timeframe: str, data: pd.DataFrame) -> None:
"""
Append data to existing data structures
@@ -142,11 +128,11 @@ class HDF5DataHandler(IDataHandler):
"""
key = self._pair_trades_key(pair)
ds = pd.HDFStore(self._pair_trades_filename(self._datadir, pair),
mode='a', complevel=9, complib='blosc')
ds.put(key, pd.DataFrame(data, columns=DEFAULT_TRADES_COLUMNS),
format='table', data_columns=['timestamp'])
ds.close()
pd.DataFrame(data, columns=DEFAULT_TRADES_COLUMNS).to_hdf(
self._pair_trades_filename(self._datadir, pair), key,
mode='a', complevel=9, complib='blosc',
format='table', data_columns=['timestamp']
)
def trades_append(self, pair: str, data: TradeList):
"""
@@ -180,17 +166,9 @@ class HDF5DataHandler(IDataHandler):
trades[['id', 'type']] = trades[['id', 'type']].replace({np.nan: None})
return trades.values.tolist()
def trades_purge(self, pair: str) -> bool:
"""
Remove data for this pair
:param pair: Delete data for this pair.
:return: True when deleted, false if file did not exist.
"""
filename = self._pair_trades_filename(self._datadir, pair)
if filename.exists():
filename.unlink()
return True
return False
@classmethod
def _get_file_extension(cls):
return "h5"
@classmethod
def _pair_ohlcv_key(cls, pair: str, timeframe: str) -> str:
@@ -199,15 +177,3 @@ class HDF5DataHandler(IDataHandler):
@classmethod
def _pair_trades_key(cls, pair: str) -> str:
return f"{pair}/trades"
@classmethod
def _pair_data_filename(cls, datadir: Path, pair: str, timeframe: str) -> Path:
pair_s = misc.pair_to_filename(pair)
filename = datadir.joinpath(f'{pair_s}-{timeframe}.h5')
return filename
@classmethod
def _pair_trades_filename(cls, datadir: Path, pair: str) -> Path:
pair_s = misc.pair_to_filename(pair)
filename = datadir.joinpath(f'{pair_s}-trades.h5')
return filename

View File

@@ -5,7 +5,7 @@ from pathlib import Path
from typing import Dict, List, Optional, Tuple
import arrow
from pandas import DataFrame
from pandas import DataFrame, concat
from freqtrade.configuration import TimeRange
from freqtrade.constants import DEFAULT_DATAFRAME_COLUMNS
@@ -208,7 +208,7 @@ def _download_pair_history(pair: str, *,
else:
# Run cleaning again to ensure there were no duplicate candles
# Especially between existing and new data.
data = clean_ohlcv_dataframe(data.append(new_dataframe), timeframe, pair,
data = clean_ohlcv_dataframe(concat([data, new_dataframe], axis=0), timeframe, pair,
fill_missing=False, drop_incomplete=False)
logger.debug("New Start: %s",

View File

@@ -12,6 +12,7 @@ from typing import List, Optional, Type
from pandas import DataFrame
from freqtrade import misc
from freqtrade.configuration import TimeRange
from freqtrade.constants import ListPairsWithTimeframes, TradeList
from freqtrade.data.converter import clean_ohlcv_dataframe, trades_remove_duplicates, trim_dataframe
@@ -26,6 +27,13 @@ class IDataHandler(ABC):
def __init__(self, datadir: Path) -> None:
self._datadir = datadir
@classmethod
def _get_file_extension(cls) -> str:
"""
Get file extension for this particular datahandler
"""
raise NotImplementedError()
@abstractclassmethod
def ohlcv_get_available_data(cls, datadir: Path) -> ListPairsWithTimeframes:
"""
@@ -70,7 +78,6 @@ class IDataHandler(ABC):
:return: DataFrame with ohlcv data, or empty DataFrame
"""
@abstractmethod
def ohlcv_purge(self, pair: str, timeframe: str) -> bool:
"""
Remove data for this pair
@@ -78,6 +85,11 @@ class IDataHandler(ABC):
:param timeframe: Timeframe (e.g. "5m")
:return: True when deleted, false if file did not exist.
"""
filename = self._pair_data_filename(self._datadir, pair, timeframe)
if filename.exists():
filename.unlink()
return True
return False
@abstractmethod
def ohlcv_append(self, pair: str, timeframe: str, data: DataFrame) -> None:
@@ -123,13 +135,17 @@ class IDataHandler(ABC):
:return: List of trades
"""
@abstractmethod
def trades_purge(self, pair: str) -> bool:
"""
Remove data for this pair
:param pair: Delete data for this pair.
:return: True when deleted, false if file did not exist.
"""
filename = self._pair_trades_filename(self._datadir, pair)
if filename.exists():
filename.unlink()
return True
return False
def trades_load(self, pair: str, timerange: Optional[TimeRange] = None) -> TradeList:
"""
@@ -141,6 +157,18 @@ class IDataHandler(ABC):
"""
return trades_remove_duplicates(self._trades_load(pair, timerange=timerange))
@classmethod
def _pair_data_filename(cls, datadir: Path, pair: str, timeframe: str) -> Path:
pair_s = misc.pair_to_filename(pair)
filename = datadir.joinpath(f'{pair_s}-{timeframe}.{cls._get_file_extension()}')
return filename
@classmethod
def _pair_trades_filename(cls, datadir: Path, pair: str) -> Path:
pair_s = misc.pair_to_filename(pair)
filename = datadir.joinpath(f'{pair_s}-trades.{cls._get_file_extension()}')
return filename
def ohlcv_load(self, pair, timeframe: str,
timerange: Optional[TimeRange] = None,
fill_missing: bool = True,
@@ -173,7 +201,7 @@ class IDataHandler(ABC):
enddate = pairdf.iloc[-1]['date']
if timerange_startup:
self._validate_pairdata(pair, pairdf, timerange_startup)
self._validate_pairdata(pair, pairdf, timeframe, timerange_startup)
pairdf = trim_dataframe(pairdf, timerange_startup)
if self._check_empty_df(pairdf, pair, timeframe, warn_no_data):
return pairdf
@@ -200,7 +228,7 @@ class IDataHandler(ABC):
return True
return False
def _validate_pairdata(self, pair, pairdata: DataFrame, timerange: TimeRange):
def _validate_pairdata(self, pair, pairdata: DataFrame, timeframe: str, timerange: TimeRange):
"""
Validates pairdata for missing data at start end end and logs warnings.
:param pairdata: Dataframe to validate
@@ -210,12 +238,12 @@ class IDataHandler(ABC):
if timerange.starttype == 'date':
start = datetime.fromtimestamp(timerange.startts, tz=timezone.utc)
if pairdata.iloc[0]['date'] > start:
logger.warning(f"Missing data at start for pair {pair}, "
logger.warning(f"Missing data at start for pair {pair} at {timeframe}, "
f"data starts at {pairdata.iloc[0]['date']:%Y-%m-%d %H:%M:%S}")
if timerange.stoptype == 'date':
stop = datetime.fromtimestamp(timerange.stopts, tz=timezone.utc)
if pairdata.iloc[-1]['date'] < stop:
logger.warning(f"Missing data at end for pair {pair}, "
logger.warning(f"Missing data at end for pair {pair} at {timeframe}, "
f"data ends at {pairdata.iloc[-1]['date']:%Y-%m-%d %H:%M:%S}")

View File

@@ -174,34 +174,10 @@ class JsonDataHandler(IDataHandler):
pass
return tradesdata
def trades_purge(self, pair: str) -> bool:
"""
Remove data for this pair
:param pair: Delete data for this pair.
:return: True when deleted, false if file did not exist.
"""
filename = self._pair_trades_filename(self._datadir, pair)
if filename.exists():
filename.unlink()
return True
return False
@classmethod
def _pair_data_filename(cls, datadir: Path, pair: str, timeframe: str) -> Path:
pair_s = misc.pair_to_filename(pair)
filename = datadir.joinpath(f'{pair_s}-{timeframe}.{cls._get_file_extension()}')
return filename
@classmethod
def _get_file_extension(cls):
return "json.gz" if cls._use_zip else "json"
@classmethod
def _pair_trades_filename(cls, datadir: Path, pair: str) -> Path:
pair_s = misc.pair_to_filename(pair)
filename = datadir.joinpath(f'{pair_s}-trades.{cls._get_file_extension()}')
return filename
class JsonGzDataHandler(JsonDataHandler):

View File

@@ -1,5 +1,6 @@
# flake8: noqa: F401
from freqtrade.enums.backteststate import BacktestState
from freqtrade.enums.ordertypevalue import OrderTypeValues
from freqtrade.enums.rpcmessagetype import RPCMessageType
from freqtrade.enums.runmode import NON_UTIL_MODES, OPTIMIZE_MODES, TRADING_MODES, RunMode
from freqtrade.enums.selltype import SellType

View File

@@ -0,0 +1,6 @@
from enum import Enum
class OrderTypeValues(str, Enum):
limit = 'limit'
market = 'market'

View File

@@ -5,6 +5,7 @@ from freqtrade.exchange.exchange import Exchange
# isort: on
from freqtrade.exchange.bibox import Bibox
from freqtrade.exchange.binance import Binance
from freqtrade.exchange.bitpanda import Bitpanda
from freqtrade.exchange.bittrex import Bittrex
from freqtrade.exchange.bybit import Bybit
from freqtrade.exchange.coinbasepro import Coinbasepro

View File

@@ -0,0 +1,37 @@
""" Bitpanda exchange subclass """
import logging
from datetime import datetime, timezone
from typing import Dict, List, Optional
from freqtrade.exchange import Exchange
logger = logging.getLogger(__name__)
class Bitpanda(Exchange):
"""
Bitpanda exchange class. Contains adjustments needed for Freqtrade to work
with this exchange.
"""
def get_trades_for_order(self, order_id: str, pair: str, since: datetime,
params: Optional[Dict] = None) -> List:
"""
Fetch Orders using the "fetch_my_trades" endpoint and filter them by order-id.
The "since" argument passed in is coming from the database and is in UTC,
as timezone-native datetime object.
From the python documentation:
> Naive datetime instances are assumed to represent local time
Therefore, calling "since.timestamp()" will get the UTC timestamp, after applying the
transformation from local timezone to UTC.
This works for timezones UTC+ since then the result will contain trades from a few hours
instead of from the last 5 seconds, however fails for UTC- timezones,
since we're then asking for trades with a "since" argument in the future.
:param order_id order_id: Order-id as given when creating the order
:param pair: Pair the order is for
:param since: datetime object of the order creation time. Assumes object is in UTC.
"""
params = {'to': int(datetime.now(timezone.utc).timestamp() * 1000)}
return super().get_trades_for_order(order_id, pair, since, params)

View File

@@ -4,9 +4,20 @@ import time
from functools import wraps
from freqtrade.exceptions import DDosProtection, RetryableOrderError, TemporaryError
from freqtrade.mixins import LoggingMixin
logger = logging.getLogger(__name__)
__logging_mixin = None
def _get_logging_mixin():
# Logging-mixin to cache kucoin responses
# Only to be used in retrier
global __logging_mixin
if not __logging_mixin:
__logging_mixin = LoggingMixin(logger)
return __logging_mixin
# Maximum default retry count.
@@ -72,28 +83,33 @@ def calculate_backoff(retrycount, max_retries):
def retrier_async(f):
async def wrapper(*args, **kwargs):
count = kwargs.pop('count', API_RETRY_COUNT)
kucoin = args[0].name == "Kucoin" # Check if the exchange is KuCoin.
try:
return await f(*args, **kwargs)
except TemporaryError as ex:
logger.warning('%s() returned exception: "%s"', f.__name__, ex)
msg = f'{f.__name__}() returned exception: "{ex}". '
if count > 0:
logger.warning('retrying %s() still for %s times', f.__name__, count)
msg += f'Retrying still for {count} times.'
count -= 1
kwargs.update({'count': count})
kwargs['count'] = count
if isinstance(ex, DDosProtection):
if "kucoin" in str(ex) and "429000" in str(ex):
if kucoin and "429000" in str(ex):
# Temporary fix for 429000 error on kucoin
# see https://github.com/freqtrade/freqtrade/issues/5700 for details.
logger.warning(
_get_logging_mixin().log_once(
f"Kucoin 429 error, avoid triggering DDosProtection backoff delay. "
f"{count} tries left before giving up")
f"{count} tries left before giving up", logmethod=logger.warning)
# Reset msg to avoid logging too many times.
msg = ''
else:
backoff_delay = calculate_backoff(count + 1, API_RETRY_COUNT)
logger.info(f"Applying DDosProtection backoff delay: {backoff_delay}")
await asyncio.sleep(backoff_delay)
if msg:
logger.warning(msg)
return await wrapper(*args, **kwargs)
else:
logger.warning('Giving up retrying: %s()', f.__name__)
logger.warning(msg + 'Giving up.')
raise ex
return wrapper
@@ -106,9 +122,9 @@ def retrier(_func=None, retries=API_RETRY_COUNT):
try:
return f(*args, **kwargs)
except (TemporaryError, RetryableOrderError) as ex:
logger.warning('%s() returned exception: "%s"', f.__name__, ex)
msg = f'{f.__name__}() returned exception: "{ex}". '
if count > 0:
logger.warning('retrying %s() still for %s times', f.__name__, count)
logger.warning(msg + f'Retrying still for {count} times.')
count -= 1
kwargs.update({'count': count})
if isinstance(ex, (DDosProtection, RetryableOrderError)):
@@ -118,7 +134,7 @@ def retrier(_func=None, retries=API_RETRY_COUNT):
time.sleep(backoff_delay)
return wrapper(*args, **kwargs)
else:
logger.warning('Giving up retrying: %s()', f.__name__)
logger.warning(msg + 'Giving up.')
raise ex
return wrapper
# Support both @retrier and @retrier(retries=2) syntax

View File

@@ -67,6 +67,8 @@ class Exchange:
"ohlcv_params": {},
"ohlcv_candle_limit": 500,
"ohlcv_partial_candle": True,
# Check https://github.com/ccxt/ccxt/issues/10767 for removal of ohlcv_volume_currency
"ohlcv_volume_currency": "base", # "base" or "quote"
"trades_pagination": "time", # Possible are "time" or "id"
"trades_pagination_arg": "since",
"l2_limit_range": None,
@@ -83,6 +85,8 @@ class Exchange:
self._api: ccxt.Exchange = None
self._api_async: ccxt_async.Exchange = None
self._markets: Dict = {}
self.loop = asyncio.new_event_loop()
asyncio.set_event_loop(self.loop)
self._config.update(config)
@@ -170,8 +174,10 @@ class Exchange:
def close(self):
logger.debug("Exchange object destroyed, closing async loop")
if self._api_async and inspect.iscoroutinefunction(self._api_async.close):
asyncio.get_event_loop().run_until_complete(self._api_async.close())
if (self._api_async and inspect.iscoroutinefunction(self._api_async.close)
and self._api_async.session):
logger.info("Closing async ccxt session.")
self.loop.run_until_complete(self._api_async.close())
def _init_ccxt(self, exchange_config: Dict[str, Any], ccxt_module: CcxtModuleType = ccxt,
ccxt_kwargs: Dict = {}) -> ccxt.Exchange:
@@ -326,7 +332,7 @@ class Exchange:
def _load_async_markets(self, reload: bool = False) -> None:
try:
if self._api_async:
asyncio.get_event_loop().run_until_complete(
self.loop.run_until_complete(
self._api_async.load_markets(reload=reload))
except (asyncio.TimeoutError, ccxt.BaseError) as e:
@@ -606,8 +612,9 @@ class Exchange:
'cost': _amount * rate,
'type': ordertype,
'side': side,
'filled': 0,
'remaining': _amount,
'datetime': arrow.utcnow().isoformat(),
'datetime': arrow.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%fZ'),
'timestamp': arrow.utcnow().int_timestamp * 1000,
'status': "closed" if ordertype == "market" else "open",
'fee': None,
@@ -621,6 +628,7 @@ class Exchange:
average = self.get_dry_market_fill_price(pair, side, amount, rate)
dry_order.update({
'average': average,
'filled': _amount,
'cost': dry_order['amount'] * average,
})
dry_order = self.add_dry_order_fee(pair, dry_order)
@@ -652,7 +660,8 @@ class Exchange:
max_slippage_val = rate * ((1 + slippage) if side == 'buy' else (1 - slippage))
remaining_amount = amount
filled_amount = 0
filled_amount = 0.0
book_entry_price = 0.0
for book_entry in ob[ob_type]:
book_entry_price = book_entry[0]
book_entry_coin_volume = book_entry[1]
@@ -685,16 +694,20 @@ class Exchange:
if not self.exchange_has('fetchL2OrderBook'):
return True
ob = self.fetch_l2_order_book(pair, 1)
if side == 'buy':
price = ob['asks'][0][0]
logger.debug(f"{pair} checking dry buy-order: price={price}, limit={limit}")
if limit >= price:
return True
else:
price = ob['bids'][0][0]
logger.debug(f"{pair} checking dry sell-order: price={price}, limit={limit}")
if limit <= price:
return True
try:
if side == 'buy':
price = ob['asks'][0][0]
logger.debug(f"{pair} checking dry buy-order: price={price}, limit={limit}")
if limit >= price:
return True
else:
price = ob['bids'][0][0]
logger.debug(f"{pair} checking dry sell-order: price={price}, limit={limit}")
if limit <= price:
return True
except IndexError:
# Ignore empty orderbooks when filling - can be filled with the next iteration.
pass
return False
def check_dry_limit_order_filled(self, order: Dict[str, Any]) -> Dict[str, Any]:
@@ -940,7 +953,7 @@ class Exchange:
raise OperationalException(e) from e
@retrier
def get_tickers(self, cached: bool = False) -> Dict:
def get_tickers(self, symbols: List[str] = None, cached: bool = False) -> Dict:
"""
:param cached: Allow cached result
:return: fetch_tickers result
@@ -950,7 +963,7 @@ class Exchange:
if tickers:
return tickers
try:
tickers = self._api.fetch_tickers()
tickers = self._api.fetch_tickers(symbols)
self._fetch_tickers_cache['fetch_tickers'] = tickers
return tickers
except ccxt.NotSupported as e:
@@ -1087,7 +1100,8 @@ class Exchange:
# Fee handling
@retrier
def get_trades_for_order(self, order_id: str, pair: str, since: datetime) -> List:
def get_trades_for_order(self, order_id: str, pair: str, since: datetime,
params: Optional[Dict] = None) -> List:
"""
Fetch Orders using the "fetch_my_trades" endpoint and filter them by order-id.
The "since" argument passed in is coming from the database and is in UTC,
@@ -1111,8 +1125,10 @@ class Exchange:
try:
# Allow 5s offset to catch slight time offsets (discovered in #1185)
# since needs to be int in milliseconds
_params = params if params else {}
my_trades = self._api.fetch_my_trades(
pair, int((since.replace(tzinfo=timezone.utc).timestamp() - 5) * 1000))
pair, int((since.replace(tzinfo=timezone.utc).timestamp() - 5) * 1000),
params=_params)
matched_trades = [trade for trade in my_trades if trade['order'] == order_id]
self._log_exchange_response('get_trades_for_order', matched_trades)
@@ -1190,9 +1206,11 @@ class Exchange:
tick = self.fetch_ticker(comb)
fee_to_quote_rate = safe_value_fallback2(tick, tick, 'last', 'ask')
return round((order['fee']['cost'] * fee_to_quote_rate) / order['cost'], 8)
except ExchangeError:
return None
fee_to_quote_rate = self._config['exchange'].get('unknown_fee_rate', None)
if not fee_to_quote_rate:
return None
return round((order['fee']['cost'] * fee_to_quote_rate) / order['cost'], 8)
def extract_cost_curr_rate(self, order: Dict) -> Tuple[float, str, Optional[float]]:
"""
@@ -1218,7 +1236,7 @@ class Exchange:
:param since_ms: Timestamp in milliseconds to get history from
:return: List with candle (OHLCV) data
"""
pair, timeframe, data = asyncio.get_event_loop().run_until_complete(
pair, timeframe, data = self.loop.run_until_complete(
self._async_get_historic_ohlcv(pair=pair, timeframe=timeframe,
since_ms=since_ms, is_new_pair=is_new_pair))
logger.info(f"Downloaded data for {pair} with length {len(data)}.")
@@ -1263,7 +1281,7 @@ class Exchange:
results = await asyncio.gather(*input_coro, return_exceptions=True)
for res in results:
if isinstance(res, Exception):
logger.warning("Async code raised an exception: %s", res.__class__.__name__)
logger.warning(f"Async code raised an exception: {repr(res)}")
if raise_:
raise
continue
@@ -1294,7 +1312,7 @@ class Exchange:
cached_pairs = []
# Gather coroutines to run
for pair, timeframe in set(pair_list):
if ((pair, timeframe) not in self._klines
if ((pair, timeframe) not in self._klines or not cache
or self._now_is_time_to_refresh(pair, timeframe)):
if not since_ms and self.required_candle_call_count > 1:
# Multiple calls for one pair - to get more history
@@ -1317,27 +1335,32 @@ class Exchange:
)
cached_pairs.append((pair, timeframe))
results = asyncio.get_event_loop().run_until_complete(
asyncio.gather(*input_coroutines, return_exceptions=True))
results_df = {}
# handle caching
for res in results:
if isinstance(res, Exception):
logger.warning("Async code raised an exception: %s", res.__class__.__name__)
continue
# Deconstruct tuple (has 3 elements)
pair, timeframe, ticks = res
# keeping last candle time as last refreshed time of the pair
if ticks:
self._pairs_last_refresh_time[(pair, timeframe)] = ticks[-1][0] // 1000
# keeping parsed dataframe in cache
ohlcv_df = ohlcv_to_dataframe(
ticks, timeframe, pair=pair, fill_missing=True,
drop_incomplete=self._ohlcv_partial_candle)
results_df[(pair, timeframe)] = ohlcv_df
if cache:
self._klines[(pair, timeframe)] = ohlcv_df
# Chunk requests into batches of 100 to avoid overwelming ccxt Throttling
for input_coro in chunks(input_coroutines, 100):
async def gather_stuff():
return await asyncio.gather(*input_coro, return_exceptions=True)
results = self.loop.run_until_complete(gather_stuff())
# handle caching
for res in results:
if isinstance(res, Exception):
logger.warning(f"Async code raised an exception: {repr(res)}")
continue
# Deconstruct tuple (has 3 elements)
pair, timeframe, ticks = res
# keeping last candle time as last refreshed time of the pair
if ticks:
self._pairs_last_refresh_time[(pair, timeframe)] = ticks[-1][0] // 1000
# keeping parsed dataframe in cache
ohlcv_df = ohlcv_to_dataframe(
ticks, timeframe, pair=pair, fill_missing=True,
drop_incomplete=self._ohlcv_partial_candle)
results_df[(pair, timeframe)] = ohlcv_df
if cache:
self._klines[(pair, timeframe)] = ohlcv_df
# Return cached klines
for pair, timeframe in cached_pairs:
results_df[(pair, timeframe)] = self.klines((pair, timeframe), copy=False)
@@ -1554,7 +1577,7 @@ class Exchange:
if not self.exchange_has("fetchTrades"):
raise OperationalException("This exchange does not support downloading Trades.")
return asyncio.get_event_loop().run_until_complete(
return self.loop.run_until_complete(
self._async_get_trade_history(pair=pair, since=since,
until=until, from_id=from_id))

View File

@@ -19,6 +19,7 @@ class Ftx(Exchange):
_ft_has: Dict = {
"stoploss_on_exchange": True,
"ohlcv_candle_limit": 1500,
"ohlcv_volume_currency": "quote",
}
def market_is_tradable(self, market: Dict[str, Any]) -> bool:

View File

@@ -21,6 +21,7 @@ class Gateio(Exchange):
_ft_has: Dict = {
"ohlcv_candle_limit": 1000,
"ohlcv_volume_currency": "quote",
}
_headers = {'X-Gate-Channel-Id': 'freqtrade'}

View File

@@ -1,6 +1,6 @@
""" Kraken exchange subclass """
import logging
from typing import Any, Dict
from typing import Any, Dict, List
import ccxt
@@ -33,6 +33,12 @@ class Kraken(Exchange):
return (parent_check and
market.get('darkpool', False) is False)
def get_tickers(self, symbols: List[str] = None, cached: bool = False) -> Dict:
# Only fetch tickers for current stake currency
# Otherwise the request for kraken becomes too large.
symbols = list(self.get_markets(quote_currencies=[self._config['stake_currency']]))
return super().get_tickers(symbols=symbols, cached=cached)
@retrier
def get_balances(self) -> dict:
if self._config['dry_run']:

View File

@@ -14,5 +14,5 @@ class Okex(Exchange):
"""
_ft_has: Dict = {
"ohlcv_candle_limit": 100,
"ohlcv_candle_limit": 300,
}

View File

@@ -7,16 +7,14 @@ import traceback
from datetime import datetime, timezone
from math import isclose
from threading import Lock
from typing import Any, Dict, List, Optional
import arrow
from typing import Any, Dict, List, Optional, Tuple
from freqtrade import __version__, constants
from freqtrade.configuration import validate_config_consistency
from freqtrade.data.converter import order_book_to_dataframe
from freqtrade.data.dataprovider import DataProvider
from freqtrade.edge import Edge
from freqtrade.enums import RPCMessageType, SellType, State
from freqtrade.enums import RPCMessageType, RunMode, SellType, State
from freqtrade.exceptions import (DependencyException, ExchangeError, InsufficientFundsError,
InvalidOrderException, PricingError)
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds
@@ -126,6 +124,7 @@ class FreqtradeBot(LoggingMixin):
self.rpc.cleanup()
cleanup_db()
self.exchange.close()
def startup(self) -> None:
"""
@@ -178,6 +177,11 @@ class FreqtradeBot(LoggingMixin):
# First process current opened trades (positions)
self.exit_positions(trades)
# Check if we need to adjust our current positions before attempting to buy new trades.
if self.strategy.position_adjustment_enable:
with self._exit_lock:
self.process_open_trade_positions()
# Then looking for buy opportunities
if self.get_free_open_trades():
self.enter_positions()
@@ -278,15 +282,17 @@ class FreqtradeBot(LoggingMixin):
if order:
logger.info(f"Updating sell-fee on trade {trade} for order {order.order_id}.")
self.update_trade_state(trade, order.order_id,
stoploss_order=order.ft_order_side == 'stoploss')
stoploss_order=order.ft_order_side == 'stoploss',
send_msg=False)
trades: List[Trade] = Trade.get_open_trades_without_assigned_fees()
for trade in trades:
if trade.is_open and not trade.fee_updated('buy'):
order = trade.select_order('buy', False)
if order:
open_order = trade.select_order('buy', True)
if order and open_order is None:
logger.info(f"Updating buy-fee on trade {trade} for order {order.order_id}.")
self.update_trade_state(trade, order.order_id)
self.update_trade_state(trade, order.order_id, send_msg=False)
def handle_insufficient_funds(self, trade: Trade):
"""
@@ -308,7 +314,7 @@ class FreqtradeBot(LoggingMixin):
order = trade.select_order('buy', False)
if order:
logger.info(f"Updating buy-fee on trade {trade} for order {order.order_id}.")
self.update_trade_state(trade, order.order_id)
self.update_trade_state(trade, order.order_id, send_msg=False)
def refind_lost_order(self, trade):
"""
@@ -442,6 +448,59 @@ class FreqtradeBot(LoggingMixin):
else:
return False
#
# BUY / increase positions / DCA logic and methods
#
def process_open_trade_positions(self):
"""
Tries to execute additional buy or sell orders for open trades (positions)
"""
# Walk through each pair and check if it needs changes
for trade in Trade.get_open_trades():
# If there is any open orders, wait for them to finish.
if trade.open_order_id is None:
try:
self.check_and_call_adjust_trade_position(trade)
except DependencyException as exception:
logger.warning(
f"Unable to adjust position of trade for {trade.pair}: {exception}")
def check_and_call_adjust_trade_position(self, trade: Trade):
"""
Check the implemented trading strategy for adjustment command.
If the strategy triggers the adjustment, a new order gets issued.
Once that completes, the existing trade is modified to match new data.
"""
if self.strategy.max_entry_position_adjustment > -1:
count_of_buys = trade.nr_of_successful_buys
if count_of_buys > self.strategy.max_entry_position_adjustment:
logger.debug(f"Max adjustment entries for {trade.pair} has been reached.")
return
else:
logger.debug("Max adjustment entries is set to unlimited.")
current_rate = self.exchange.get_rate(trade.pair, refresh=True, side="buy")
current_profit = trade.calc_profit_ratio(current_rate)
min_stake_amount = self.exchange.get_min_pair_stake_amount(trade.pair,
current_rate,
self.strategy.stoploss)
max_stake_amount = 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)(
trade=trade, current_time=datetime.now(timezone.utc), current_rate=current_rate,
current_profit=current_profit, min_stake=min_stake_amount, max_stake=max_stake_amount)
if stake_amount is not None and stake_amount > 0.0:
# We should increase our position
self.execute_entry(trade.pair, stake_amount, trade=trade)
if stake_amount is not None and stake_amount < 0.0:
# We should decrease our position
# TODO: Selling part of the trade not implemented yet.
logger.error(f"Unable to decrease trade position / sell partially"
f" for pair {trade.pair}, feature not implemented.")
def _check_depth_of_market_buy(self, pair: str, conf: Dict) -> bool:
"""
Checks depth of market before executing a buy
@@ -466,58 +525,40 @@ class FreqtradeBot(LoggingMixin):
logger.info(f"Bids to asks delta for {pair} does not satisfy condition.")
return False
def execute_entry(self, pair: str, stake_amount: float, price: Optional[float] = None,
forcebuy: bool = False, buy_tag: Optional[str] = None) -> bool:
def execute_entry(self, pair: str, stake_amount: float, price: Optional[float] = None, *,
ordertype: Optional[str] = None, buy_tag: Optional[str] = None,
trade: Optional[Trade] = None) -> bool:
"""
Executes a limit buy for the given pair
:param pair: pair for which we want to create a LIMIT_BUY
:param stake_amount: amount of stake-currency for the pair
:return: True if a buy order is created, false if it fails.
"""
time_in_force = self.strategy.order_time_in_force['buy']
if price:
enter_limit_requested = price
else:
# Calculate price
proposed_enter_rate = self.exchange.get_rate(pair, refresh=True, side="buy")
custom_entry_price = strategy_safe_wrapper(self.strategy.custom_entry_price,
default_retval=proposed_enter_rate)(
pair=pair, current_time=datetime.now(timezone.utc),
proposed_rate=proposed_enter_rate)
pos_adjust = trade is not None
enter_limit_requested = self.get_valid_price(custom_entry_price, proposed_enter_rate)
if not enter_limit_requested:
raise PricingError('Could not determine buy price.')
min_stake_amount = self.exchange.get_min_pair_stake_amount(pair, enter_limit_requested,
self.strategy.stoploss)
if not self.edge:
max_stake_amount = self.wallets.get_available_stake_amount()
stake_amount = strategy_safe_wrapper(self.strategy.custom_stake_amount,
default_retval=stake_amount)(
pair=pair, current_time=datetime.now(timezone.utc),
current_rate=enter_limit_requested, proposed_stake=stake_amount,
min_stake=min_stake_amount, max_stake=max_stake_amount)
stake_amount = self.wallets.validate_stake_amount(pair, stake_amount, min_stake_amount)
enter_limit_requested, stake_amount = self.get_valid_enter_price_and_stake(
pair, price, stake_amount, buy_tag, trade)
if not stake_amount:
return False
logger.info(f"Buy signal found: about create a new trade for {pair} with stake_amount: "
f"{stake_amount} ...")
if pos_adjust:
logger.info(f"Position adjust: about to create a new order for {pair} with stake: "
f"{stake_amount} for {trade}")
else:
logger.info(f"Buy signal found: about create a new trade for {pair} with stake_amount: "
f"{stake_amount} ...")
amount = stake_amount / enter_limit_requested
order_type = self.strategy.order_types['buy']
if forcebuy:
# Forcebuy can define a different ordertype
order_type = self.strategy.order_types.get('forcebuy', order_type)
order_type = ordertype or self.strategy.order_types['buy']
time_in_force = self.strategy.order_time_in_force['buy']
if not strategy_safe_wrapper(self.strategy.confirm_trade_entry, default_retval=True)(
if not pos_adjust and not strategy_safe_wrapper(
self.strategy.confirm_trade_entry, default_retval=True)(
pair=pair, order_type=order_type, amount=amount, rate=enter_limit_requested,
time_in_force=time_in_force, current_time=datetime.now(timezone.utc)):
time_in_force=time_in_force, current_time=datetime.now(timezone.utc),
entry_tag=buy_tag):
logger.info(f"User requested abortion of buying {pair}")
return False
amount = self.exchange.amount_to_precision(pair, amount)
@@ -527,6 +568,7 @@ class FreqtradeBot(LoggingMixin):
order_obj = Order.parse_from_ccxt_object(order, pair, 'buy')
order_id = order['id']
order_status = order.get('status', None)
logger.info(f"Order #{order_id} was created for {pair} and status is {order_status}.")
# we assume the order is executed at the price requested
enter_limit_filled_price = enter_limit_requested
@@ -562,57 +604,125 @@ class FreqtradeBot(LoggingMixin):
# Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL
fee = self.exchange.get_fee(symbol=pair, taker_or_maker='maker')
trade = Trade(
pair=pair,
stake_amount=stake_amount,
amount=amount,
is_open=True,
amount_requested=amount_requested,
fee_open=fee,
fee_close=fee,
open_rate=enter_limit_filled_price,
open_rate_requested=enter_limit_requested,
open_date=datetime.utcnow(),
exchange=self.exchange.id,
open_order_id=order_id,
strategy=self.strategy.get_strategy_name(),
buy_tag=buy_tag,
timeframe=timeframe_to_minutes(self.config['timeframe'])
)
# This is a new trade
if trade is None:
trade = Trade(
pair=pair,
stake_amount=stake_amount,
amount=amount,
is_open=True,
amount_requested=amount_requested,
fee_open=fee,
fee_close=fee,
open_rate=enter_limit_filled_price,
open_rate_requested=enter_limit_requested,
open_date=datetime.utcnow(),
exchange=self.exchange.id,
open_order_id=order_id,
fee_open_currency=None,
strategy=self.strategy.get_strategy_name(),
buy_tag=buy_tag,
timeframe=timeframe_to_minutes(self.config['timeframe'])
)
else:
# This is additional buy, we reset fee_open_currency so timeout checking can work
trade.is_open = True
trade.fee_open_currency = None
trade.open_rate_requested = enter_limit_requested
trade.open_order_id = order_id
trade.orders.append(order_obj)
# Update fees if order is closed
if order_status == 'closed':
self.update_trade_state(trade, order_id, order)
trade.recalc_trade_from_orders()
Trade.query.session.add(trade)
Trade.commit()
# Updating wallets
self.wallets.update()
self._notify_enter(trade, order_type)
self._notify_enter(trade, order, order_type)
if pos_adjust:
if order_status == 'closed':
logger.info(f"DCA order closed, trade should be up to date: {trade}")
trade = self.cancel_stoploss_on_exchange(trade)
else:
logger.info(f"DCA order {order_status}, will wait for resolution: {trade}")
# Update fees if order is closed
if order_status == 'closed':
self.update_trade_state(trade, order_id, order)
return True
def _notify_enter(self, trade: Trade, order_type: str) -> None:
def cancel_stoploss_on_exchange(self, trade: Trade) -> Trade:
# First cancelling stoploss on exchange ...
if self.strategy.order_types.get('stoploss_on_exchange') and trade.stoploss_order_id:
try:
logger.info(f"Canceling stoploss on exchange for {trade}")
co = self.exchange.cancel_stoploss_order_with_result(
trade.stoploss_order_id, trade.pair, trade.amount)
trade.update_order(co)
except InvalidOrderException:
logger.exception(f"Could not cancel stoploss order {trade.stoploss_order_id}")
return trade
def get_valid_enter_price_and_stake(
self, pair: str, price: Optional[float], stake_amount: float,
entry_tag: Optional[str],
trade: Optional[Trade]) -> Tuple[float, float]:
if price:
enter_limit_requested = price
else:
# Calculate price
proposed_enter_rate = self.exchange.get_rate(pair, refresh=True, side="buy")
custom_entry_price = strategy_safe_wrapper(self.strategy.custom_entry_price,
default_retval=proposed_enter_rate)(
pair=pair, current_time=datetime.now(timezone.utc),
proposed_rate=proposed_enter_rate, entry_tag=entry_tag)
enter_limit_requested = self.get_valid_price(custom_entry_price, proposed_enter_rate)
if not enter_limit_requested:
raise PricingError('Could not determine buy price.')
min_stake_amount = self.exchange.get_min_pair_stake_amount(pair, enter_limit_requested,
self.strategy.stoploss)
if not self.edge and trade is None:
max_stake_amount = self.wallets.get_available_stake_amount()
stake_amount = strategy_safe_wrapper(self.strategy.custom_stake_amount,
default_retval=stake_amount)(
pair=pair, current_time=datetime.now(timezone.utc),
current_rate=enter_limit_requested, proposed_stake=stake_amount,
min_stake=min_stake_amount, max_stake=max_stake_amount, entry_tag=entry_tag)
stake_amount = self.wallets.validate_stake_amount(pair, stake_amount, min_stake_amount)
return enter_limit_requested, stake_amount
def _notify_enter(self, trade: Trade, order: Dict, order_type: Optional[str] = None,
fill: bool = False) -> None:
"""
Sends rpc notification when a buy occurred.
"""
open_rate = safe_value_fallback(order, 'average', 'price')
if open_rate is None:
open_rate = trade.open_rate
current_rate = trade.open_rate_requested
if self.dataprovider.runmode in (RunMode.DRY_RUN, RunMode.LIVE):
current_rate = self.exchange.get_rate(trade.pair, refresh=False, side="buy")
msg = {
'trade_id': trade.id,
'type': RPCMessageType.BUY,
'type': RPCMessageType.BUY_FILL if fill else RPCMessageType.BUY,
'buy_tag': trade.buy_tag,
'exchange': self.exchange.name.capitalize(),
'pair': trade.pair,
'limit': trade.open_rate,
'limit': open_rate, # Deprecated (?)
'open_rate': open_rate,
'order_type': order_type,
'stake_amount': trade.stake_amount,
'stake_currency': self.config['stake_currency'],
'fiat_currency': self.config.get('fiat_display_currency', None),
'amount': trade.amount,
'amount': safe_value_fallback(order, 'filled', 'amount') or trade.amount,
'open_date': trade.open_date or datetime.utcnow(),
'current_rate': trade.open_rate_requested,
'current_rate': current_rate,
}
# Send the message
@@ -644,22 +754,6 @@ class FreqtradeBot(LoggingMixin):
# Send the message
self.rpc.send_msg(msg)
def _notify_enter_fill(self, trade: Trade) -> None:
msg = {
'trade_id': trade.id,
'type': RPCMessageType.BUY_FILL,
'buy_tag': trade.buy_tag,
'exchange': self.exchange.name.capitalize(),
'pair': trade.pair,
'open_rate': trade.open_rate,
'stake_amount': trade.stake_amount,
'stake_currency': self.config['stake_currency'],
'fiat_currency': self.config.get('fiat_display_currency', None),
'amount': trade.amount,
'open_date': trade.open_date,
}
self.rpc.send_msg(msg)
#
# SELL / exit positions / close trades logic and methods
#
@@ -682,7 +776,7 @@ class FreqtradeBot(LoggingMixin):
trades_closed += 1
except DependencyException as exception:
logger.warning('Unable to sell trade %s: %s', trade.pair, exception)
logger.warning(f'Unable to sell trade {trade.pair}: {exception}')
# Updating wallets if any trade occurred
if trades_closed:
@@ -868,24 +962,10 @@ class FreqtradeBot(LoggingMixin):
logger.info(
f'Executing Sell for {trade.pair}. Reason: {should_sell.sell_type}. '
f'Tag: {exit_tag if exit_tag is not None else "None"}')
self.execute_trade_exit(trade, exit_rate, should_sell, exit_tag)
self.execute_trade_exit(trade, exit_rate, should_sell, exit_tag=exit_tag)
return True
return False
def _check_timed_out(self, side: str, order: dict) -> bool:
"""
Check if timeout is active, and if the order is still open and timed out
"""
timeout = self.config.get('unfilledtimeout', {}).get(side)
ordertime = arrow.get(order['datetime']).datetime
if timeout is not None:
timeout_unit = self.config.get('unfilledtimeout', {}).get('unit', 'minutes')
timeout_kwargs = {timeout_unit: -timeout}
timeout_threshold = arrow.utcnow().shift(**timeout_kwargs).datetime
return (order['status'] == 'open' and order['side'] == side
and ordertime < timeout_threshold)
return False
def check_handle_timedout(self) -> None:
"""
Check if any orders are timed out and cancel if necessary
@@ -906,28 +986,28 @@ class FreqtradeBot(LoggingMixin):
if (order['side'] == 'buy' and (order['status'] == 'open' or fully_cancelled) and (
fully_cancelled
or self._check_timed_out('buy', order)
or strategy_safe_wrapper(self.strategy.check_buy_timeout,
default_retval=False)(pair=trade.pair,
trade=trade,
order=order))):
or self.strategy.ft_check_timed_out(
'buy', trade, order, datetime.now(timezone.utc))
)):
self.handle_cancel_enter(trade, order, constants.CANCEL_REASON['TIMEOUT'])
elif (order['side'] == 'sell' and (order['status'] == 'open' or fully_cancelled) and (
fully_cancelled
or self._check_timed_out('sell', order)
or strategy_safe_wrapper(self.strategy.check_sell_timeout,
default_retval=False)(pair=trade.pair,
trade=trade,
order=order))):
or self.strategy.ft_check_timed_out(
'sell', trade, order, datetime.now(timezone.utc)))
):
self.handle_cancel_exit(trade, order, constants.CANCEL_REASON['TIMEOUT'])
canceled_count = trade.get_exit_order_count()
max_timeouts = self.config.get('unfilledtimeout', {}).get('exit_timeout_count', 0)
if max_timeouts > 0 and canceled_count >= max_timeouts:
logger.warning(f'Emergencyselling trade {trade}, as the sell order '
f'timed out {max_timeouts} times.')
self.execute_trade_exit(trade, order.get('price'), sell_reason=SellCheckTuple(
sell_type=SellType.EMERGENCY_SELL))
try:
self.execute_trade_exit(
trade, order.get('price'),
sell_reason=SellCheckTuple(sell_type=SellType.EMERGENCY_SELL))
except DependencyException as exception:
logger.warning(f'Unable to emergency sell trade {trade.pair}: {exception}')
def cancel_all_open_orders(self) -> None:
"""
@@ -987,10 +1067,16 @@ class FreqtradeBot(LoggingMixin):
filled_amount = safe_value_fallback2(corder, order, 'filled', 'filled')
if isclose(filled_amount, 0.0, abs_tol=constants.MATH_CLOSE_PREC):
logger.info('Buy order fully cancelled. Removing %s from database.', trade)
# if trade is not partially completed, just delete the trade
trade.delete()
was_trade_fully_canceled = True
reason += f", {constants.CANCEL_REASON['FULLY_CANCELLED']}"
# if trade is not partially completed and it's the only order, just delete the trade
if len(trade.orders) <= 1:
trade.delete()
was_trade_fully_canceled = True
reason += f", {constants.CANCEL_REASON['FULLY_CANCELLED']}"
else:
# FIXME TODO: This could possibly reworked to not duplicate the code 15 lines below.
self.update_trade_state(trade, trade.open_order_id, corder)
trade.open_order_id = None
logger.info('Partial buy order timeout for %s.', trade)
else:
# if trade is partially complete, edit the stake details for the trade
# and close the order
@@ -1081,7 +1167,10 @@ class FreqtradeBot(LoggingMixin):
trade: Trade,
limit: float,
sell_reason: SellCheckTuple,
exit_tag: Optional[str] = None) -> bool:
*,
exit_tag: Optional[str] = None,
ordertype: Optional[str] = None,
) -> bool:
"""
Executes a trade exit for the given trade and limit
:param trade: Trade instance
@@ -1111,22 +1200,12 @@ class FreqtradeBot(LoggingMixin):
limit = self.get_valid_price(custom_exit_price, proposed_limit_rate)
# First cancelling stoploss on exchange ...
if self.strategy.order_types.get('stoploss_on_exchange') and trade.stoploss_order_id:
try:
co = self.exchange.cancel_stoploss_order_with_result(trade.stoploss_order_id,
trade.pair, trade.amount)
trade.update_order(co)
except InvalidOrderException:
logger.exception(f"Could not cancel stoploss order {trade.stoploss_order_id}")
trade = self.cancel_stoploss_on_exchange(trade)
order_type = self.strategy.order_types[sell_type]
order_type = ordertype or self.strategy.order_types[sell_type]
if sell_reason.sell_type == SellType.EMERGENCY_SELL:
# Emergency sells (default to market!)
order_type = self.strategy.order_types.get("emergencysell", "market")
if sell_reason.sell_type == SellType.FORCE_SELL:
# Force sells (default to the sell_type defined in the strategy,
# but we allow this value to be changed)
order_type = self.strategy.order_types.get("forcesell", order_type)
amount = self._safe_exit_amount(trade.pair, trade.amount)
time_in_force = self.strategy.order_time_in_force['sell']
@@ -1158,16 +1237,16 @@ class FreqtradeBot(LoggingMixin):
trade.sell_order_status = ''
trade.close_rate_requested = limit
trade.sell_reason = exit_tag or sell_reason.sell_reason
# In case of market sell orders the order can be closed immediately
if order.get('status', 'unknown') in ('closed', 'expired'):
self.update_trade_state(trade, trade.open_order_id, order)
Trade.commit()
# Lock pair for one candle to prevent immediate re-buys
self.strategy.lock_pair(trade.pair, datetime.now(timezone.utc),
reason='Auto lock')
self._notify_exit(trade, order_type)
# In case of market sell orders the order can be closed immediately
if order.get('status', 'unknown') in ('closed', 'expired'):
self.update_trade_state(trade, trade.open_order_id, order)
Trade.commit()
return True
@@ -1264,13 +1343,14 @@ class FreqtradeBot(LoggingMixin):
#
def update_trade_state(self, trade: Trade, order_id: str, action_order: Dict[str, Any] = None,
stoploss_order: bool = False) -> bool:
stoploss_order: bool = False, send_msg: bool = True) -> bool:
"""
Checks trades with open orders and updates the amount if necessary
Handles closing both buy and sell orders.
:param trade: Trade object of the trade we're analyzing
:param order_id: Order-id of the order we're analyzing
:param action_order: Already acquired order object
:param send_msg: Send notification - should always be True except in "recovery" methods
:return: True if order has been cancelled without being filled partially, False otherwise
"""
if not order_id:
@@ -1278,7 +1358,7 @@ class FreqtradeBot(LoggingMixin):
return False
# Update trade with order values
logger.info('Found open order for %s', trade)
logger.info(f'Found open order for {trade}')
try:
order = action_order or self.exchange.fetch_order_or_stoploss_order(order_id,
trade.pair,
@@ -1294,29 +1374,26 @@ class FreqtradeBot(LoggingMixin):
# Handling of this will happen in check_handle_timedout.
return True
# Try update amount (binance-fix)
try:
new_amount = self.get_real_amount(trade, order)
if not isclose(safe_value_fallback(order, 'filled', 'amount'), new_amount,
abs_tol=constants.MATH_CLOSE_PREC):
order['amount'] = new_amount
order.pop('filled', None)
trade.recalc_open_trade_value()
except DependencyException as exception:
logger.warning("Could not update trade amount: %s", exception)
order = self.handle_order_fee(trade, order)
trade.update(order)
trade.recalc_trade_from_orders()
Trade.commit()
# Updating wallets when order is closed
if order['status'] in constants.NON_OPEN_EXCHANGE_STATES:
# If a buy order was closed, force update on stoploss on exchange
if order.get('side', None) == 'buy':
trade = self.cancel_stoploss_on_exchange(trade)
# Updating wallets when order is closed
self.wallets.update()
if not trade.is_open:
if not stoploss_order and not trade.open_order_id:
if send_msg and not stoploss_order and not trade.open_order_id:
self._notify_exit(trade, '', True)
self.handle_protections(trade.pair)
self.wallets.update()
elif not trade.open_order_id:
elif send_msg and not trade.open_order_id:
# Buy fill
self._notify_enter_fill(trade)
self._notify_enter(trade, order, fill=True)
return False
@@ -1351,6 +1428,18 @@ class FreqtradeBot(LoggingMixin):
return real_amount
return amount
def handle_order_fee(self, trade: Trade, order: Dict[str, Any]) -> Dict[str, Any]:
# Try update amount (binance-fix)
try:
new_amount = self.get_real_amount(trade, order)
if not isclose(safe_value_fallback(order, 'filled', 'amount'), new_amount,
abs_tol=constants.MATH_CLOSE_PREC):
order['amount'] = new_amount
order.pop('filled', None)
except DependencyException as exception:
logger.warning("Could not update trade amount: %s", exception)
return order
def get_real_amount(self, trade: Trade, order: Dict) -> float:
"""
Detect and update trade fee.

View File

@@ -7,11 +7,25 @@ from typing import Any, Dict
from freqtrade.exceptions import OperationalException
class FTBufferingHandler(BufferingHandler):
def flush(self):
"""
Override Flush behaviour - we keep half of the configured capacity
otherwise, we have moments with "empty" logs.
"""
self.acquire()
try:
# Keep half of the records in buffer.
self.buffer = self.buffer[-int(self.capacity / 2):]
finally:
self.release()
logger = logging.getLogger(__name__)
LOGFORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
# Initialize bufferhandler - will be used for /log endpoints
bufferHandler = BufferingHandler(1000)
bufferHandler = FTBufferingHandler(1000)
bufferHandler.setFormatter(Formatter(LOGFORMAT))

View File

@@ -9,8 +9,8 @@ from typing import Any, List
# check min. python version
if sys.version_info < (3, 7): # pragma: no cover
sys.exit("Freqtrade requires Python version >= 3.7")
if sys.version_info < (3, 8): # pragma: no cover
sys.exit("Freqtrade requires Python version >= 3.8")
from freqtrade.commands import Arguments
from freqtrade.exceptions import FreqtradeException, OperationalException

View File

@@ -2,11 +2,13 @@
Various tool function for Freqtrade and scripts
"""
import gzip
import hashlib
import logging
import re
from copy import deepcopy
from datetime import datetime
from pathlib import Path
from typing import Any, Iterator, List
from typing import Any, Iterator, List, Union
from typing.io import IO
from urllib.parse import urlparse
@@ -228,3 +230,34 @@ def parse_db_uri_for_logging(uri: str):
return uri
pwd = parsed_db_uri.netloc.split(':')[1].split('@')[0]
return parsed_db_uri.geturl().replace(f':{pwd}@', ':*****@')
def get_strategy_run_id(strategy) -> str:
"""
Generate unique identification hash for a backtest run. Identical config and strategy file will
always return an identical hash.
:param strategy: strategy object.
:return: hex string id.
"""
digest = hashlib.sha1()
config = deepcopy(strategy.config)
# Options that have no impact on results of individual backtest.
not_important_keys = ('strategy_list', 'original_config', 'telegram', 'api_server')
for k in not_important_keys:
if k in config:
del config[k]
# Explicitly allow NaN values (e.g. max_open_trades).
# as it does not matter for getting the hash.
digest.update(rapidjson.dumps(config, default=str,
number_mode=rapidjson.NM_NAN).encode('utf-8'))
with open(strategy.__file__, 'rb') as fp:
digest.update(fp.read())
return digest.hexdigest().lower()
def get_backtest_metadata_filename(filename: Union[Path, str]) -> Path:
"""Return metadata filename for specified backtest results file."""
filename = Path(filename)
return filename.parent / Path(f'{filename.stem}.meta{filename.suffix}')

View File

@@ -11,20 +11,22 @@ from typing import Any, Dict, List, Optional, Tuple
from pandas import DataFrame
from freqtrade import constants
from freqtrade.configuration import TimeRange, validate_config_consistency
from freqtrade.constants import DATETIME_PRINT_FORMAT
from freqtrade.data import history
from freqtrade.data.btanalysis import trade_list_to_dataframe
from freqtrade.data.btanalysis import find_existing_backtest_stats, trade_list_to_dataframe
from freqtrade.data.converter import trim_dataframe, trim_dataframes
from freqtrade.data.dataprovider import DataProvider
from freqtrade.enums import BacktestState, SellType
from freqtrade.exceptions import DependencyException, OperationalException
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds
from freqtrade.misc import get_strategy_run_id
from freqtrade.mixins import LoggingMixin
from freqtrade.optimize.bt_progress import BTProgress
from freqtrade.optimize.optimize_reports import (generate_backtest_stats, show_backtest_results,
store_backtest_stats)
from freqtrade.persistence import LocalTrade, PairLocks, Trade
from freqtrade.persistence import LocalTrade, Order, PairLocks, Trade
from freqtrade.plugins.pairlistmanager import PairListManager
from freqtrade.plugins.protectionmanager import ProtectionManager
from freqtrade.resolvers import ExchangeResolver, StrategyResolver
@@ -60,9 +62,10 @@ class Backtesting:
LoggingMixin.show_output = False
self.config = config
self.results: Optional[Dict[str, Any]] = None
self.results: Dict[str, Any] = {}
config['dry_run'] = True
self.run_ids: Dict[str, str] = {}
self.strategylist: List[IStrategy] = []
self.all_results: Dict[str, Dict] = {}
@@ -246,6 +249,9 @@ class Backtesting:
Helper function to convert a processed dataframes into lists for performance reasons.
Used by backtest() - so keep this optimized for performance.
:param processed: a processed dictionary with format {pair, data}, which gets cleared to
optimize memory usage!
"""
# Every change to this headers list must evaluate further usages of the resulting tuple
# and eventually change the constants for indexes at the top
@@ -254,7 +260,8 @@ class Backtesting:
self.progress.init_step(BacktestState.CONVERT, len(processed))
# Create dict with data
for pair, pair_data in processed.items():
for pair in processed.keys():
pair_data = processed[pair]
self.check_abort()
self.progress.increment()
if not pair_data.empty:
@@ -266,8 +273,8 @@ class Backtesting:
df_analyzed = self.strategy.advise_sell(
self.strategy.advise_buy(pair_data, {'pair': pair}), {'pair': pair}).copy()
# Trim startup period from analyzed dataframe
df_analyzed = trim_dataframe(df_analyzed, self.timerange,
startup_candles=self.required_startup)
df_analyzed = processed[pair] = pair_data = trim_dataframe(
df_analyzed, self.timerange, startup_candles=self.required_startup)
# To avoid using data from future, we use buy/sell signals shifted
# from the previous candle
df_analyzed.loc[:, 'buy'] = df_analyzed.loc[:, 'buy'].shift(1)
@@ -342,10 +349,7 @@ class Backtesting:
# use Open rate if open_rate > calculated sell rate
return sell_row[OPEN_IDX]
# Use the maximum between close_rate and low as we
# cannot sell outside of a candle.
# Applies when a new ROI setting comes in place and the whole candle is above that.
return min(max(close_rate, sell_row[LOW_IDX]), sell_row[HIGH_IDX])
return close_rate
else:
# This should not be reached...
@@ -353,8 +357,37 @@ class Backtesting:
else:
return sell_row[OPEN_IDX]
def _get_adjust_trade_entry_for_candle(self, trade: LocalTrade, row: Tuple
) -> LocalTrade:
current_profit = trade.calc_profit_ratio(row[OPEN_IDX])
min_stake = self.exchange.get_min_pair_stake_amount(trade.pair, row[OPEN_IDX], -0.1)
max_stake = self.wallets.get_available_stake_amount()
stake_amount = strategy_safe_wrapper(self.strategy.adjust_trade_position,
default_retval=None)(
trade=trade, current_time=row[DATE_IDX].to_pydatetime(), current_rate=row[OPEN_IDX],
current_profit=current_profit, min_stake=min_stake, max_stake=max_stake)
# Check if we should increase our position
if stake_amount is not None and stake_amount > 0.0:
pos_trade = self._enter_trade(trade.pair, row, stake_amount, trade)
if pos_trade is not None:
return pos_trade
return trade
def _get_sell_trade_entry_for_candle(self, trade: LocalTrade,
sell_row: Tuple) -> Optional[LocalTrade]:
# Check if we need to adjust our current positions
if self.strategy.position_adjustment_enable:
check_adjust_buy = True
if self.strategy.max_entry_position_adjustment > -1:
count_of_buys = trade.nr_of_successful_buys
check_adjust_buy = (count_of_buys <= self.strategy.max_entry_position_adjustment)
if check_adjust_buy:
trade = self._get_adjust_trade_entry_for_candle(trade, sell_row)
sell_candle_time = sell_row[DATE_IDX].to_pydatetime()
sell = self.strategy.should_sell(trade, sell_row[OPEN_IDX], # type: ignore
sell_candle_time, sell_row[BUY_IDX],
@@ -366,6 +399,17 @@ class Backtesting:
trade_dur = int((trade.close_date_utc - trade.open_date_utc).total_seconds() // 60)
closerate = self._get_close_rate(sell_row, trade, sell, trade_dur)
# call the custom exit price,with default value as previous closerate
current_profit = trade.calc_profit_ratio(closerate)
if sell.sell_type in (SellType.SELL_SIGNAL, SellType.CUSTOM_SELL):
# Custom exit pricing only for sell-signals
closerate = strategy_safe_wrapper(self.strategy.custom_exit_price,
default_retval=closerate)(
pair=trade.pair, trade=trade,
current_time=sell_row[DATE_IDX],
proposed_rate=closerate, current_profit=current_profit)
# Use the maximum between close_rate and low as we cannot sell outside of a candle.
closerate = min(max(closerate, sell_row[LOW_IDX]), sell_row[HIGH_IDX])
# Confirm trade exit:
time_in_force = self.strategy.order_time_in_force['sell']
@@ -408,7 +452,9 @@ class Backtesting:
return self._get_sell_trade_entry_for_candle(trade, sell_row)
detail_data.loc[:, 'buy'] = sell_row[BUY_IDX]
detail_data.loc[:, 'sell'] = sell_row[SELL_IDX]
headers = ['date', 'buy', 'open', 'close', 'sell', 'low', 'high']
detail_data.loc[:, 'buy_tag'] = sell_row[BUY_TAG_IDX]
detail_data.loc[:, 'exit_tag'] = sell_row[EXIT_TAG_IDX]
headers = ['date', 'buy', 'open', 'close', 'sell', 'low', 'high', 'buy_tag', 'exit_tag']
for det_row in detail_data[headers].values.tolist():
res = self._get_sell_trade_entry_for_candle(trade, det_row)
if res:
@@ -419,49 +465,94 @@ class Backtesting:
else:
return self._get_sell_trade_entry_for_candle(trade, sell_row)
def _enter_trade(self, pair: str, row: List) -> Optional[LocalTrade]:
try:
stake_amount = self.wallets.get_trade_stake_amount(pair, None)
except DependencyException:
return None
def _enter_trade(self, pair: str, row: Tuple, stake_amount: Optional[float] = None,
trade: Optional[LocalTrade] = None) -> Optional[LocalTrade]:
min_stake_amount = self.exchange.get_min_pair_stake_amount(pair, row[OPEN_IDX], -0.05) or 0
current_time = row[DATE_IDX].to_pydatetime()
entry_tag = row[BUY_TAG_IDX] if len(row) >= BUY_TAG_IDX + 1 else None
# let's call the custom entry price, using the open price as default price
propose_rate = strategy_safe_wrapper(self.strategy.custom_entry_price,
default_retval=row[OPEN_IDX])(
pair=pair, current_time=current_time,
proposed_rate=row[OPEN_IDX], entry_tag=entry_tag) # default value is the open rate
# Move rate to within the candle's low/high rate
propose_rate = min(max(propose_rate, row[LOW_IDX]), row[HIGH_IDX])
min_stake_amount = self.exchange.get_min_pair_stake_amount(pair, propose_rate, -0.05) or 0
max_stake_amount = self.wallets.get_available_stake_amount()
stake_amount = strategy_safe_wrapper(self.strategy.custom_stake_amount,
default_retval=stake_amount)(
pair=pair, current_time=row[DATE_IDX].to_pydatetime(), current_rate=row[OPEN_IDX],
proposed_stake=stake_amount, min_stake=min_stake_amount, max_stake=max_stake_amount)
pos_adjust = trade is not None
if not pos_adjust:
try:
stake_amount = self.wallets.get_trade_stake_amount(pair, None)
except DependencyException:
return trade
stake_amount = strategy_safe_wrapper(self.strategy.custom_stake_amount,
default_retval=stake_amount)(
pair=pair, current_time=current_time, current_rate=propose_rate,
proposed_stake=stake_amount, min_stake=min_stake_amount, max_stake=max_stake_amount,
entry_tag=entry_tag)
stake_amount = self.wallets.validate_stake_amount(pair, stake_amount, min_stake_amount)
if not stake_amount:
return None
# In case of pos adjust, still return the original trade
# If not pos adjust, trade is None
return trade
order_type = self.strategy.order_types['buy']
time_in_force = self.strategy.order_time_in_force['sell']
# Confirm trade entry:
if not strategy_safe_wrapper(self.strategy.confirm_trade_entry, default_retval=True)(
pair=pair, order_type=order_type, amount=stake_amount, rate=row[OPEN_IDX],
time_in_force=time_in_force, current_time=row[DATE_IDX].to_pydatetime()):
return None
if not pos_adjust:
if not strategy_safe_wrapper(self.strategy.confirm_trade_entry, default_retval=True)(
pair=pair, order_type=order_type, amount=stake_amount, rate=propose_rate,
time_in_force=time_in_force, current_time=current_time,
entry_tag=entry_tag):
return None
if stake_amount and (not min_stake_amount or stake_amount > min_stake_amount):
# Enter trade
has_buy_tag = len(row) >= BUY_TAG_IDX + 1
trade = LocalTrade(
pair=pair,
open_rate=row[OPEN_IDX],
open_date=row[DATE_IDX].to_pydatetime(),
stake_amount=stake_amount,
amount=round(stake_amount / row[OPEN_IDX], 8),
fee_open=self.fee,
fee_close=self.fee,
is_open=True,
buy_tag=row[BUY_TAG_IDX] if has_buy_tag else None,
exchange='backtesting',
amount = round(stake_amount / propose_rate, 8)
if trade is None:
# Enter trade
trade = LocalTrade(
pair=pair,
open_rate=propose_rate,
open_date=current_time,
stake_amount=stake_amount,
amount=amount,
fee_open=self.fee,
fee_close=self.fee,
is_open=True,
buy_tag=entry_tag,
exchange='backtesting',
orders=[]
)
trade.adjust_stop_loss(trade.open_rate, self.strategy.stoploss, initial=True)
order = Order(
ft_is_open=False,
ft_pair=trade.pair,
symbol=trade.pair,
ft_order_side="buy",
side="buy",
order_type="market",
status="closed",
order_date=current_time,
order_filled_date=current_time,
order_update_date=current_time,
price=propose_rate,
average=propose_rate,
amount=amount,
filled=amount,
cost=stake_amount + trade.fee_open
)
return trade
return None
trade.orders.append(order)
if pos_adjust:
trade.recalc_trade_from_orders()
return trade
def handle_left_open(self, open_trades: Dict[str, List[LocalTrade]],
data: Dict[str, List[Tuple]]) -> List[LocalTrade]:
@@ -503,7 +594,8 @@ class Backtesting:
Of course try to not have ugly code. By some accessor are sometime slower than functions.
Avoid extensive logging in this method and functions it calls.
:param processed: a processed dictionary with format {pair, data}
:param processed: a processed dictionary with format {pair, data}, which gets cleared to
optimize memory usage!
:param start_date: backtesting timerange start datetime
:param end_date: backtesting timerange end datetime
:param max_open_trades: maximum number of concurrent trades, <= 0 means unlimited
@@ -650,6 +742,7 @@ class Backtesting:
)
backtest_end_time = datetime.now(timezone.utc)
results.update({
'run_id': self.run_ids.get(strat.get_strategy_name(), ''),
'backtest_start_time': int(backtest_start_time.timestamp()),
'backtest_end_time': int(backtest_end_time.timestamp()),
})
@@ -657,6 +750,33 @@ class Backtesting:
return min_date, max_date
def _get_min_cached_backtest_date(self):
min_backtest_date = None
backtest_cache_age = self.config.get('backtest_cache', constants.BACKTEST_CACHE_DEFAULT)
if self.timerange.stopts == 0 or datetime.fromtimestamp(
self.timerange.stopts, tz=timezone.utc) > datetime.now(tz=timezone.utc):
logger.warning('Backtest result caching disabled due to use of open-ended timerange.')
elif backtest_cache_age == 'day':
min_backtest_date = datetime.now(tz=timezone.utc) - timedelta(days=1)
elif backtest_cache_age == 'week':
min_backtest_date = datetime.now(tz=timezone.utc) - timedelta(weeks=1)
elif backtest_cache_age == 'month':
min_backtest_date = datetime.now(tz=timezone.utc) - timedelta(weeks=4)
return min_backtest_date
def load_prior_backtest(self):
self.run_ids = {
strategy.get_strategy_name(): get_strategy_run_id(strategy)
for strategy in self.strategylist
}
# Load previous result that will be updated incrementally.
# This can be circumvented in certain instances in combination with downloading more data
min_backtest_date = self._get_min_cached_backtest_date()
if min_backtest_date is not None:
self.results = find_existing_backtest_stats(
self.config['user_data_dir'] / 'backtest_results', self.run_ids, min_backtest_date)
def start(self) -> None:
"""
Run backtesting end-to-end
@@ -668,15 +788,38 @@ class Backtesting:
self.load_bt_data_detail()
logger.info("Dataload complete. Calculating indicators")
for strat in self.strategylist:
min_date, max_date = self.backtest_one_strategy(strat, data, timerange)
if len(self.strategylist) > 0:
self.load_prior_backtest()
self.results = generate_backtest_stats(data, self.all_results,
min_date=min_date, max_date=max_date)
for strat in self.strategylist:
if self.results and strat.get_strategy_name() in self.results['strategy']:
# When previous result hash matches - reuse that result and skip backtesting.
logger.info(f'Reusing result of previous backtest for {strat.get_strategy_name()}')
continue
min_date, max_date = self.backtest_one_strategy(strat, data, timerange)
# Update old results with new ones.
if len(self.all_results) > 0:
results = generate_backtest_stats(
data, self.all_results, min_date=min_date, max_date=max_date)
if self.results:
self.results['metadata'].update(results['metadata'])
self.results['strategy'].update(results['strategy'])
self.results['strategy_comparison'].extend(results['strategy_comparison'])
else:
self.results = results
if self.config.get('export', 'none') == 'trades':
store_backtest_stats(self.config['exportfilename'], self.results)
# Results may be mixed up now. Sort them so they follow --strategy-list order.
if 'strategy_list' in self.config and len(self.results) > 0:
self.results['strategy_comparison'] = sorted(
self.results['strategy_comparison'],
key=lambda c: self.config['strategy_list'].index(c['key']))
self.results['strategy'] = dict(
sorted(self.results['strategy'].items(),
key=lambda kv: self.config['strategy_list'].index(kv[0])))
if len(self.strategylist) > 0:
# Show backtest results
show_backtest_results(self.config, self.results)

View File

@@ -12,7 +12,7 @@ class BTProgress:
def init_step(self, action: BacktestState, max_steps: float):
self._action = action
self._max_steps = max_steps
self._proress = 0
self._progress = 0
def set_new_value(self, new_value: float):
self._progress = new_value

View File

@@ -34,7 +34,7 @@ class EdgeCli:
self.config['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT
self.exchange = ExchangeResolver.load_exchange(self.config['exchange']['name'], self.config)
self.strategy = StrategyResolver.load_strategy(self.config)
self.strategy.dp = DataProvider(config, None)
self.strategy.dp = DataProvider(config, self.exchange)
validate_config_consistency(self.config)

View File

@@ -76,6 +76,7 @@ class Hyperopt:
self.config = config
self.backtesting = Backtesting(self.config)
self.pairlist = self.backtesting.pairlists.whitelist
if not self.config.get('hyperopt'):
self.custom_hyperopt = HyperOptAuto(self.config)
@@ -332,7 +333,7 @@ class Hyperopt:
params_details = self._get_params_details(params_dict)
strat_stats = generate_strategy_stats(
processed, self.backtesting.strategy.get_strategy_name(),
self.pairlist, self.backtesting.strategy.get_strategy_name(),
backtesting_results, min_date, max_date, market_change=0
)
results_explanation = HyperoptTools.format_results_explanation_string(
@@ -366,7 +367,7 @@ class Hyperopt:
}
def get_optimizer(self, dimensions: List[Dimension], cpu_count) -> Optimizer:
estimator = self.custom_hyperopt.generate_estimator()
estimator = self.custom_hyperopt.generate_estimator(dimensions=dimensions)
acq_optimizer = "sampling"
if isinstance(estimator, str):
@@ -422,6 +423,7 @@ class Hyperopt:
self.backtesting.exchange.close()
self.backtesting.exchange._api = None # type: ignore
self.backtesting.exchange._api_async = None # type: ignore
self.backtesting.exchange.loop = None # type: ignore
# self.backtesting.exchange = None # type: ignore
self.backtesting.pairlists = None # type: ignore

View File

@@ -91,5 +91,5 @@ class HyperOptAuto(IHyperOpt):
def trailing_space(self) -> List['Dimension']:
return self._get_func('trailing_space')()
def generate_estimator(self) -> EstimatorType:
return self._get_func('generate_estimator')()
def generate_estimator(self, dimensions: List['Dimension'], **kwargs) -> EstimatorType:
return self._get_func('generate_estimator')(dimensions=dimensions, **kwargs)

View File

@@ -40,7 +40,7 @@ class IHyperOpt(ABC):
IHyperOpt.ticker_interval = str(config['timeframe']) # DEPRECATED
IHyperOpt.timeframe = str(config['timeframe'])
def generate_estimator(self) -> EstimatorType:
def generate_estimator(self, dimensions: List[Dimension], **kwargs) -> EstimatorType:
"""
Return base_estimator.
Can be any of "GP", "RF", "ET", "GBRT" or an instance of a class

View File

@@ -47,10 +47,9 @@ class CalmarHyperOptLoss(IHyperOptLoss):
# calculate max drawdown
try:
_, _, _, high_val, low_val = calculate_max_drawdown(
_, _, _, _, _, max_drawdown = calculate_max_drawdown(
results, value_col="profit_abs"
)
max_drawdown = (high_val - low_val) / high_val
except ValueError:
max_drawdown = 0

View File

@@ -137,6 +137,7 @@ class HyperoptTools():
}
if not HyperoptTools._test_hyperopt_results_exist(results_file):
# No file found.
logger.warning(f"Hyperopt file {results_file} not found.")
return [], 0
epochs = []
@@ -299,8 +300,7 @@ class HyperoptTools():
f"Objective: {results['loss']:.5f}")
@staticmethod
def prepare_trials_columns(trials: pd.DataFrame, legacy_mode: bool,
has_drawdown: bool) -> pd.DataFrame:
def prepare_trials_columns(trials: pd.DataFrame, has_drawdown: bool) -> pd.DataFrame:
trials['Best'] = ''
if 'results_metrics.winsdrawslosses' not in trials.columns:
@@ -309,33 +309,26 @@ class HyperoptTools():
if not has_drawdown:
# Ensure compatibility with older versions of hyperopt results
trials['results_metrics.max_drawdown_abs'] = None
trials['results_metrics.max_drawdown'] = None
trials['results_metrics.max_drawdown_account'] = None
if not legacy_mode:
# New mode, using backtest result for metrics
trials['results_metrics.winsdrawslosses'] = trials.apply(
lambda x: f"{x['results_metrics.wins']} {x['results_metrics.draws']:>4} "
f"{x['results_metrics.losses']:>4}", axis=1)
trials = trials[['Best', 'current_epoch', 'results_metrics.total_trades',
'results_metrics.winsdrawslosses',
'results_metrics.profit_mean', 'results_metrics.profit_total_abs',
'results_metrics.profit_total', 'results_metrics.holding_avg',
'results_metrics.max_drawdown', 'results_metrics.max_drawdown_abs',
'loss', 'is_initial_point', 'is_best']]
# New mode, using backtest result for metrics
trials['results_metrics.winsdrawslosses'] = trials.apply(
lambda x: f"{x['results_metrics.wins']} {x['results_metrics.draws']:>4} "
f"{x['results_metrics.losses']:>4}", axis=1)
else:
# Legacy mode
trials = trials[['Best', 'current_epoch', 'results_metrics.trade_count',
'results_metrics.winsdrawslosses', 'results_metrics.avg_profit',
'results_metrics.total_profit', 'results_metrics.profit',
'results_metrics.duration', 'results_metrics.max_drawdown',
'results_metrics.max_drawdown_abs', 'loss', 'is_initial_point',
'is_best']]
trials = trials[['Best', 'current_epoch', 'results_metrics.total_trades',
'results_metrics.winsdrawslosses',
'results_metrics.profit_mean', 'results_metrics.profit_total_abs',
'results_metrics.profit_total', 'results_metrics.holding_avg',
'results_metrics.max_drawdown',
'results_metrics.max_drawdown_account', 'results_metrics.max_drawdown_abs',
'loss', 'is_initial_point', 'is_best']]
trials.columns = ['Best', 'Epoch', 'Trades', ' Win Draw Loss', 'Avg profit',
'Total profit', 'Profit', 'Avg duration', 'Max Drawdown',
'max_drawdown_abs', 'Objective', 'is_initial_point', 'is_best']
trials.columns = [
'Best', 'Epoch', 'Trades', ' Win Draw Loss', 'Avg profit',
'Total profit', 'Profit', 'Avg duration', 'max_drawdown', 'max_drawdown_account',
'max_drawdown_abs', 'Objective', 'is_initial_point', 'is_best'
]
return trials
@@ -351,10 +344,9 @@ class HyperoptTools():
tabulate.PRESERVE_WHITESPACE = True
trials = json_normalize(results, max_level=1)
legacy_mode = 'results_metrics.total_trades' not in trials
has_drawdown = 'results_metrics.max_drawdown_abs' in trials.columns
has_account_drawdown = 'results_metrics.max_drawdown_account' in trials.columns
trials = HyperoptTools.prepare_trials_columns(trials, legacy_mode, has_drawdown)
trials = HyperoptTools.prepare_trials_columns(trials, has_account_drawdown)
trials['is_profit'] = False
trials.loc[trials['is_initial_point'], 'Best'] = '* '
@@ -362,12 +354,12 @@ class HyperoptTools():
trials.loc[trials['is_initial_point'] & trials['is_best'], 'Best'] = '* Best'
trials.loc[trials['Total profit'] > 0, 'is_profit'] = True
trials['Trades'] = trials['Trades'].astype(str)
perc_multi = 1 if legacy_mode else 100
# perc_multi = 1 if legacy_mode else 100
trials['Epoch'] = trials['Epoch'].apply(
lambda x: '{}/{}'.format(str(x).rjust(len(str(total_epochs)), ' '), total_epochs)
)
trials['Avg profit'] = trials['Avg profit'].apply(
lambda x: f'{x * perc_multi:,.2f}%'.rjust(7, ' ') if not isna(x) else "--".rjust(7, ' ')
lambda x: f'{x:,.2%}'.rjust(7, ' ') if not isna(x) else "--".rjust(7, ' ')
)
trials['Avg duration'] = trials['Avg duration'].apply(
lambda x: f'{x:,.1f} m'.rjust(7, ' ') if isinstance(x, float) else f"{x}"
@@ -379,24 +371,25 @@ class HyperoptTools():
stake_currency = config['stake_currency']
if has_drawdown:
trials['Max Drawdown'] = trials.apply(
lambda x: '{} {}'.format(
round_coin_value(x['max_drawdown_abs'], stake_currency),
'({:,.2f}%)'.format(x['Max Drawdown'] * perc_multi).rjust(10, ' ')
).rjust(25 + len(stake_currency))
if x['Max Drawdown'] != 0.0 else '--'.rjust(25 + len(stake_currency)),
axis=1
)
else:
trials = trials.drop(columns=['Max Drawdown'])
trials[f"Max Drawdown{' (Acct)' if has_account_drawdown else ''}"] = trials.apply(
lambda x: "{} {}".format(
round_coin_value(x['max_drawdown_abs'], stake_currency),
(f"({x['max_drawdown_account']:,.2%})"
if has_account_drawdown
else f"({x['max_drawdown']:,.2%})"
).rjust(10, ' ')
).rjust(25 + len(stake_currency))
if x['max_drawdown'] != 0.0 or x['max_drawdown_account'] != 0.0
else '--'.rjust(25 + len(stake_currency)),
axis=1
)
trials = trials.drop(columns=['max_drawdown_abs'])
trials = trials.drop(columns=['max_drawdown_abs', 'max_drawdown', 'max_drawdown_account'])
trials['Profit'] = trials.apply(
lambda x: '{} {}'.format(
round_coin_value(x['Total profit'], stake_currency),
'({:,.2f}%)'.format(x['Profit'] * perc_multi).rjust(10, ' ')
f"({x['Profit']:,.2%})".rjust(10, ' ')
).rjust(25+len(stake_currency))
if x['Total profit'] != 0.0 else '--'.rjust(25+len(stake_currency)),
axis=1

View File

@@ -1,4 +1,5 @@
import logging
from copy import deepcopy
from datetime import datetime, timedelta, timezone
from pathlib import Path
from typing import Any, Dict, List, Union
@@ -10,7 +11,8 @@ from tabulate import tabulate
from freqtrade.constants import DATETIME_PRINT_FORMAT, LAST_BT_RESULT_FN, UNLIMITED_STAKE_AMOUNT
from freqtrade.data.btanalysis import (calculate_csum, calculate_market_change,
calculate_max_drawdown)
from freqtrade.misc import decimals_per_coin, file_dump_json, round_coin_value
from freqtrade.misc import (decimals_per_coin, file_dump_json, get_backtest_metadata_filename,
round_coin_value)
logger = logging.getLogger(__name__)
@@ -32,6 +34,11 @@ def store_backtest_stats(recordfilename: Path, stats: Dict[str, DataFrame]) -> N
recordfilename.parent,
f'{recordfilename.stem}-{datetime.now().strftime("%Y-%m-%d_%H-%M-%S")}'
).with_suffix(recordfilename.suffix)
# Store metadata separately.
file_dump_json(get_backtest_metadata_filename(filename), stats['metadata'])
del stats['metadata']
file_dump_json(filename, stats)
latest_filename = Path.joinpath(filename.parent, LAST_BT_RESULT_FN)
@@ -98,11 +105,11 @@ def _generate_result_line(result: DataFrame, starting_balance: int, first_column
}
def generate_pair_metrics(data: Dict[str, Dict], stake_currency: str, starting_balance: int,
def generate_pair_metrics(pairlist: List[str], stake_currency: str, starting_balance: int,
results: DataFrame, skip_nan: bool = False) -> List[Dict]:
"""
Generates and returns a list for the given backtest data and the results dataframe
:param data: Dict of <pair: dataframe> containing data that was used during backtesting.
:param pairlist: Pairlist used
:param stake_currency: stake-currency - used to correctly name headers
:param starting_balance: Starting balance
:param results: Dataframe containing the backtest results
@@ -112,7 +119,7 @@ def generate_pair_metrics(data: Dict[str, Dict], stake_currency: str, starting_b
tabular_data = []
for pair in data:
for pair in pairlist:
result = results[results['pair'] == pair]
if skip_nan and result['profit_abs'].isnull().all():
continue
@@ -194,29 +201,21 @@ def generate_sell_reason_stats(max_open_trades: int, results: DataFrame) -> List
return tabular_data
def generate_strategy_comparison(all_results: Dict) -> List[Dict]:
def generate_strategy_comparison(bt_stats: Dict) -> List[Dict]:
"""
Generate summary per strategy
:param all_results: Dict of <Strategyname: DataFrame> containing results for all strategies
:param bt_stats: Dict of <Strategyname: DataFrame> containing results for all strategies
:return: List of Dicts containing the metrics per Strategy
"""
tabular_data = []
for strategy, results in all_results.items():
tabular_data.append(_generate_result_line(
results['results'], results['config']['dry_run_wallet'], strategy)
)
try:
max_drawdown_per, _, _, _, _ = calculate_max_drawdown(results['results'],
value_col='profit_ratio')
max_drawdown_abs, _, _, _, _ = calculate_max_drawdown(results['results'],
value_col='profit_abs')
except ValueError:
max_drawdown_per = 0
max_drawdown_abs = 0
tabular_data[-1]['max_drawdown_per'] = round(max_drawdown_per * 100, 2)
tabular_data[-1]['max_drawdown_abs'] = \
round_coin_value(max_drawdown_abs, results['config']['stake_currency'], False)
for strategy, result in bt_stats.items():
tabular_data.append(deepcopy(result['results_per_pair'][-1]))
# Update "key" to strategy (results_per_pair has it as "Total").
tabular_data[-1]['key'] = strategy
tabular_data[-1]['max_drawdown_account'] = result['max_drawdown_account']
tabular_data[-1]['max_drawdown_abs'] = round_coin_value(
result['max_drawdown_abs'], result['stake_currency'], False)
return tabular_data
@@ -352,14 +351,14 @@ def generate_daily_stats(results: DataFrame) -> Dict[str, Any]:
}
def generate_strategy_stats(btdata: Dict[str, DataFrame],
def generate_strategy_stats(pairlist: List[str],
strategy: str,
content: Dict[str, Any],
min_date: datetime, max_date: datetime,
market_change: float
) -> Dict[str, Any]:
"""
:param btdata: Backtest data
:param pairlist: List of pairs to backtest
:param strategy: Strategy name
:param content: Backtest result data in the format:
{'results: results, 'config: config}}.
@@ -372,11 +371,11 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame],
if not isinstance(results, DataFrame):
return {}
config = content['config']
max_open_trades = min(config['max_open_trades'], len(btdata.keys()))
max_open_trades = min(config['max_open_trades'], len(pairlist))
starting_balance = config['dry_run_wallet']
stake_currency = config['stake_currency']
pair_results = generate_pair_metrics(btdata, stake_currency=stake_currency,
pair_results = generate_pair_metrics(pairlist, stake_currency=stake_currency,
starting_balance=starting_balance,
results=results, skip_nan=False)
@@ -385,7 +384,7 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame],
sell_reason_stats = generate_sell_reason_stats(max_open_trades=max_open_trades,
results=results)
left_open_results = generate_pair_metrics(btdata, stake_currency=stake_currency,
left_open_results = generate_pair_metrics(pairlist, stake_currency=stake_currency,
starting_balance=starting_balance,
results=results.loc[results['is_open']],
skip_nan=True)
@@ -429,7 +428,7 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame],
'trades_per_day': round(len(results) / backtest_days, 2),
'market_change': market_change,
'pairlist': list(btdata.keys()),
'pairlist': pairlist,
'stake_amount': config['stake_amount'],
'stake_currency': config['stake_currency'],
'stake_currency_decimals': decimals_per_coin(config['stake_currency']),
@@ -462,12 +461,14 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame],
}
try:
max_drawdown, _, _, _, _ = calculate_max_drawdown(
max_drawdown_legacy, _, _, _, _, _ = calculate_max_drawdown(
results, value_col='profit_ratio')
drawdown_abs, drawdown_start, drawdown_end, high_val, low_val = calculate_max_drawdown(
results, value_col='profit_abs')
(drawdown_abs, drawdown_start, drawdown_end, high_val, low_val,
max_drawdown) = calculate_max_drawdown(
results, value_col='profit_abs', starting_balance=starting_balance)
strat_stats.update({
'max_drawdown': max_drawdown,
'max_drawdown': max_drawdown_legacy, # Deprecated - do not use
'max_drawdown_account': max_drawdown,
'max_drawdown_abs': drawdown_abs,
'drawdown_start': drawdown_start.strftime(DATETIME_PRINT_FORMAT),
'drawdown_start_ts': drawdown_start.timestamp() * 1000,
@@ -487,6 +488,7 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame],
except ValueError:
strat_stats.update({
'max_drawdown': 0.0,
'max_drawdown_account': 0.0,
'max_drawdown_abs': 0.0,
'max_drawdown_low': 0.0,
'max_drawdown_high': 0.0,
@@ -513,16 +515,26 @@ def generate_backtest_stats(btdata: Dict[str, DataFrame],
:param max_date: Backtest end date
:return: Dictionary containing results per strategy and a strategy summary.
"""
result: Dict[str, Any] = {'strategy': {}}
result: Dict[str, Any] = {
'metadata': {},
'strategy': {},
'strategy_comparison': [],
}
market_change = calculate_market_change(btdata, 'close')
metadata = {}
pairlist = list(btdata.keys())
for strategy, content in all_results.items():
strat_stats = generate_strategy_stats(btdata, strategy, content,
strat_stats = generate_strategy_stats(pairlist, strategy, content,
min_date, max_date, market_change=market_change)
metadata[strategy] = {
'run_id': content['run_id'],
'backtest_start_time': content['backtest_start_time'],
}
result['strategy'][strategy] = strat_stats
strategy_results = generate_strategy_comparison(all_results=all_results)
strategy_results = generate_strategy_comparison(bt_stats=result['strategy'])
result['metadata'] = metadata
result['strategy_comparison'] = strategy_results
return result
@@ -646,7 +658,12 @@ def text_table_strategy(strategy_results, stake_currency: str) -> str:
headers.append('Drawdown')
# Align drawdown string on the center two space separator.
drawdown = [f'{t["max_drawdown_per"]:.2f}' for t in strategy_results]
if 'max_drawdown_account' in strategy_results[0]:
drawdown = [f'{t["max_drawdown_account"] * 100:.2f}' for t in strategy_results]
else:
# Support for prior backtest results
drawdown = [f'{t["max_drawdown_per"]:.2f}' for t in strategy_results]
dd_pad_abs = max([len(t['max_drawdown_abs']) for t in strategy_results])
dd_pad_per = max([len(dd) for dd in drawdown])
drawdown = [f'{t["max_drawdown_abs"]:>{dd_pad_abs}} {stake_currency} {dd:>{dd_pad_per}}%'
@@ -716,7 +733,10 @@ def text_table_add_metrics(strat_results: Dict) -> str:
('Max balance', round_coin_value(strat_results['csum_max'],
strat_results['stake_currency'])),
('Drawdown', f"{strat_results['max_drawdown']:.2%}"),
# Compatibility to show old hyperopt results
('Drawdown (Account)', f"{strat_results['max_drawdown_account']:.2%}")
if 'max_drawdown_account' in strat_results else (
'Drawdown', f"{strat_results['max_drawdown']:.2%}"),
('Drawdown', round_coin_value(strat_results['max_drawdown_abs'],
strat_results['stake_currency'])),
('Drawdown high', round_coin_value(strat_results['max_drawdown_high'],

View File

@@ -424,10 +424,10 @@ class LocalTrade():
# Update open rate and actual amount
self.open_rate = float(safe_value_fallback(order, 'average', 'price'))
self.amount = float(safe_value_fallback(order, 'filled', 'amount'))
self.recalc_open_trade_value()
if self.is_open:
logger.info(f'{order_type.upper()}_BUY has been fulfilled for {self}.')
self.open_order_id = None
self.recalc_trade_from_orders()
elif order_type in ('market', 'limit') and order['side'] == 'sell':
if self.is_open:
logger.info(f'{order_type.upper()}_SELL has been fulfilled for {self}.')
@@ -568,6 +568,38 @@ class LocalTrade():
profit_ratio = (close_trade_value / self.open_trade_value) - 1
return float(f"{profit_ratio:.8f}")
def recalc_trade_from_orders(self):
# We need at least 2 entry orders for averaging amounts and rates.
if len(self.select_filled_orders('buy')) < 2:
# Just in case, still recalc open trade value
self.recalc_open_trade_value()
return
total_amount = 0.0
total_stake = 0.0
for o in self.orders:
if (o.ft_is_open or
(o.ft_order_side != 'buy') or
(o.status not in NON_OPEN_EXCHANGE_STATES)):
continue
tmp_amount = o.amount
tmp_price = o.average or o.price
if o.filled is not None:
tmp_amount = o.filled
if tmp_amount > 0.0 and tmp_price is not None:
total_amount += tmp_amount
total_stake += tmp_price * tmp_amount
if total_amount > 0:
self.open_rate = total_stake / total_amount
self.stake_amount = total_stake
self.amount = total_amount
self.fee_open_cost = self.fee_open * self.stake_amount
self.recalc_open_trade_value()
if self.stop_loss_pct is not None and self.open_rate is not None:
self.adjust_stop_loss(self.open_rate, self.stop_loss_pct)
def select_order(self, order_side: str, is_open: Optional[bool]) -> Optional[Order]:
"""
Finds latest order for this orderside and status
@@ -583,6 +615,34 @@ class LocalTrade():
else:
return None
def select_filled_orders(self, order_side: str) -> List['Order']:
"""
Finds filled orders for this orderside.
:param order_side: Side of the order (either 'buy' or 'sell')
:return: array of Order objects
"""
return [o for o in self.orders if o.ft_order_side == order_side and
o.ft_is_open is False and
(o.filled or 0) > 0 and
o.status in NON_OPEN_EXCHANGE_STATES]
@property
def nr_of_successful_buys(self) -> int:
"""
Helper function to count the number of buy orders that have been filled.
:return: int count of buy orders that have been filled for this trade.
"""
return len(self.select_filled_orders('buy'))
@property
def nr_of_successful_sells(self) -> int:
"""
Helper function to count the number of sell orders that have been filled.
:return: int count of sell orders that have been filled for this trade.
"""
return len(self.select_filled_orders('sell'))
@staticmethod
def get_trades_proxy(*, pair: str = None, is_open: bool = None,
open_date: datetime = None, close_date: datetime = None,
@@ -670,7 +730,7 @@ class Trade(_DECL_BASE, LocalTrade):
id = Column(Integer, primary_key=True)
orders = relationship("Order", order_by="Order.id", cascade="all, delete-orphan")
orders = relationship("Order", order_by="Order.id", cascade="all, delete-orphan", lazy="joined")
exchange = Column(String(25), nullable=False)
pair = Column(String(25), nullable=False, index=True)

View File

@@ -5,7 +5,8 @@ from typing import Any, Dict, List
import pandas as pd
from freqtrade.configuration import TimeRange
from freqtrade.data.btanalysis import (calculate_max_drawdown, combine_dataframes_with_mean,
from freqtrade.data.btanalysis import (analyze_trade_parallelism, calculate_max_drawdown,
calculate_underwater, combine_dataframes_with_mean,
create_cum_profit, extract_trades_of_period, load_trades)
from freqtrade.data.converter import trim_dataframe
from freqtrade.data.dataprovider import DataProvider
@@ -160,7 +161,7 @@ def add_max_drawdown(fig, row, trades: pd.DataFrame, df_comb: pd.DataFrame,
Add scatter points indicating max drawdown
"""
try:
max_drawdown, highdate, lowdate, _, _ = calculate_max_drawdown(trades)
_, highdate, lowdate, _, _, max_drawdown = calculate_max_drawdown(trades)
drawdown = go.Scatter(
x=[highdate, lowdate],
@@ -185,6 +186,48 @@ def add_max_drawdown(fig, row, trades: pd.DataFrame, df_comb: pd.DataFrame,
return fig
def add_underwater(fig, row, trades: pd.DataFrame) -> make_subplots:
"""
Add underwater plot
"""
try:
underwater = calculate_underwater(trades, value_col="profit_abs")
underwater = go.Scatter(
x=underwater['date'],
y=underwater['drawdown'],
name="Underwater Plot",
fill='tozeroy',
fillcolor='#cc362b',
line={'color': '#cc362b'},
)
fig.add_trace(underwater, row, 1)
except ValueError:
logger.warning("No trades found - not plotting underwater plot")
return fig
def add_parallelism(fig, row, trades: pd.DataFrame, timeframe: str) -> make_subplots:
"""
Add Chart showing trade parallelism
"""
try:
result = analyze_trade_parallelism(trades, timeframe)
drawdown = go.Scatter(
x=result.index,
y=result['open_trades'],
name="Parallel trades",
fill='tozeroy',
fillcolor='#242222',
line={'color': '#242222'},
)
fig.add_trace(drawdown, row, 1)
except ValueError:
logger.warning("No trades found - not plotting Parallelism.")
return fig
def plot_trades(fig, trades: pd.DataFrame) -> make_subplots:
"""
Add trades to "fig"
@@ -192,10 +235,12 @@ def plot_trades(fig, trades: pd.DataFrame) -> make_subplots:
# Trades can be empty
if trades is not None and len(trades) > 0:
# Create description for sell summarizing the trade
trades['desc'] = trades.apply(lambda row: f"{row['profit_ratio']:.2%}, "
f"{row['sell_reason']}, "
f"{row['trade_duration']} min",
axis=1)
trades['desc'] = trades.apply(
lambda row: f"{row['profit_ratio']:.2%}, " +
(f"{row['buy_tag']}, " if row['buy_tag'] is not None else "") +
f"{row['sell_reason']}, " +
f"{row['trade_duration']} min",
axis=1)
trade_buys = go.Scatter(
x=trades["open_date"],
y=trades["open_rate"],
@@ -460,7 +505,12 @@ def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFra
def generate_profit_graph(pairs: str, data: Dict[str, pd.DataFrame],
trades: pd.DataFrame, timeframe: str, stake_currency: str) -> go.Figure:
# Combine close-values for all pairs, rename columns to "pair"
df_comb = combine_dataframes_with_mean(data, "close")
try:
df_comb = combine_dataframes_with_mean(data, "close")
except ValueError:
raise OperationalException(
"No data found. Please make sure that data is available for "
"the timerange and pairs selected.")
# Trim trades to available OHLCV data
trades = extract_trades_of_period(df_comb, trades, date_index=True)
@@ -477,20 +527,30 @@ def generate_profit_graph(pairs: str, data: Dict[str, pd.DataFrame],
name='Avg close price',
)
fig = make_subplots(rows=3, cols=1, shared_xaxes=True,
row_width=[1, 1, 1],
fig = make_subplots(rows=5, cols=1, shared_xaxes=True,
row_heights=[1, 1, 1, 0.5, 1],
vertical_spacing=0.05,
subplot_titles=["AVG Close Price", "Combined Profit", "Profit per pair"])
subplot_titles=[
"AVG Close Price",
"Combined Profit",
"Profit per pair",
"Parallelism",
"Underwater",
])
fig['layout'].update(title="Freqtrade Profit plot")
fig['layout']['yaxis1'].update(title='Price')
fig['layout']['yaxis2'].update(title=f'Profit {stake_currency}')
fig['layout']['yaxis3'].update(title=f'Profit {stake_currency}')
fig['layout']['yaxis4'].update(title='Trade count')
fig['layout']['yaxis5'].update(title='Underwater Plot')
fig['layout']['xaxis']['rangeslider'].update(visible=False)
fig.update_layout(modebar_add=["v1hovermode", "toggleSpikeLines"])
fig.add_trace(avgclose, 1, 1)
fig = add_profit(fig, 2, df_comb, 'cum_profit', 'Profit')
fig = add_max_drawdown(fig, 2, trades, df_comb, timeframe)
fig = add_parallelism(fig, 4, trades, timeframe)
fig = add_underwater(fig, 5, trades)
for pair in pairs:
profit_col = f'cum_profit_{pair}'

View File

@@ -68,14 +68,14 @@ class PerformanceFilter(IPairList):
# - then pair name alphametically
sorted_df = list_df.merge(performance, on='pair', how='left')\
.fillna(0).sort_values(by=['count', 'pair'], ascending=True)\
.sort_values(by=['profit'], ascending=False)
.sort_values(by=['profit_ratio'], ascending=False)
if self._min_profit is not None:
removed = sorted_df[sorted_df['profit'] < self._min_profit]
removed = sorted_df[sorted_df['profit_ratio'] < self._min_profit]
for _, row in removed.iterrows():
self.log_once(
f"Removing pair {row['pair']} since {row['profit']} is "
f"Removing pair {row['pair']} since {row['profit_ratio']} is "
f"below {self._min_profit}", logger.info)
sorted_df = sorted_df[sorted_df['profit'] >= self._min_profit]
sorted_df = sorted_df[sorted_df['profit_ratio'] >= self._min_profit]
pairlist = sorted_df['pair'].tolist()

View File

@@ -5,6 +5,7 @@ import logging
import random
from typing import Any, Dict, List
from freqtrade.enums import RunMode
from freqtrade.plugins.pairlist.IPairList import IPairList
@@ -18,7 +19,15 @@ class ShuffleFilter(IPairList):
pairlist_pos: int) -> None:
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
self._seed = pairlistconfig.get('seed')
# Apply seed in backtesting mode to get comparable results,
# but not in live modes to get a non-repeating order of pairs during live modes.
if config.get('runmode') in (RunMode.LIVE, RunMode.DRY_RUN):
self._seed = None
logger.info("Live mode detected, not applying seed.")
else:
self._seed = pairlistconfig.get('seed')
logger.info(f"Backtesting mode detected, applying seed value: {self._seed}")
self._random = random.Random(self._seed)
@property

View File

@@ -47,7 +47,7 @@ class SpreadFilter(IPairList):
spread = 1 - ticker['bid'] / ticker['ask']
if spread > self._max_spread_ratio:
self.log_once(f"Removed {pair} from whitelist, because spread "
f"{spread * 100:.3%} > {self._max_spread_ratio:.3%}",
f"{spread:.3%} > {self._max_spread_ratio:.3%}",
logger.info)
return False
else:

View File

@@ -8,7 +8,7 @@ from typing import Any, Dict, List, Optional
import arrow
import numpy as np
from cachetools.ttl import TTLCache
from cachetools import TTLCache
from pandas import DataFrame
from freqtrade.exceptions import OperationalException

View File

@@ -4,11 +4,10 @@ Volume PairList provider
Provides dynamic pair list based on trade volumes
"""
import logging
from functools import partial
from typing import Any, Dict, List
import arrow
from cachetools.ttl import TTLCache
from cachetools import TTLCache
from freqtrade.exceptions import OperationalException
from freqtrade.exchange import timeframe_to_minutes
@@ -120,10 +119,17 @@ class VolumePairList(IPairList):
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],
pairs_only=True, active_only=True).keys()]
# No point in testing for blacklisted pairs...
_pairlist = self.verify_blacklist(_pairlist, logger.info)
filtered_tickers = [
v for k, v in tickers.items()
if (self._exchange.get_pair_quote_currency(k) == self._stake_currency
and (self._use_range or v[self._sort_key] is not None))]
and (self._use_range or v[self._sort_key] is not None)
and v['symbol'] in _pairlist)]
pairlist = [s['symbol'] for s in filtered_tickers]
pairlist = self.filter_pairlist(pairlist, tickers)
@@ -178,12 +184,16 @@ class VolumePairList(IPairList):
] if (p['symbol'], self._lookback_timeframe) in candles else None
# in case of candle data calculate typical price and quoteVolume for candle
if pair_candles is not None and not pair_candles.empty:
pair_candles['typical_price'] = (pair_candles['high'] + pair_candles['low']
+ pair_candles['close']) / 3
pair_candles['quoteVolume'] = (
pair_candles['volume'] * pair_candles['typical_price']
)
if self._exchange._ft_has["ohlcv_volume_currency"] == "base":
pair_candles['typical_price'] = (pair_candles['high'] + pair_candles['low']
+ pair_candles['close']) / 3
pair_candles['quoteVolume'] = (
pair_candles['volume'] * pair_candles['typical_price']
)
else:
# Exchange ohlcv data is in quote volume already.
pair_candles['quoteVolume'] = pair_candles['volume']
# ensure that a rolling sum over the lookback_period is built
# if pair_candles contains more candles than lookback_period
quoteVolume = (pair_candles['quoteVolume']
@@ -204,7 +214,7 @@ class VolumePairList(IPairList):
# Validate whitelist to only have active market pairs
pairs = self._whitelist_for_active_markets([s['symbol'] for s in sorted_tickers])
pairs = self.verify_blacklist(pairs, partial(self.log_once, logmethod=logger.info))
pairs = self.verify_blacklist(pairs, logmethod=logger.info)
# Limit pairlist to the requested number of pairs
pairs = pairs[:self._number_pairs]

View File

@@ -6,7 +6,7 @@ from copy import deepcopy
from typing import Any, Dict, List, Optional
import arrow
from cachetools.ttl import TTLCache
from cachetools import TTLCache
from pandas import DataFrame
from freqtrade.exceptions import OperationalException

View File

@@ -2,13 +2,14 @@
PairList manager class
"""
import logging
from copy import deepcopy
from functools import partial
from typing import Dict, List
from cachetools import TTLCache, cached
from freqtrade.constants import ListPairsWithTimeframes
from freqtrade.exceptions import OperationalException
from freqtrade.mixins import LoggingMixin
from freqtrade.plugins.pairlist.IPairList import IPairList
from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist
from freqtrade.resolvers import PairListResolver
@@ -17,7 +18,7 @@ from freqtrade.resolvers import PairListResolver
logger = logging.getLogger(__name__)
class PairListManager():
class PairListManager(LoggingMixin):
def __init__(self, exchange, config: dict) -> None:
self._exchange = exchange
@@ -41,6 +42,9 @@ class PairListManager():
if not self._pairlist_handlers:
raise OperationalException("No Pairlist Handlers defined")
refresh_period = config.get('pairlist_refresh_period', 3600)
LoggingMixin.__init__(self, logger, refresh_period)
@property
def whitelist(self) -> List[str]:
"""The current whitelist"""
@@ -108,9 +112,10 @@ class PairListManager():
except ValueError as err:
logger.error(f"Pair blacklist contains an invalid Wildcard: {err}")
return []
for pair in deepcopy(pairlist):
log_once = partial(self.log_once, logmethod=logmethod)
for pair in pairlist.copy():
if pair in blacklist:
logmethod(f"Pair {pair} in your blacklist. Removing it from whitelist...")
log_once(f"Pair {pair} in your blacklist. Removing it from whitelist...")
pairlist.remove(pair)
return pairlist

View File

@@ -55,7 +55,8 @@ class MaxDrawdown(IProtection):
# Drawdown is always positive
try:
drawdown, _, _, _, _ = calculate_max_drawdown(trades_df, value_col='close_profit')
# TODO: This should use absolute profit calculation, considering account balance.
drawdown, _, _, _, _, _ = calculate_max_drawdown(trades_df, value_col='close_profit')
except ValueError:
return False, None, None

View File

@@ -96,7 +96,9 @@ class StrategyResolver(IResolver):
("ignore_roi_if_buy_signal", False),
("sell_profit_offset", 0.0),
("disable_dataframe_checks", False),
("ignore_buying_expired_candle_after", 0)
("ignore_buying_expired_candle_after", 0),
("position_adjustment_enable", False),
("max_entry_position_adjustment", -1),
]
for attribute, default in attributes:
StrategyResolver._override_attribute_helper(strategy, config,

View File

@@ -33,10 +33,14 @@ async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: Bac
if settings[setting] is not None:
btconfig[setting] = settings[setting]
# Force dry-run for backtesting
btconfig['dry_run'] = True
# Start backtesting
# Initialize backtesting object
def run_backtest():
from freqtrade.optimize.optimize_reports import generate_backtest_stats
from freqtrade.optimize.optimize_reports import (generate_backtest_stats,
store_backtest_stats)
from freqtrade.resolvers import StrategyResolver
asyncio.set_event_loop(asyncio.new_event_loop())
try:
@@ -73,13 +77,25 @@ async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: Bac
lastconfig['enable_protections'] = btconfig.get('enable_protections')
lastconfig['dry_run_wallet'] = btconfig.get('dry_run_wallet')
ApiServer._bt.abort = False
min_date, max_date = ApiServer._bt.backtest_one_strategy(
strat, ApiServer._bt_data, ApiServer._bt_timerange)
ApiServer._bt.results = {}
ApiServer._bt.load_prior_backtest()
ApiServer._bt.abort = False
if (ApiServer._bt.results and
strat.get_strategy_name() in ApiServer._bt.results['strategy']):
# When previous result hash matches - reuse that result and skip backtesting.
logger.info(f'Reusing result of previous backtest for {strat.get_strategy_name()}')
else:
min_date, max_date = ApiServer._bt.backtest_one_strategy(
strat, ApiServer._bt_data, ApiServer._bt_timerange)
ApiServer._bt.results = generate_backtest_stats(
ApiServer._bt_data, ApiServer._bt.all_results,
min_date=min_date, max_date=max_date)
if btconfig.get('export', 'none') == 'trades':
store_backtest_stats(btconfig['exportfilename'], ApiServer._bt.results)
ApiServer._bt.results = generate_backtest_stats(
ApiServer._bt_data, ApiServer._bt.all_results,
min_date=min_date, max_date=max_date)
logger.info("Backtest finished.")
except DependencyException as e:

View File

@@ -4,6 +4,7 @@ from typing import Any, Dict, List, Optional, Union
from pydantic import BaseModel
from freqtrade.constants import DATETIME_PRINT_FORMAT
from freqtrade.enums import OrderTypeValues
class Ping(BaseModel):
@@ -125,25 +126,26 @@ class Daily(BaseModel):
class UnfilledTimeout(BaseModel):
buy: int
sell: int
unit: str
buy: Optional[int]
sell: Optional[int]
unit: Optional[str]
exit_timeout_count: Optional[int]
class OrderTypes(BaseModel):
buy: str
sell: str
emergencysell: Optional[str]
forcesell: Optional[str]
forcebuy: Optional[str]
stoploss: str
buy: OrderTypeValues
sell: OrderTypeValues
emergencysell: Optional[OrderTypeValues]
forcesell: Optional[OrderTypeValues]
forcebuy: Optional[OrderTypeValues]
stoploss: OrderTypeValues
stoploss_on_exchange: bool
stoploss_on_exchange_interval: Optional[int]
class ShowConfig(BaseModel):
version: str
strategy_version: Optional[str]
api_version: float
dry_run: bool
stake_currency: str
@@ -158,7 +160,7 @@ class ShowConfig(BaseModel):
trailing_stop_positive_offset: Optional[float]
trailing_only_offset_is_reached: Optional[bool]
unfilledtimeout: UnfilledTimeout
order_types: OrderTypes
order_types: Optional[OrderTypes]
use_custom_stoploss: Optional[bool]
timeframe: Optional[str]
timeframe_ms: int
@@ -171,6 +173,8 @@ class ShowConfig(BaseModel):
bot_name: str
state: str
runmode: str
position_adjustment_enable: bool
max_entry_position_adjustment: int
class TradeSchema(BaseModel):
@@ -274,10 +278,13 @@ class Logs(BaseModel):
class ForceBuyPayload(BaseModel):
pair: str
price: Optional[float]
ordertype: Optional[OrderTypeValues]
stakeamount: Optional[float]
class ForceSellPayload(BaseModel):
tradeid: str
ordertype: Optional[OrderTypeValues]
class BlacklistPayload(BaseModel):

View File

@@ -3,7 +3,7 @@ from copy import deepcopy
from pathlib import Path
from typing import List, Optional
from fastapi import APIRouter, Depends
from fastapi import APIRouter, Depends, Query
from fastapi.exceptions import HTTPException
from freqtrade import __version__
@@ -20,7 +20,7 @@ from freqtrade.rpc.api_server.api_schemas import (AvailablePairs, Balances, Blac
Stats, StatusMsg, StrategyListResponse,
StrategyResponse, SysInfo, Version,
WhitelistResponse)
from freqtrade.rpc.api_server.deps import get_config, get_rpc, get_rpc_optional
from freqtrade.rpc.api_server.deps import get_config, get_exchange, get_rpc, get_rpc_optional
from freqtrade.rpc.rpc import RPCException
@@ -29,7 +29,10 @@ logger = logging.getLogger(__name__)
# API version
# Pre-1.1, no version was provided
# Version increments should happen in "small" steps (1.1, 1.12, ...) unless big changes happen.
API_VERSION = 1.1
# 1.11: forcebuy and forcesell accept ordertype
# 1.12: add blacklist delete endpoint
# 1.13: forcebuy supports stake_amount
API_VERSION = 1.13
# Public API, requires no auth.
router_public = APIRouter()
@@ -120,16 +123,21 @@ def edge(rpc: RPC = Depends(get_rpc)):
@router.get('/show_config', response_model=ShowConfig, tags=['info'])
def show_config(rpc: Optional[RPC] = Depends(get_rpc_optional), config=Depends(get_config)):
state = ''
strategy_version = None
if rpc:
state = rpc._freqtrade.state
resp = RPC._rpc_show_config(config, state)
strategy_version = rpc._freqtrade.strategy.version()
resp = RPC._rpc_show_config(config, state, strategy_version)
resp['api_version'] = API_VERSION
return resp
@router.post('/forcebuy', response_model=ForceBuyResponse, tags=['trading'])
def forcebuy(payload: ForceBuyPayload, rpc: RPC = Depends(get_rpc)):
trade = rpc._rpc_forcebuy(payload.pair, payload.price)
ordertype = payload.ordertype.value if payload.ordertype else None
stake_amount = payload.stakeamount if payload.stakeamount else None
trade = rpc._rpc_forcebuy(payload.pair, payload.price, ordertype, stake_amount)
if trade:
return ForceBuyResponse.parse_obj(trade.to_json())
@@ -139,7 +147,8 @@ def forcebuy(payload: ForceBuyPayload, rpc: RPC = Depends(get_rpc)):
@router.post('/forcesell', response_model=ResultMsg, tags=['trading'])
def forcesell(payload: ForceSellPayload, rpc: RPC = Depends(get_rpc)):
return rpc._rpc_forcesell(payload.tradeid)
ordertype = payload.ordertype.value if payload.ordertype else None
return rpc._rpc_forcesell(payload.tradeid, ordertype)
@router.get('/blacklist', response_model=BlacklistResponse, tags=['info', 'pairlist'])
@@ -152,6 +161,13 @@ def blacklist_post(payload: BlacklistPayload, rpc: RPC = Depends(get_rpc)):
return rpc._rpc_blacklist(payload.blacklist)
@router.delete('/blacklist', response_model=BlacklistResponse, tags=['info', 'pairlist'])
def blacklist_delete(pairs_to_delete: List[str] = Query([]), rpc: RPC = Depends(get_rpc)):
"""Provide a list of pairs to delete from the blacklist"""
return rpc._rpc_blacklist_delete(pairs_to_delete)
@router.get('/whitelist', response_model=WhitelistResponse, tags=['info', 'pairlist'])
def whitelist(rpc: RPC = Depends(get_rpc)):
return rpc._rpc_whitelist()
@@ -198,18 +214,21 @@ def reload_config(rpc: RPC = Depends(get_rpc)):
@router.get('/pair_candles', response_model=PairHistory, tags=['candle data'])
def pair_candles(pair: str, timeframe: str, limit: Optional[int], rpc: RPC = Depends(get_rpc)):
def pair_candles(
pair: str, timeframe: str, limit: Optional[int] = None, rpc: RPC = Depends(get_rpc)):
return rpc._rpc_analysed_dataframe(pair, timeframe, limit)
@router.get('/pair_history', response_model=PairHistory, tags=['candle data'])
def pair_history(pair: str, timeframe: str, timerange: str, strategy: str,
config=Depends(get_config)):
config=Depends(get_config), exchange=Depends(get_exchange)):
# The initial call to this endpoint can be slow, as it may need to initialize
# the exchange class.
config = deepcopy(config)
config.update({
'strategy': strategy,
})
return RPC._rpc_analysed_history_full(config, pair, timeframe, timerange)
return RPC._rpc_analysed_history_full(config, pair, timeframe, timerange, exchange)
@router.get('/plot_config', response_model=PlotConfig, tags=['candle data'])

View File

@@ -1,5 +1,7 @@
from typing import Any, Dict, Iterator, Optional
from fastapi import Depends
from freqtrade.persistence import Trade
from freqtrade.rpc.rpc import RPC, RPCException
@@ -28,3 +30,11 @@ def get_config() -> Dict[str, Any]:
def get_api_config() -> Dict[str, Any]:
return ApiServer._config['api_server']
def get_exchange(config=Depends(get_config)):
if not ApiServer._exchange:
from freqtrade.resolvers import ExchangeResolver
ApiServer._exchange = ExchangeResolver.load_exchange(
config['exchange']['name'], config)
return ApiServer._exchange

View File

@@ -47,7 +47,7 @@ class UvicornServer(uvicorn.Server):
else:
asyncio.set_event_loop(uvloop.new_event_loop())
try:
loop = asyncio.get_event_loop()
loop = asyncio.get_running_loop()
except RuntimeError:
# When running in a thread, we'll not have an eventloop yet.
loop = asyncio.new_event_loop()

View File

@@ -41,6 +41,8 @@ class ApiServer(RPCHandler):
_has_rpc: bool = False
_bgtask_running: bool = False
_config: Dict[str, Any] = {}
# Exchange - only available in webserver mode.
_exchange = None
def __new__(cls, *args, **kwargs):
"""

View File

@@ -7,7 +7,7 @@ import datetime
import logging
from typing import Dict, List
from cachetools.ttl import TTLCache
from cachetools import TTLCache
from pycoingecko import CoinGeckoAPI
from requests.exceptions import RequestException
@@ -77,6 +77,9 @@ class CryptoToFiatConverter:
else:
return None
found = [x for x in self._coinlistings if x['symbol'] == crypto_symbol]
if crypto_symbol == 'eth':
found = [x for x in self._coinlistings if x['id'] == 'ethereum']
if len(found) == 1:
return found[0]['id']

View File

@@ -98,7 +98,8 @@ class RPC:
self._fiat_converter = CryptoToFiatConverter()
@staticmethod
def _rpc_show_config(config, botstate: Union[State, str]) -> Dict[str, Any]:
def _rpc_show_config(config, botstate: Union[State, str],
strategy_version: Optional[str] = None) -> Dict[str, Any]:
"""
Return a dict of config options.
Explicitly does NOT return the full config to avoid leakage of sensitive
@@ -106,6 +107,7 @@ class RPC:
"""
val = {
'version': __version__,
'strategy_version': strategy_version,
'dry_run': config['dry_run'],
'stake_currency': config['stake_currency'],
'stake_currency_decimals': decimals_per_coin(config['stake_currency']),
@@ -134,7 +136,12 @@ class RPC:
'ask_strategy': config.get('ask_strategy', {}),
'bid_strategy': config.get('bid_strategy', {}),
'state': str(botstate),
'runmode': config['runmode'].value
'runmode': config['runmode'].value,
'position_adjustment_enable': config.get('position_adjustment_enable', False),
'max_entry_position_adjustment': (
config.get('max_entry_position_adjustment', -1)
if config.get('max_entry_position_adjustment') != float('inf')
else -1)
}
return val
@@ -236,19 +243,29 @@ class RPC:
profit_str += f" ({fiat_profit:.2f})"
fiat_profit_sum = fiat_profit if isnan(fiat_profit_sum) \
else fiat_profit_sum + fiat_profit
trades_list.append([
detail_trade = [
trade.id,
trade.pair + ('*' if (trade.open_order_id is not None
and trade.close_rate_requested is None) else '')
+ ('**' if (trade.close_rate_requested is not None) else ''),
+ ('**' if (trade.close_rate_requested is not None) else ''),
shorten_date(arrow.get(trade.open_date).humanize(only_distance=True)),
profit_str
])
]
if self._config.get('position_adjustment_enable', False):
max_buy_str = ''
if self._config.get('max_entry_position_adjustment', -1) > 0:
max_buy_str = f"/{self._config['max_entry_position_adjustment'] + 1}"
filled_buys = trade.nr_of_successful_buys
detail_trade.append(f"{filled_buys}{max_buy_str}")
trades_list.append(detail_trade)
profitcol = "Profit"
if self._fiat_converter:
profitcol += " (" + fiat_display_currency + ")"
columns = ['ID', 'Pair', 'Since', profitcol]
if self._config.get('position_adjustment_enable', False):
columns = ['ID', 'Pair', 'Since', profitcol, '# Buys']
else:
columns = ['ID', 'Pair', 'Since', profitcol]
return trades_list, columns, fiat_profit_sum
def _rpc_daily_profit(
@@ -590,6 +607,7 @@ class RPC:
value = self._fiat_converter.convert_amount(
total, stake_currency, fiat_display_currency) if self._fiat_converter else 0
trade_count = len(Trade.get_trades_proxy())
starting_capital_ratio = 0.0
starting_capital_ratio = (total / starting_capital) - 1 if starting_capital else 0.0
starting_cap_fiat_ratio = (value / starting_cap_fiat) - 1 if starting_cap_fiat else 0.0
@@ -606,6 +624,7 @@ class RPC:
'starting_capital_fiat': starting_cap_fiat,
'starting_capital_fiat_ratio': starting_cap_fiat_ratio,
'starting_capital_fiat_pct': round(starting_cap_fiat_ratio * 100, 2),
'trade_count': trade_count,
'note': 'Simulated balances' if self._freqtrade.config['dry_run'] else ''
}
@@ -640,7 +659,7 @@ class RPC:
return {'status': 'No more buy will occur from now. Run /reload_config to reset.'}
def _rpc_forcesell(self, trade_id: str) -> Dict[str, str]:
def _rpc_forcesell(self, trade_id: str, ordertype: Optional[str] = None) -> Dict[str, str]:
"""
Handler for forcesell <id>.
Sells the given trade at current price
@@ -664,7 +683,11 @@ class RPC:
current_rate = self._freqtrade.exchange.get_rate(
trade.pair, refresh=False, side="sell")
sell_reason = SellCheckTuple(sell_type=SellType.FORCE_SELL)
self._freqtrade.execute_trade_exit(trade, current_rate, sell_reason)
order_type = ordertype or self._freqtrade.strategy.order_types.get(
"forcesell", self._freqtrade.strategy.order_types["sell"])
self._freqtrade.execute_trade_exit(
trade, current_rate, sell_reason, ordertype=order_type)
# ---- EOF def _exec_forcesell ----
if self._freqtrade.state != State.RUNNING:
@@ -692,7 +715,8 @@ class RPC:
self._freqtrade.wallets.update()
return {'result': f'Created sell order for trade {trade_id}.'}
def _rpc_forcebuy(self, pair: str, price: Optional[float]) -> Optional[Trade]:
def _rpc_forcebuy(self, pair: str, price: Optional[float], order_type: Optional[str] = None,
stake_amount: Optional[float] = None) -> Optional[Trade]:
"""
Handler for forcebuy <asset> <price>
Buys a pair trade at the given or current price
@@ -714,13 +738,19 @@ class RPC:
# check if pair already has an open pair
trade = Trade.get_trades([Trade.is_open.is_(True), Trade.pair == pair]).first()
if trade:
raise RPCException(f'position for {pair} already open - id: {trade.id}')
if not self._freqtrade.strategy.position_adjustment_enable:
raise RPCException(f'position for {pair} already open - id: {trade.id}')
# gen stake amount
stakeamount = self._freqtrade.wallets.get_trade_stake_amount(pair)
if not stake_amount:
# gen stake amount
stake_amount = self._freqtrade.wallets.get_trade_stake_amount(pair)
# execute buy
if self._freqtrade.execute_entry(pair, stakeamount, price, forcebuy=True):
if not order_type:
order_type = self._freqtrade.strategy.order_types.get(
'forcebuy', self._freqtrade.strategy.order_types['buy'])
if self._freqtrade.execute_entry(pair, stake_amount, price,
ordertype=order_type, trade=trade):
Trade.commit()
trade = Trade.get_trades([Trade.is_open.is_(True), Trade.pair == pair]).first()
return trade
@@ -850,6 +880,20 @@ class RPC:
}
return res
def _rpc_blacklist_delete(self, delete: List[str]) -> Dict:
""" Removes pairs from currently active blacklist """
errors = {}
for pair in delete:
if pair in self._freqtrade.pairlists.blacklist:
self._freqtrade.pairlists.blacklist.remove(pair)
else:
errors[pair] = {
'error_msg': f"Pair {pair} is not in the current blacklist."
}
resp = self._rpc_blacklist()
resp['errors'] = errors
return resp
def _rpc_blacklist(self, add: List[str] = None) -> Dict:
""" Returns the currently active blacklist"""
errors = {}
@@ -960,7 +1004,7 @@ class RPC:
@staticmethod
def _rpc_analysed_history_full(config, pair: str, timeframe: str,
timerange: str) -> Dict[str, Any]:
timerange: str, exchange) -> Dict[str, Any]:
timerange_parsed = TimeRange.parse_timerange(timerange)
_data = load_data(
@@ -975,7 +1019,7 @@ class RPC:
from freqtrade.data.dataprovider import DataProvider
from freqtrade.resolvers.strategy_resolver import StrategyResolver
strategy = StrategyResolver.load_strategy(config)
strategy.dp = DataProvider(config, exchange=None, pairlists=None)
strategy.dp = DataProvider(config, exchange=exchange, pairlists=None)
df_analyzed = strategy.analyze_ticker(_data[pair], {'pair': pair})

View File

@@ -60,6 +60,10 @@ class RPCManager:
}
"""
logger.info('Sending rpc message: %s', msg)
if 'pair' in msg:
msg.update({
'base_currency': self._rpc._freqtrade.exchange.get_pair_base_currency(msg['pair'])
})
for mod in self.registered_modules:
logger.debug('Forwarding message to rpc.%s', mod.name)
try:
@@ -81,12 +85,14 @@ class RPCManager:
timeframe = config['timeframe']
exchange_name = config['exchange']['name']
strategy_name = config.get('strategy', '')
pos_adjust_enabled = 'On' if config['position_adjustment_enable'] else 'Off'
self.send_msg({
'type': RPCMessageType.STARTUP,
'status': f'*Exchange:* `{exchange_name}`\n'
f'*Stake per trade:* `{stake_amount} {stake_currency}`\n'
f'*Minimum ROI:* `{minimal_roi}`\n'
f'*{"Trailing " if trailing_stop else ""}Stoploss:* `{stoploss}`\n'
f'*Position adjustment:* `{pos_adjust_enabled}`\n'
f'*Timeframe:* `{timeframe}`\n'
f'*Strategy:* `{strategy_name}`'
})

View File

@@ -111,8 +111,9 @@ class Telegram(RPCHandler):
r'/daily$', r'/daily \d+$', r'/profit$', r'/profit \d+',
r'/stats$', r'/count$', r'/locks$', r'/balance$',
r'/stopbuy$', r'/reload_config$', r'/show_config$',
r'/logs$', r'/whitelist$', r'/blacklist$', r'/edge$',
r'/forcebuy$', r'/help$', r'/version$']
r'/logs$', r'/whitelist$', r'/blacklist$', r'/bl_delete$',
r'/weekly$', r'/weekly \d+$', r'/monthly$', r'/monthly \d+$',
r'/forcebuy$', r'/edge$', r'/help$', r'/version$']
# Create keys for generation
valid_keys_print = [k.replace('$', '') for k in valid_keys]
@@ -169,6 +170,7 @@ class Telegram(RPCHandler):
CommandHandler('stopbuy', self._stopbuy),
CommandHandler('whitelist', self._whitelist),
CommandHandler('blacklist', self._blacklist),
CommandHandler(['blacklist_delete', 'bl_delete'], self._blacklist_delete),
CommandHandler('logs', self._logs),
CommandHandler('edge', self._edge),
CommandHandler('help', self._help),
@@ -197,8 +199,8 @@ class Telegram(RPCHandler):
self._updater.start_polling(
bootstrap_retries=-1,
timeout=30,
read_latency=60,
timeout=20,
read_latency=60, # Assumed transmission latency
drop_pending_updates=True,
)
logger.info(
@@ -211,6 +213,7 @@ class Telegram(RPCHandler):
Stops all running telegram threads.
:return: None
"""
# This can take up to `timeout` from the call to `start_polling`.
self._updater.stop()
def _format_buy_msg(self, msg: Dict[str, Any]) -> str:
@@ -762,14 +765,17 @@ class Telegram(RPCHandler):
f"(< {balance_dust_level} {result['stake']}):*\n"
f"\t`Est. {result['stake']}: "
f"{round_coin_value(total_dust_balance, result['stake'], False)}`\n")
tc = result['trade_count'] > 0
stake_improve = f" `({result['starting_capital_ratio']:.2%})`" if tc else ''
fiat_val = f" `({result['starting_capital_fiat_ratio']:.2%})`" if tc else ''
output += ("\n*Estimated Value*:\n"
f"\t`{result['stake']}: "
f"{round_coin_value(result['total'], result['stake'], False)}`"
f" `({result['starting_capital_ratio']:.2%})`\n"
f"{stake_improve}\n"
f"\t`{result['symbol']}: "
f"{round_coin_value(result['value'], result['symbol'], False)}`"
f" `({result['starting_capital_fiat_ratio']:.2%})`\n")
f"{fiat_val}\n")
self._send_msg(output, reload_able=True, callback_path="update_balance",
query=update.callback_query)
except RPCException as e:
@@ -1161,22 +1167,28 @@ class Telegram(RPCHandler):
Handler for /blacklist
Shows the currently active blacklist
"""
try:
self.send_blacklist_msg(self._rpc._rpc_blacklist(context.args))
blacklist = self._rpc._rpc_blacklist(context.args)
errmsgs = []
for pair, error in blacklist['errors'].items():
errmsgs.append(f"Error adding `{pair}` to blacklist: `{error['error_msg']}`")
if errmsgs:
self._send_msg('\n'.join(errmsgs))
def send_blacklist_msg(self, blacklist: Dict):
errmsgs = []
for pair, error in blacklist['errors'].items():
errmsgs.append(f"Error adding `{pair}` to blacklist: `{error['error_msg']}`")
if errmsgs:
self._send_msg('\n'.join(errmsgs))
message = f"Blacklist contains {blacklist['length']} pairs\n"
message += f"`{', '.join(blacklist['blacklist'])}`"
message = f"Blacklist contains {blacklist['length']} pairs\n"
message += f"`{', '.join(blacklist['blacklist'])}`"
logger.debug(message)
self._send_msg(message)
except RPCException as e:
self._send_msg(str(e))
logger.debug(message)
self._send_msg(message)
@authorized_only
def _blacklist_delete(self, update: Update, context: CallbackContext) -> None:
"""
Handler for /bl_delete
Deletes pair(s) from current blacklist
"""
self.send_blacklist_msg(self._rpc._rpc_blacklist_delete(context.args or []))
@authorized_only
def _logs(self, update: Update, context: CallbackContext) -> None:
@@ -1257,6 +1269,8 @@ class Telegram(RPCHandler):
"*/whitelist:* `Show current whitelist` \n"
"*/blacklist [pair]:* `Show current blacklist, or adds one or more pairs "
"to the blacklist.` \n"
"*/blacklist_delete [pairs]| /bl_delete [pairs]:* "
"`Delete pair / pattern from blacklist. Will reset on reload_conf.` \n"
"*/reload_config:* `Reload configuration file` \n"
"*/unlock <pair|id>:* `Unlock this Pair (or this lock id if it's numeric)`\n"
@@ -1304,7 +1318,12 @@ class Telegram(RPCHandler):
:param update: message update
:return: None
"""
self._send_msg('*Version:* `{}`'.format(__version__))
strategy_version = self._rpc._freqtrade.strategy.version()
version_string = f'*Version:* `{__version__}`'
if strategy_version is not None:
version_string += f', *Strategy version: * `{strategy_version}`'
self._send_msg(version_string)
@authorized_only
def _show_config(self, update: Update, context: CallbackContext) -> None:
@@ -1328,6 +1347,14 @@ class Telegram(RPCHandler):
else:
sl_info = f"*Stoploss:* `{val['stoploss']}`\n"
if val['position_adjustment_enable']:
pa_info = (
f"*Position adjustment:* On\n"
f"*Max enter position adjustment:* `{val['max_entry_position_adjustment']}`\n"
)
else:
pa_info = "*Position adjustment:* Off\n"
self._send_msg(
f"*Mode:* `{'Dry-run' if val['dry_run'] else 'Live'}`\n"
f"*Exchange:* `{val['exchange']}`\n"
@@ -1337,6 +1364,7 @@ class Telegram(RPCHandler):
f"*Ask strategy:* ```\n{json.dumps(val['ask_strategy'])}```\n"
f"*Bid strategy:* ```\n{json.dumps(val['bid_strategy'])}```\n"
f"{sl_info}"
f"{pa_info}"
f"*Timeframe:* `{val['timeframe']}`\n"
f"*Strategy:* `{val['strategy']}`\n"
f"*Current state:* `{val['state']}`"

View File

@@ -2,6 +2,7 @@
This module manages webhook communication
"""
import logging
import time
from typing import Any, Dict
from requests import RequestException, post
@@ -28,12 +29,9 @@ class Webhook(RPCHandler):
super().__init__(rpc, config)
self._url = self._config['webhook']['url']
self._format = self._config['webhook'].get('format', 'form')
if self._format != 'form' and self._format != 'json':
raise NotImplementedError('Unknown webhook format `{}`, possible values are '
'`form` (default) and `json`'.format(self._format))
self._retries = self._config['webhook'].get('retries', 0)
self._retry_delay = self._config['webhook'].get('retry_delay', 0.1)
def cleanup(self) -> None:
"""
@@ -77,13 +75,30 @@ class Webhook(RPCHandler):
def _send_msg(self, payload: dict) -> None:
"""do the actual call to the webhook"""
try:
if self._format == 'form':
post(self._url, data=payload)
elif self._format == 'json':
post(self._url, json=payload)
else:
raise NotImplementedError('Unknown format: {}'.format(self._format))
success = False
attempts = 0
while not success and attempts <= self._retries:
if attempts:
if self._retry_delay:
time.sleep(self._retry_delay)
logger.info("Retrying webhook...")
except RequestException as exc:
logger.warning("Could not call webhook url. Exception: %s", exc)
attempts += 1
try:
if self._format == 'form':
response = post(self._url, data=payload)
elif self._format == 'json':
response = post(self._url, json=payload)
elif self._format == 'raw':
response = post(self._url, data=payload['data'],
headers={'Content-Type': 'text/plain'})
else:
raise NotImplementedError('Unknown format: {}'.format(self._format))
# Throw a RequestException if the post was not successful
response.raise_for_status()
success = True
except RequestException as exc:
logger.warning("Could not call webhook url. Exception: %s", exc)

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