Compare commits

..

70 Commits

Author SHA1 Message Date
Matthias
2db5cc177d Merge pull request #7029 from freqtrade/new_release
New release 2022.6
2022-07-03 19:42:24 +02:00
Matthias
c1d4078518 Version bump to 2022.6 2022-07-03 15:04:38 +02:00
Matthias
d25ec6d0b8 Merge branch 'stable' into new_release 2022-07-03 15:04:16 +02:00
Matthias
07aa372e2a Ensure bot_loop_start is called in hyperopt, too
closes #7001
2022-07-03 14:10:59 +02:00
Matthias
c5e6520fee Reorder methods in freqtradebot 2022-07-03 13:35:26 +02:00
Matthias
f2fdc21374 Only use exit_tag if exit_type i exit_signal
closes #7027
2022-07-03 11:07:05 +02:00
Matthias
906c7b92fe Add enhance testcase to show problematic exit_reason behavior 2022-07-03 11:05:15 +02:00
Matthias
df8c9fc4e1 Merge pull request #7005 from freqtrade/dependabot/pip/develop/uvicorn-0.18.1
Bump uvicorn from 0.17.6 to 0.18.1
2022-07-03 07:52:09 +02:00
Matthias
3c1380fbc6 Merge pull request #7009 from freqtrade/dependabot/pip/develop/types-python-dateutil-2.8.18
Bump types-python-dateutil from 2.8.17 to 2.8.18
2022-06-28 08:02:33 +02:00
Matthias
86f4077024 update dateutil precommit 2022-06-28 07:37:54 +02:00
dependabot[bot]
f2bc35e058 Bump types-python-dateutil from 2.8.17 to 2.8.18
Bumps [types-python-dateutil](https://github.com/python/typeshed) from 2.8.17 to 2.8.18.
- [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-06-27 20:06:56 +00:00
Matthias
0a5225695a Merge pull request #7016 from freqtrade/dependabot/pip/develop/types-tabulate-0.8.11
Bump types-tabulate from 0.8.9 to 0.8.11
2022-06-27 22:05:45 +02:00
Matthias
74471e41db update tabulate precommit types 2022-06-27 18:23:00 +02:00
dependabot[bot]
8b1798522c Bump types-tabulate from 0.8.9 to 0.8.11
Bumps [types-tabulate](https://github.com/python/typeshed) from 0.8.9 to 0.8.11.
- [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-06-27 13:18:58 +00:00
Matthias
7de7425e24 Merge pull request #7007 from freqtrade/dependabot/pip/develop/time-machine-2.7.1
Bump time-machine from 2.7.0 to 2.7.1
2022-06-27 15:18:23 +02:00
Matthias
37dff8dc82 Merge pull request #7018 from freqtrade/dependabot/pip/develop/types-requests-2.28.0
Bump types-requests from 2.27.30 to 2.28.0
2022-06-27 15:17:57 +02:00
Matthias
0c69a08863 update requests precommit 2022-06-27 12:09:27 +02:00
dependabot[bot]
f6e058a327 Bump types-requests from 2.27.30 to 2.28.0
Bumps [types-requests](https://github.com/python/typeshed) from 2.27.30 to 2.28.0.
- [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-06-27 09:59:19 +00:00
dependabot[bot]
d60127a6d8 Bump time-machine from 2.7.0 to 2.7.1
Bumps [time-machine](https://github.com/adamchainz/time-machine) from 2.7.0 to 2.7.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.7.0...2.7.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>
2022-06-27 09:59:07 +00:00
Matthias
11a8151653 Merge pull request #7012 from freqtrade/dependabot/pip/develop/types-cachetools-5.2.1
Bump types-cachetools from 5.0.2 to 5.2.1
2022-06-27 11:54:43 +02:00
Matthias
e3abaaa1b7 Merge pull request #7019 from freqtrade/dependabot/pip/develop/pandas-1.4.3
Bump pandas from 1.4.2 to 1.4.3
2022-06-27 11:54:06 +02:00
dependabot[bot]
82ef97af7e Bump pandas from 1.4.2 to 1.4.3
Bumps [pandas](https://github.com/pandas-dev/pandas) from 1.4.2 to 1.4.3.
- [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.4.2...v1.4.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-27 07:44:33 +00:00
Matthias
74fdda6846 Merge pull request #7017 from freqtrade/dependabot/pip/develop/ccxt-1.89.14
Bump ccxt from 1.88.15 to 1.89.14
2022-06-27 09:43:29 +02:00
Matthias
9eaf0400fa Merge pull request #7020 from freqtrade/dependabot/pip/develop/orjson-3.7.3
Bump orjson from 3.7.2 to 3.7.3
2022-06-27 09:10:46 +02:00
Matthias
01185ab483 update cachetools precommit 2022-06-27 07:59:26 +02:00
Matthias
8405bf767b Merge pull request #7006 from freqtrade/dependabot/pip/develop/pytest-mock-3.8.1
Bump pytest-mock from 3.7.0 to 3.8.1
2022-06-27 07:43:55 +02:00
dependabot[bot]
9a9d1a8974 Bump orjson from 3.7.2 to 3.7.3
Bumps [orjson](https://github.com/ijl/orjson) from 3.7.2 to 3.7.3.
- [Release notes](https://github.com/ijl/orjson/releases)
- [Changelog](https://github.com/ijl/orjson/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ijl/orjson/compare/3.7.2...3.7.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-27 05:39:04 +00:00
dependabot[bot]
0ef2c812db Bump ccxt from 1.88.15 to 1.89.14
Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.88.15 to 1.89.14.
- [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.88.15...1.89.14)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-27 05:38:31 +00:00
Matthias
85d1b433bc Merge pull request #7013 from freqtrade/dependabot/pip/develop/tabulate-0.8.10
Bump tabulate from 0.8.9 to 0.8.10
2022-06-27 07:38:20 +02:00
Matthias
d8f616cf35 Merge pull request #7011 from freqtrade/dependabot/pip/develop/plotly-5.9.0
Bump plotly from 5.8.2 to 5.9.0
2022-06-27 07:37:33 +02:00
Matthias
870c25c81f Merge pull request #7010 from freqtrade/dependabot/pip/develop/sqlalchemy-1.4.39
Bump sqlalchemy from 1.4.37 to 1.4.39
2022-06-27 07:37:00 +02:00
Matthias
fb3bc189b5 Merge pull request #7008 from freqtrade/dependabot/pip/develop/mkdocs-material-8.3.8
Bump mkdocs-material from 8.3.6 to 8.3.8
2022-06-27 07:36:08 +02:00
dependabot[bot]
6510c8d330 Bump tabulate from 0.8.9 to 0.8.10
Bumps [tabulate](https://github.com/astanin/python-tabulate) from 0.8.9 to 0.8.10.
- [Release notes](https://github.com/astanin/python-tabulate/releases)
- [Changelog](https://github.com/astanin/python-tabulate/blob/master/CHANGELOG)
- [Commits](https://github.com/astanin/python-tabulate/compare/v0.8.9...v0.8.10)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-27 03:03:00 +00:00
dependabot[bot]
efee148e43 Bump types-cachetools from 5.0.2 to 5.2.1
Bumps [types-cachetools](https://github.com/python/typeshed) from 5.0.2 to 5.2.1.
- [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-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-27 03:02:53 +00:00
dependabot[bot]
8b7dc031f7 Bump plotly from 5.8.2 to 5.9.0
Bumps [plotly](https://github.com/plotly/plotly.py) from 5.8.2 to 5.9.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.8.2...v5.9.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-27 03:02:51 +00:00
dependabot[bot]
963f38a690 Bump sqlalchemy from 1.4.37 to 1.4.39
Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 1.4.37 to 1.4.39.
- [Release notes](https://github.com/sqlalchemy/sqlalchemy/releases)
- [Changelog](https://github.com/sqlalchemy/sqlalchemy/blob/main/CHANGES.rst)
- [Commits](https://github.com/sqlalchemy/sqlalchemy/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-27 03:02:46 +00:00
dependabot[bot]
45db2347dc Bump mkdocs-material from 8.3.6 to 8.3.8
Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 8.3.6 to 8.3.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.3.6...8.3.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-06-27 03:02:29 +00:00
dependabot[bot]
4840c7d2fd Bump pytest-mock from 3.7.0 to 3.8.1
Bumps [pytest-mock](https://github.com/pytest-dev/pytest-mock) from 3.7.0 to 3.8.1.
- [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.7.0...v3.8.1)

---
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-06-27 03:02:16 +00:00
dependabot[bot]
92dbb0d366 Bump uvicorn from 0.17.6 to 0.18.1
Bumps [uvicorn](https://github.com/encode/uvicorn) from 0.17.6 to 0.18.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.6...0.18.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-27 03:02:07 +00:00
Matthias
b5d0bc997d Clarify stoploss behavior when not defining offset
closes #6828
2022-06-24 17:25:33 +02:00
Matthias
ca88ea50c5 Merge pull request #6859 from mkavinkumar1/get
Removed None in dict.get()
2022-06-23 21:45:13 +02:00
Matthias
2b07d34611 Revert several undesired changes 2022-06-23 20:47:51 +02:00
Matthias
8bf0bf10c5 Merge branch 'develop' into pr/SmartManoj/6859 2022-06-23 20:43:35 +02:00
Matthias
c57db0a330 Version bump 2022.5.1 2022-06-01 06:34:28 +02:00
Matthias
f5087a82dc Merge branch 'stable' into new_release 2022-06-01 06:33:42 +02:00
Matthias
eed0d67005 Merge pull request #6893 from freqtrade/new_release
New release 2022.5
2022-05-28 13:46:24 +02:00
Matthias
a1d54f5ae0 Version bump 2022.5 2022-05-28 09:49:58 +02:00
Matthias
a4a7c6536d Merge branch 'stable' into new_release 2022-05-28 09:49:46 +02:00
மனோஜ்குமார் பழனிச்சாமி
2b61aa282a Removed None in dict.get()
https://stackoverflow.com/a/12631641

Extra Changes: freqtrade\freqtradebot.py:70
freqtrade\plugins\pairlistmanager.py:31
2022-05-18 03:41:10 +05:30
Matthias
340a97d1df Merge pull request #6811 from DJCrashdummy/patch-1
corrected minor "typo" in formatting
2022-05-10 19:16:40 +02:00
DJCrashdummy
fab197edf2 corrected minor "typo" in formatting 2022-05-10 10:33:04 +00:00
Matthias
851c5dad30 Version bump 2022.4.2 2022-05-03 20:37:29 +02:00
Matthias
5b76ae452f Fix fee handling for futures trades 2022-05-03 20:35:30 +02:00
Matthias
2c750fdb09 Reduce no stake amount verbosity
closes #6768
2022-05-03 20:35:22 +02:00
Matthias
e7f5252074 Version bump 2022.4.1 2022-05-01 16:49:11 +02:00
Matthias
dfbd1c34c4 Merge pull request #6755 from freqtrade/new_release
New release 2022.4
2022-05-01 14:51:39 +02:00
Matthias
7615c4e904 Version bump 2022.4 2022-05-01 11:19:32 +02:00
Matthias
e9b78bf3ae Merge branch 'stable' into new_release 2022-05-01 11:19:17 +02:00
Matthias
2e397a88e1 Merge pull request #6592 from freqtrade/new_release
New release 2022.3
2022-03-27 15:51:58 +02:00
Matthias
fe6c62e144 Version bump 2022.3 2022-03-27 15:27:16 +02:00
Matthias
f0db721f05 Merge branch 'stable' into new_release 2022-03-27 15:09:06 +02:00
Matthias
4d8d30ea39 Version bump to 2022.2.2 2022-03-21 06:34:33 +01:00
Matthias
e90e3cead0 Map usdt fiat to correct coingecko fiat 2022-03-21 06:34:20 +01:00
Matthias
a568548192 Merge pull request #6464 from freqtrade/new_release
New release 2022.2.1
2022-02-26 08:57:42 +01:00
Matthias
f9d10a7fad Version bump 2022.2.1 2022-02-26 08:35:50 +01:00
Matthias
cbc2b00ee6 Merge branch 'stable' into new_release 2022-02-26 08:35:31 +01:00
Matthias
8f7b857ae9 Merge pull request #6459 from freqtrade/new_release
New release 2022.2
2022-02-25 15:14:27 +01:00
Matthias
e88b022cd4 Version bump 2022.2 2022-02-25 12:07:09 +01:00
Matthias
bfb738f69f Merge branch 'stable' into new_release 2022-02-25 12:06:11 +01:00
Matthias
00dd8e76ee Merge pull request #6416 from froggleston/patch-2
Update windows_installation.md
2022-02-25 11:44:40 +01:00
28 changed files with 124 additions and 176 deletions

View File

@@ -13,11 +13,11 @@ repos:
- id: mypy
exclude: build_helpers
additional_dependencies:
- types-cachetools==5.0.2
- types-cachetools==5.2.1
- types-filelock==3.2.7
- types-requests==2.27.30
- types-tabulate==0.8.9
- types-python-dateutil==2.8.17
- types-requests==2.28.0
- types-tabulate==0.8.11
- types-python-dateutil==2.8.18
# stages: [push]
- repo: https://github.com/pycqa/isort

View File

@@ -20,7 +20,9 @@ All profit calculations of Freqtrade include fees. For Backtesting / Hyperopt /
## Bot execution logic
Starting freqtrade in dry-run or live mode (using `freqtrade trade`) will start the bot and start the bot iteration loop.
By default, loop runs every few seconds (`internals.process_throttle_secs`) and does roughly the following in the following sequence:
This will also run the `bot_start()` callback.
By default, the bot loop runs every few seconds (`internals.process_throttle_secs`) and performs the following actions:
* Fetch open trades from persistence.
* Calculate current list of tradable pairs.
@@ -54,6 +56,7 @@ This loop will be repeated again and again until the bot is stopped.
[backtesting](backtesting.md) or [hyperopt](hyperopt.md) do only part of the above logic, since most of the trading operations are fully simulated.
* Load historic data for configured pairlist.
* Calls `bot_start()` once.
* Calls `bot_loop_start()` once.
* Calculate indicators (calls `populate_indicators()` once per pair).
* Calculate entry / exit signals (calls `populate_entry_trend()` and `populate_exit_trend()` once per pair).

View File

@@ -1,5 +1,5 @@
mkdocs==1.3.0
mkdocs-material==8.3.6
mkdocs-material==8.3.8
mdx_truly_sane_lists==1.2
pymdown-extensions==9.5
jinja2==3.1.2

View File

@@ -130,7 +130,7 @@ In summary: The stoploss will be adjusted to be always be -10% of the highest ob
### Trailing stop loss, custom positive loss
It is also possible to have a default stop loss, when you are in the red with your buy (buy - fee), but once you hit positive result the system will utilize a new stop loss, which can have a different value.
You could also have a default stop loss when you are in the red with your buy (buy - fee), but once you hit a positive result (or an offset you define) the system will utilize a new stop loss, which can have a different value.
For example, your default stop loss is -10%, but once you have more than 0% profit (example 0.1%) a different trailing stoploss will be used.
!!! Note
@@ -142,6 +142,8 @@ Both values require `trailing_stop` to be set to true and `trailing_stop_positiv
stoploss = -0.10
trailing_stop = True
trailing_stop_positive = 0.02
trailing_stop_positive_offset = 0.0
trailing_only_offset_is_reached = False # Default - not necessary for this example
```
For example, simplified math:
@@ -156,11 +158,31 @@ For example, simplified math:
The 0.02 would translate to a -2% stop loss.
Before this, `stoploss` is used for the trailing stoploss.
!!! Tip "Use an offset to change your stoploss"
Use `trailing_stop_positive_offset` to ensure that your new trailing stoploss will be in profit by setting `trailing_stop_positive_offset` higher than `trailing_stop_positive`. Your first new stoploss value will then already have locked in profits.
Example with simplified math:
``` python
stoploss = -0.10
trailing_stop = True
trailing_stop_positive = 0.02
trailing_stop_positive_offset = 0.03
```
* the bot buys an asset at a price of 100$
* the stop loss is defined at -10%, so the stop loss would get triggered once the asset drops below 90$
* assuming the asset now increases to 102$
* the stoploss will now be at 91.8$ - 10% below the highest observed rate
* assuming the asset now increases to 103.5$ (above the offset configured)
* the stop loss will now be -2% of 103$ = 101.42$
* now the asset drops in value to 102\$, the stop loss will still be 101.42$ and would trigger once price breaks below 101.42$
### Trailing stop loss only once the trade has reached a certain offset
It is also possible to use a static stoploss until the offset is reached, and then trail the trade to take profits once the market turns.
You can also keep a static stoploss until the offset is reached, and then trail the trade to take profits once the market turns.
If `"trailing_only_offset_is_reached": true` then the trailing stoploss is only activated once the offset is reached. Until then, the stoploss remains at the configured `stoploss`.
If `trailing_only_offset_is_reached = True` then the trailing stoploss is only activated once the offset is reached. Until then, the stoploss remains at the configured `stoploss`.
This option can be used with or without `trailing_stop_positive`, but uses `trailing_stop_positive_offset` as offset.
``` python
@@ -203,7 +225,6 @@ If price moves 1% - you've lost 10$ of your own capital - therfore stoploss will
Make sure to be aware of this, and avoid using too tight stoploss (at 10x leverage, 10% risk may be too little to allow the trade to "breath" a little).
## Changing stoploss on open trades
A stoploss on an open trade can be changed by changing the value in the configuration or strategy and use the `/reload_config` command (alternatively, completely stopping and restarting the bot also works).

View File

@@ -1,5 +1,5 @@
""" Freqtrade bot """
__version__ = 'develop'
__version__ = '2022.6'
if 'dev' in __version__:
try:

View File

@@ -24,7 +24,7 @@ def start_hyperopt_list(args: Dict[str, Any]) -> None:
print_colorized = config.get('print_colorized', False)
print_json = config.get('print_json', False)
export_csv = config.get('export_csv', None)
export_csv = config.get('export_csv')
no_details = config.get('hyperopt_list_no_details', False)
no_header = False

View File

@@ -129,7 +129,7 @@ class Configuration:
# Default to in-memory db for dry_run if not specified
config['db_url'] = constants.DEFAULT_DB_DRYRUN_URL
else:
if not config.get('db_url', None):
if not config.get('db_url'):
config['db_url'] = constants.DEFAULT_DB_PROD_URL
logger.info('Dry run is disabled')
@@ -182,7 +182,7 @@ class Configuration:
config['user_data_dir'] = create_userdata_dir(config['user_data_dir'], create_dir=False)
logger.info('Using user-data directory: %s ...', config['user_data_dir'])
config.update({'datadir': create_datadir(config, self.args.get('datadir', None))})
config.update({'datadir': create_datadir(config, self.args.get('datadir'))})
logger.info('Using data directory: %s ...', config.get('datadir'))
if self.args.get('exportfilename'):
@@ -221,7 +221,7 @@ class Configuration:
if config.get('max_open_trades') == -1:
config['max_open_trades'] = float('inf')
if self.args.get('stake_amount', None):
if self.args.get('stake_amount'):
# Convert explicitly to float to support CLI argument for both unlimited and value
try:
self.args['stake_amount'] = float(self.args['stake_amount'])
@@ -474,7 +474,7 @@ class Configuration:
configuration instead of the content)
"""
if (argname in self.args and self.args[argname] is not None
and self.args[argname] is not False):
and self.args[argname] is not False):
config.update({argname: self.args[argname]})
if logfun:

View File

@@ -387,7 +387,7 @@ class Exchange:
and market.get('base', None) is not None
and (self.precisionMode != TICK_SIZE
# Too low precision will falsify calculations
or market.get('precision', {}).get('price', None) > 1e-11)
or market.get('precision', {}).get('price') > 1e-11)
and ((self.trading_mode == TradingMode.SPOT and self.market_is_spot(market))
or (self.trading_mode == TradingMode.MARGIN and self.market_is_margin(market))
or (self.trading_mode == TradingMode.FUTURES and self.market_is_future(market)))
@@ -537,7 +537,7 @@ class Exchange:
# The internal info array is different for each particular market,
# its contents depend on the exchange.
# It can also be a string or similar ... so we need to verify that first.
elif (isinstance(self.markets[pair].get('info', None), dict)
elif (isinstance(self.markets[pair].get('info'), dict)
and self.markets[pair].get('info', {}).get('prohibitedIn', False)):
# Warn users about restricted pairs in whitelist.
# We cannot determine reliably if Users are affected.

View File

@@ -17,13 +17,12 @@ from freqtrade.constants import BuySell, LongShort
from freqtrade.data.converter import order_book_to_dataframe
from freqtrade.data.dataprovider import DataProvider
from freqtrade.edge import Edge
from freqtrade.enums import (ExitCheckTuple, ExitType, MarginMode, RPCMessageType, RunMode,
SignalDirection, State, TradingMode)
from freqtrade.enums import (ExitCheckTuple, ExitType, RPCMessageType, RunMode, SignalDirection,
State, TradingMode)
from freqtrade.exceptions import (DependencyException, ExchangeError, InsufficientFundsError,
InvalidOrderException, PricingError)
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds
from freqtrade.exchange.exchange import timeframe_to_next_date
from freqtrade.maintenance_margin import MaintenanceMargin
from freqtrade.misc import safe_value_fallback, safe_value_fallback2
from freqtrade.mixins import LoggingMixin
from freqtrade.persistence import Order, PairLocks, Trade, cleanup_db, init_db
@@ -68,7 +67,7 @@ class FreqtradeBot(LoggingMixin):
self.exchange = ExchangeResolver.load_exchange(self.config['exchange']['name'], self.config)
init_db(self.config.get('db_url', None))
init_db(self.config['db_url'])
self.wallets = Wallets(self.config, self.exchange)
@@ -105,11 +104,7 @@ class FreqtradeBot(LoggingMixin):
LoggingMixin.__init__(self, logger, timeframe_to_seconds(self.strategy.timeframe))
self.trading_mode: TradingMode = self.config.get('trading_mode', TradingMode.SPOT)
self.margin_mode: MarginMode = (
MarginMode(config.get('margin_mode'))
if config.get('margin_mode')
else MarginMode.NONE
)
self._schedule = Scheduler()
if self.trading_mode == TradingMode.FUTURES:
@@ -130,16 +125,6 @@ class FreqtradeBot(LoggingMixin):
# Initialize protections AFTER bot start - otherwise parameters are not loaded.
self.protections = ProtectionManager(self.config, self.strategy.protections)
# Start calculating maintenance margin if on cross margin
# TODO-lev: finish the below...
if self.margin_mode == MarginMode.CROSS:
self.maintenance_margin = MaintenanceMargin(
exchange_name=self.exchange.name,
trading_mode=self.trading_mode)
self.maintenance_margin.run()
def notify_status(self, msg: str) -> None:
"""
Public method for users of this class (worker, etc.) to send notifications
@@ -663,7 +648,7 @@ class FreqtradeBot(LoggingMixin):
)
order_obj = Order.parse_from_ccxt_object(order, pair, side)
order_id = order['id']
order_status = order.get('status', None)
order_status = order.get('status')
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
@@ -736,10 +721,6 @@ class FreqtradeBot(LoggingMixin):
trade.orders.append(order_obj)
trade.recalc_trade_from_orders()
if self.margin_mode == MarginMode.CROSS:
self.maintenance_margin.add_new_trade(trade)
Trade.query.session.add(trade)
Trade.commit()
@@ -978,6 +959,29 @@ class FreqtradeBot(LoggingMixin):
logger.debug(f'Found no {exit_signal_type} signal for %s.', trade)
return False
def _check_and_execute_exit(self, trade: Trade, exit_rate: float,
enter: bool, exit_: bool, exit_tag: Optional[str]) -> bool:
"""
Check and execute trade exit
"""
exits: List[ExitCheckTuple] = self.strategy.should_exit(
trade,
exit_rate,
datetime.now(timezone.utc),
enter=enter,
exit_=exit_,
force_stoploss=self.edge.stoploss(trade.pair) if self.edge else 0
)
for should_exit in exits:
if should_exit.exit_flag:
exit_tag1 = exit_tag if should_exit.exit_type == ExitType.EXIT_SIGNAL else None
logger.info(f'Exit for {trade.pair} detected. Reason: {should_exit.exit_type}'
f'{f" Tag: {exit_tag1}" if exit_tag1 is not None else ""}')
exited = self.execute_trade_exit(trade, exit_rate, should_exit, exit_tag=exit_tag1)
if exited:
return True
return False
def create_stoploss_order(self, trade: Trade, stop_price: float) -> bool:
"""
Abstracts creating stoploss orders from the logic.
@@ -1129,28 +1133,6 @@ class FreqtradeBot(LoggingMixin):
logger.warning(f"Could not create trailing stoploss order "
f"for pair {trade.pair}.")
def _check_and_execute_exit(self, trade: Trade, exit_rate: float,
enter: bool, exit_: bool, exit_tag: Optional[str]) -> bool:
"""
Check and execute trade exit
"""
exits: List[ExitCheckTuple] = self.strategy.should_exit(
trade,
exit_rate,
datetime.now(timezone.utc),
enter=enter,
exit_=exit_,
force_stoploss=self.edge.stoploss(trade.pair) if self.edge else 0
)
for should_exit in exits:
if should_exit.exit_flag:
logger.info(f'Exit for {trade.pair} detected. Reason: {should_exit.exit_type}'
f'{f" Tag: {exit_tag}" if exit_tag is not None else ""}')
exited = self.execute_trade_exit(trade, exit_rate, should_exit, exit_tag=exit_tag)
if exited:
return True
return False
def manage_open_orders(self) -> None:
"""
Management of open orders on exchange. Unfilled orders might be cancelled if timeout
@@ -1520,21 +1502,10 @@ class FreqtradeBot(LoggingMixin):
# 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()
self._remove_maintenance_trade(trade)
return True
def _remove_maintenance_trade(self, trade: Trade):
"""
Removes a trade from the maintenance margin object
:param trade: The trade to remove from the maintenance margin
"""
if self.margin_mode == MarginMode.CROSS:
self.maintenance_margin.remove_trade(trade)
def _notify_exit(self, trade: Trade, order_type: str, fill: bool = False) -> None:
"""
Sends rpc notification when a sell occurred.
@@ -1571,7 +1542,7 @@ class FreqtradeBot(LoggingMixin):
'open_date': trade.open_date,
'close_date': trade.close_date or datetime.utcnow(),
'stake_currency': self.config['stake_currency'],
'fiat_currency': self.config.get('fiat_display_currency', None),
'fiat_currency': self.config.get('fiat_display_currency'),
}
if 'fiat_display_currency' in self.config:
@@ -1682,7 +1653,7 @@ class FreqtradeBot(LoggingMixin):
if order['status'] in constants.NON_OPEN_EXCHANGE_STATES:
# If a entry order was closed, force update on stoploss on exchange
if order.get('side', None) == trade.entry_side:
if order.get('side') == trade.entry_side:
trade = self.cancel_stoploss_on_exchange(trade)
# TODO: Margin will need to use interest_rate as well.
# interest_rate = self.exchange.get_interest_rate()

View File

@@ -1,52 +0,0 @@
from typing import List
from freqtrade.enums import TradingMode
from freqtrade.leverage import liquidation_price
from freqtrade.persistence import Trade
class MaintenanceMargin:
trades: List[Trade]
exchange_name: str
trading_mode: TradingMode
@property
def margin_level(self):
# This is the current value of all assets,
# and if you pass below liq_level, you are liquidated
# TODO-lev: Add args to formula
return liquidation_price(
trading_mode=self.trading_mode,
exchange_name=self.exchange_name
)
@property
def liq_level(self): # This may be a constant value and may not need a function
# TODO-lev: The is the value that you are liquidated at
return # If constant, would need to be recalculated after each new trade
def __init__(self, exchange_name: str, trading_mode: TradingMode):
self.exchange_name = exchange_name
self.trading_mode = trading_mode
return
def add_new_trade(self, trade):
self.trades.append(trade)
def remove_trade(self, trade):
self.trades.remove(trade)
# ? def update_trade_pric(self):
def sell_all(self):
# TODO-lev
return
def run(self):
# TODO-lev: implement a thread that constantly updates with every price change,
# TODO-lev: must update at least every few seconds or so
# while true:
# if self.margin_level <= self.liq_level:
# self.sell_all()
return

View File

@@ -87,7 +87,7 @@ class Backtesting:
self.exchange = ExchangeResolver.load_exchange(self._exchange_name, self.config)
self.dataprovider = DataProvider(self.config, self.exchange)
if self.config.get('strategy_list', None):
if self.config.get('strategy_list'):
for strat in list(self.config['strategy_list']):
stratconf = deepcopy(self.config)
stratconf['strategy'] = strat
@@ -189,6 +189,7 @@ class Backtesting:
self.strategy.order_types['stoploss_on_exchange'] = False
self.strategy.ft_bot_start()
strategy_safe_wrapper(self.strategy.bot_loop_start, supress_error=True)()
def _load_protections(self, strategy: IStrategy):
if self.config.get('enable_protections', False):
@@ -1140,8 +1141,6 @@ class Backtesting:
backtest_start_time = datetime.now(timezone.utc)
self._set_strategy(strat)
strategy_safe_wrapper(self.strategy.bot_loop_start, supress_error=True)()
# Use max_open_trades in backtesting, except --disable-max-market-positions is set
if self.config.get('use_max_market_positions', True):
# Must come from strategy config, as the strategy may modify this setting.

View File

@@ -455,7 +455,7 @@ class Hyperopt:
return self.opt.ask(n_points=n_points), [False for _ in range(n_points)]
def start(self) -> None:
self.random_state = self._set_random_state(self.config.get('hyperopt_random_state', None))
self.random_state = self._set_random_state(self.config.get('hyperopt_random_state'))
logger.info(f"Using optimizer random state: {self.random_state}")
self.hyperopt_table_header = -1
# Initialize spaces ...

View File

@@ -127,14 +127,14 @@ class HyperoptTools():
'only_profitable': config.get('hyperopt_list_profitable', False),
'filter_min_trades': config.get('hyperopt_list_min_trades', 0),
'filter_max_trades': config.get('hyperopt_list_max_trades', 0),
'filter_min_avg_time': config.get('hyperopt_list_min_avg_time', None),
'filter_max_avg_time': config.get('hyperopt_list_max_avg_time', None),
'filter_min_avg_profit': config.get('hyperopt_list_min_avg_profit', None),
'filter_max_avg_profit': config.get('hyperopt_list_max_avg_profit', None),
'filter_min_total_profit': config.get('hyperopt_list_min_total_profit', None),
'filter_max_total_profit': config.get('hyperopt_list_max_total_profit', None),
'filter_min_objective': config.get('hyperopt_list_min_objective', None),
'filter_max_objective': config.get('hyperopt_list_max_objective', None),
'filter_min_avg_time': config.get('hyperopt_list_min_avg_time'),
'filter_max_avg_time': config.get('hyperopt_list_max_avg_time'),
'filter_min_avg_profit': config.get('hyperopt_list_min_avg_profit'),
'filter_max_avg_profit': config.get('hyperopt_list_max_avg_profit'),
'filter_min_total_profit': config.get('hyperopt_list_min_total_profit'),
'filter_max_total_profit': config.get('hyperopt_list_max_total_profit'),
'filter_min_objective': config.get('hyperopt_list_min_objective'),
'filter_max_objective': config.get('hyperopt_list_max_objective'),
}
if not HyperoptTools._test_hyperopt_results_exist(results_file):
# No file found.

View File

@@ -30,7 +30,7 @@ class AgeFilter(IPairList):
self._symbolsCheckFailed = PeriodicCache(maxsize=1000, ttl=86_400)
self._min_days_listed = pairlistconfig.get('min_days_listed', 10)
self._max_days_listed = pairlistconfig.get('max_days_listed', None)
self._max_days_listed = pairlistconfig.get('max_days_listed')
candle_limit = exchange.ohlcv_candle_limit('1d', self._config['candle_type_def'])
if self._min_days_listed < 1:

View File

@@ -21,7 +21,7 @@ class PerformanceFilter(IPairList):
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
self._minutes = pairlistconfig.get('minutes', 0)
self._min_profit = pairlistconfig.get('min_profit', None)
self._min_profit = pairlistconfig.get('min_profit')
@property
def needstickers(self) -> bool:

View File

@@ -27,7 +27,7 @@ class RangeStabilityFilter(IPairList):
self._days = pairlistconfig.get('lookback_days', 10)
self._min_rate_of_change = pairlistconfig.get('min_rate_of_change', 0.01)
self._max_rate_of_change = pairlistconfig.get('max_rate_of_change', None)
self._max_rate_of_change = pairlistconfig.get('max_rate_of_change')
self._refresh_period = pairlistconfig.get('refresh_period', 1440)
self._def_candletype = self._config['candle_type_def']

View File

@@ -28,7 +28,7 @@ class PairListManager(LoggingMixin):
self._blacklist = self._config['exchange'].get('pair_blacklist', [])
self._pairlist_handlers: List[IPairList] = []
self._tickers_needed = False
for pairlist_handler_config in self._config.get('pairlists', None):
for pairlist_handler_config in self._config.get('pairlists', []):
pairlist_handler = PairListResolver.load_pairlist(
pairlist_handler_config['method'],
exchange=exchange,

View File

@@ -282,7 +282,7 @@ def get_strategy(strategy: str, config=Depends(get_config)):
def list_available_pairs(timeframe: Optional[str] = None, stake_currency: Optional[str] = None,
candletype: Optional[CandleType] = None, config=Depends(get_config)):
dh = get_datahandler(config['datadir'], config.get('dataformat_ohlcv', None))
dh = get_datahandler(config['datadir'], config.get('dataformat_ohlcv'))
trading_mode: TradingMode = config.get('trading_mode', TradingMode.SPOT)
pair_interval = dh.ohlcv_get_available_data(config['datadir'], trading_mode)

View File

@@ -97,7 +97,7 @@ class RPC:
"""
self._freqtrade = freqtrade
self._config: Dict[str, Any] = freqtrade.config
if self._config.get('fiat_display_currency', None):
if self._config.get('fiat_display_currency'):
self._fiat_converter = CryptoToFiatConverter()
@staticmethod
@@ -566,7 +566,7 @@ class RPC:
else:
try:
pair = self._freqtrade.exchange.get_valid_pair_combination(coin, stake_currency)
rate = tickers.get(pair, {}).get('last', None)
rate = tickers.get(pair, {}).get('last')
if rate:
if pair.startswith(stake_currency) and not pair.endswith(stake_currency):
rate = 1.0 / rate

View File

@@ -259,7 +259,7 @@ class Telegram(RPCHandler):
f" {entry_side['entered'] if is_fill else entry_side['enter']} {msg['pair']}"
f" (#{msg['trade_id']})\n"
)
message += f"*Enter Tag:* `{msg['enter_tag']}`\n" if msg.get('enter_tag', None) else ""
message += f"*Enter Tag:* `{msg['enter_tag']}`\n" if msg.get('enter_tag') else ""
message += f"*Amount:* `{msg['amount']:.8f}`\n"
if msg.get('leverage') and msg.get('leverage', 1.0) != 1.0:
message += f"*Leverage:* `{msg['leverage']}`\n"
@@ -272,7 +272,7 @@ class Telegram(RPCHandler):
message += f"*Total:* `({round_coin_value(msg['stake_amount'], msg['stake_currency'])}"
if msg.get('fiat_currency', None):
if msg.get('fiat_currency'):
message += f", {round_coin_value(msg['stake_amount_fiat'], msg['fiat_currency'])}"
message += ")`"
@@ -288,7 +288,7 @@ class Telegram(RPCHandler):
msg['enter_tag'] = msg['enter_tag'] if "enter_tag" in msg.keys() else None
msg['emoji'] = self._get_sell_emoji(msg)
msg['leverage_text'] = (f"*Leverage:* `{msg['leverage']:.1f}`\n"
if msg.get('leverage', None) and msg.get('leverage', 1.0) != 1.0
if msg.get('leverage') and msg.get('leverage', 1.0) != 1.0
else "")
# Check if all sell properties are available.

View File

@@ -45,21 +45,21 @@ class Webhook(RPCHandler):
try:
whconfig = self._config['webhook']
if msg['type'] in [RPCMessageType.ENTRY]:
valuedict = whconfig.get('webhookentry', None)
valuedict = whconfig.get('webhookentry')
elif msg['type'] in [RPCMessageType.ENTRY_CANCEL]:
valuedict = whconfig.get('webhookentrycancel', None)
valuedict = whconfig.get('webhookentrycancel')
elif msg['type'] in [RPCMessageType.ENTRY_FILL]:
valuedict = whconfig.get('webhookentryfill', None)
valuedict = whconfig.get('webhookentryfill')
elif msg['type'] == RPCMessageType.EXIT:
valuedict = whconfig.get('webhookexit', None)
valuedict = whconfig.get('webhookexit')
elif msg['type'] == RPCMessageType.EXIT_FILL:
valuedict = whconfig.get('webhookexitfill', None)
valuedict = whconfig.get('webhookexitfill')
elif msg['type'] == RPCMessageType.EXIT_CANCEL:
valuedict = whconfig.get('webhookexitcancel', None)
valuedict = whconfig.get('webhookexitcancel')
elif msg['type'] in (RPCMessageType.STATUS,
RPCMessageType.STARTUP,
RPCMessageType.WARNING):
valuedict = whconfig.get('webhookstatus', None)
valuedict = whconfig.get('webhookstatus')
else:
raise NotImplementedError('Unknown message type: {}'.format(msg['type']))
if not valuedict:

View File

@@ -131,9 +131,9 @@ class Wallets:
if isinstance(balances[currency], dict):
self._wallets[currency] = Wallet(
currency,
balances[currency].get('free', None),
balances[currency].get('used', None),
balances[currency].get('total', None)
balances[currency].get('free'),
balances[currency].get('used'),
balances[currency].get('total')
)
# Remove currencies no longer in get_balances output
for currency in deepcopy(self._wallets):

View File

@@ -12,18 +12,18 @@ pre-commit==2.19.0
pytest==7.1.2
pytest-asyncio==0.18.3
pytest-cov==3.0.0
pytest-mock==3.7.0
pytest-mock==3.8.1
pytest-random-order==1.0.4
isort==5.10.1
# For datetime mocking
time-machine==2.7.0
time-machine==2.7.1
# Convert jupyter notebooks to markdown documents
nbconvert==6.5.0
# mypy types
types-cachetools==5.0.2
types-cachetools==5.2.1
types-filelock==3.2.7
types-requests==2.27.30
types-tabulate==0.8.9
types-python-dateutil==2.8.17
types-requests==2.28.0
types-tabulate==0.8.11
types-python-dateutil==2.8.18

View File

@@ -1,4 +1,4 @@
# Include all requirements to run the bot.
-r requirements.txt
plotly==5.8.2
plotly==5.9.0

View File

@@ -1,12 +1,12 @@
numpy==1.23.0
pandas==1.4.2
pandas==1.4.3
pandas-ta==0.3.14b
ccxt==1.88.15
ccxt==1.89.14
# Pin cryptography for now due to rust build errors with piwheels
cryptography==37.0.2
aiohttp==3.8.1
SQLAlchemy==1.4.37
SQLAlchemy==1.4.39
python-telegram-bot==13.12
arrow==1.2.2
cachetools==4.2.2
@@ -15,7 +15,7 @@ urllib3==1.26.9
jsonschema==4.6.0
TA-Lib==0.4.24
technical==1.3.0
tabulate==0.8.9
tabulate==0.8.10
pycoingecko==2.2.0
jinja2==3.1.2
tables==3.7.0
@@ -28,14 +28,14 @@ py_find_1st==1.1.5
# Load ticker files 30% faster
python-rapidjson==1.6
# Properly format api responses
orjson==3.7.2
orjson==3.7.3
# Notify systemd
sdnotify==0.3.2
# API Server
fastapi==0.78.0
uvicorn==0.17.6
uvicorn==0.18.1
pyjwt==2.4.0
aiofiles==0.8.0
psutil==5.9.1

View File

@@ -861,6 +861,7 @@ def test_in_strategy_auto_hyperopt(mocker, hyperopt_conf, tmpdir, fee) -> None:
hyperopt.backtesting.exchange.get_max_leverage = MagicMock(return_value=1.0)
assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto)
assert isinstance(hyperopt.backtesting.strategy.buy_rsi, IntParameter)
assert hyperopt.backtesting.strategy.bot_loop_started is True
assert hyperopt.backtesting.strategy.buy_rsi.in_space is True
assert hyperopt.backtesting.strategy.buy_rsi.value == 35

View File

@@ -44,6 +44,11 @@ class HyperoptableStrategy(StrategyTestV2):
})
return prot
bot_loop_started = False
def bot_loop_start(self):
self.bot_loop_started = True
def bot_start(self, **kwargs) -> None:
"""
Parameters can also be defined here ...

View File

@@ -3951,9 +3951,9 @@ def test_ignore_roi_if_entry_signal(default_conf_usdt, limit_order, limit_order_
# Test if entry-signal is absent (should sell due to roi = true)
if is_short:
patch_get_signal(freqtrade, enter_long=False, exit_short=False)
patch_get_signal(freqtrade, enter_long=False, exit_short=False, exit_tag='something')
else:
patch_get_signal(freqtrade, enter_long=False, exit_long=False)
patch_get_signal(freqtrade, enter_long=False, exit_long=False, exit_tag='something')
assert freqtrade.handle_trade(trade) is True
assert trade.exit_reason == ExitType.ROI.value