From d4272269007b3f1d7027ca7b4f5e1c99980666b1 Mon Sep 17 00:00:00 2001 From: Matteo Manzi <33622899+matteoettam09@users.noreply.github.com> Date: Tue, 18 Oct 2022 19:15:20 +0200 Subject: [PATCH 01/49] Update docker_quickstart.md --- docs/docker_quickstart.md | 62 +++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/docs/docker_quickstart.md b/docs/docker_quickstart.md index 84c1d596a..6b48a7877 100644 --- a/docs/docker_quickstart.md +++ b/docs/docker_quickstart.md @@ -10,14 +10,14 @@ Start by downloading and installing Docker CE for your platform: * [Windows](https://docs.docker.com/docker-for-windows/install/) * [Linux](https://docs.docker.com/install/) -To simplify running freqtrade, [`docker-compose`](https://docs.docker.com/compose/install/) should be installed and available to follow the below [docker quick start guide](#docker-quick-start). +To simplify running freqtrade, [`docker compose`](https://docs.docker.com/compose/install/) should be installed and available to follow the below [docker quick start guide](#docker-quick-start). -## Freqtrade with docker-compose +## Freqtrade with docker -Freqtrade provides an official Docker image on [Dockerhub](https://hub.docker.com/r/freqtradeorg/freqtrade/), as well as a [docker-compose file](https://github.com/freqtrade/freqtrade/blob/stable/docker-compose.yml) ready for usage. +Freqtrade provides an official Docker image on [Dockerhub](https://hub.docker.com/r/freqtradeorg/freqtrade/), as well as a [docker compose file](https://github.com/freqtrade/freqtrade/blob/stable/docker-compose.yml) ready for usage. !!! Note - - The following section assumes that `docker` and `docker-compose` are installed and available to the logged in user. + - The following section assumes that `docker` is installed and available to the logged in user. - All below commands use relative directories and will have to be executed from the directory containing the `docker-compose.yml` file. ### Docker quick start @@ -31,13 +31,13 @@ cd ft_userdata/ curl https://raw.githubusercontent.com/freqtrade/freqtrade/stable/docker-compose.yml -o docker-compose.yml # Pull the freqtrade image -docker-compose pull +docker compose pull # Create user directory structure -docker-compose run --rm freqtrade create-userdir --userdir user_data +docker compose run --rm freqtrade create-userdir --userdir user_data # Create configuration - Requires answering interactive questions -docker-compose run --rm freqtrade new-config --config user_data/config.json +docker compose run --rm freqtrade new-config --config user_data/config.json ``` The above snippet creates a new directory called `ft_userdata`, downloads the latest compose file and pulls the freqtrade image. @@ -64,7 +64,7 @@ The `SampleStrategy` is run by default. Once this is done, you're ready to launch the bot in trading mode (Dry-run or Live-trading, depending on your answer to the corresponding question you made above). ``` bash -docker-compose up -d +docker compose up -d ``` !!! Warning "Default configuration" @@ -84,27 +84,27 @@ You can now access the UI by typing localhost:8080 in your browser. #### Monitoring the bot -You can check for running instances with `docker-compose ps`. +You can check for running instances with `docker compose ps`. This should list the service `freqtrade` as `running`. If that's not the case, best check the logs (see next point). -#### Docker-compose logs +#### Docker compose logs Logs will be written to: `user_data/logs/freqtrade.log`. -You can also check the latest log with the command `docker-compose logs -f`. +You can also check the latest log with the command `docker compose logs -f`. #### Database The database will be located at: `user_data/tradesv3.sqlite` -#### Updating freqtrade with docker-compose +#### Updating freqtrade with docker -Updating freqtrade when using `docker-compose` is as simple as running the following 2 commands: +Updating freqtrade when using `docker` is as simple as running the following 2 commands: ``` bash # Download the latest image -docker-compose pull +docker compose pull # Restart the image -docker-compose up -d +docker compose up -d ``` This will first pull the latest image, and will then restart the container with the just pulled version. @@ -116,43 +116,43 @@ This will first pull the latest image, and will then restart the container with Advanced users may edit the docker-compose file further to include all possible options or arguments. -All freqtrade arguments will be available by running `docker-compose run --rm freqtrade `. +All freqtrade arguments will be available by running `docker compose run --rm freqtrade `. -!!! Warning "`docker-compose` for trade commands" - Trade commands (`freqtrade trade <...>`) should not be ran via `docker-compose run` - but should use `docker-compose up -d` instead. +!!! Warning "`docker compose` for trade commands" + Trade commands (`freqtrade trade <...>`) should not be ran via `docker compose run` - but should use `docker compose up -d` instead. This makes sure that the container is properly started (including port forwardings) and will make sure that the container will restart after a system reboot. If you intend to use freqUI, please also ensure to adjust the [configuration accordingly](rest-api.md#configuration-with-docker), otherwise the UI will not be available. -!!! Note "`docker-compose run --rm`" +!!! 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. +??? Note "Using docker without docker" + "`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 +#### Example: Download data with docker 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. ``` bash -docker-compose run --rm freqtrade download-data --pairs ETH/BTC --exchange binance --days 5 -t 1h +docker compose run --rm freqtrade download-data --pairs ETH/BTC --exchange binance --days 5 -t 1h ``` Head over to the [Data Downloading Documentation](data-download.md) for more details on downloading data. -#### Example: Backtest with docker-compose +#### Example: Backtest with docker Run backtesting in docker-containers for SampleStrategy and specified timerange of historical data, on 5m timeframe: ``` bash -docker-compose run --rm freqtrade backtesting --config user_data/config.json --strategy SampleStrategy --timerange 20190801-20191001 -i 5m +docker compose run --rm freqtrade backtesting --config user_data/config.json --strategy SampleStrategy --timerange 20190801-20191001 -i 5m ``` Head over to the [Backtesting Documentation](backtesting.md) to learn more. -### Additional dependencies with docker-compose +### Additional dependencies with docker If your strategy requires dependencies not included in the default image - it will be necessary to build the image on your host. For this, please create a Dockerfile containing installation steps for the additional dependencies (have a look at [docker/Dockerfile.custom](https://github.com/freqtrade/freqtrade/blob/develop/docker/Dockerfile.custom) for an example). @@ -166,15 +166,15 @@ You'll then also need to modify the `docker-compose.yml` file and uncomment the dockerfile: "./Dockerfile." ``` -You can then run `docker-compose build --pull` to build the docker image, and run it using the commands described above. +You can then run `docker compose build --pull` to build the docker image, and run it using the commands described above. -### Plotting with docker-compose +### Plotting with docker Commands `freqtrade plot-profit` and `freqtrade plot-dataframe` ([Documentation](plotting.md)) are available by changing the image to `*_plot` in your docker-compose.yml file. You can then use these commands as follows: ``` bash -docker-compose run --rm freqtrade plot-dataframe --strategy AwesomeStrategy -p BTC/ETH --timerange=20180801-20180805 +docker compose run --rm freqtrade plot-dataframe --strategy AwesomeStrategy -p BTC/ETH --timerange=20180801-20180805 ``` The output will be stored in the `user_data/plot` directory, and can be opened with any modern browser. @@ -185,7 +185,7 @@ Freqtrade provides a docker-compose file which starts up a jupyter lab server. You can run this server using the following command: ``` bash -docker-compose -f docker/docker-compose-jupyter.yml up +docker compose -f docker/docker-compose-jupyter.yml up ``` This will create a docker-container running jupyter lab, which will be accessible using `https://127.0.0.1:8888/lab`. @@ -194,7 +194,7 @@ Please use the link that's printed in the console after startup for simplified l Since part of this image is built on your machine, it is recommended to rebuild the image from time to time to keep freqtrade (and dependencies) up-to-date. ``` bash -docker-compose -f docker/docker-compose-jupyter.yml build --no-cache +docker compose -f docker/docker-compose-jupyter.yml build --no-cache ``` ## Troubleshooting From abcbe7a42153740208f7ac4186fdecb0bc45f3f4 Mon Sep 17 00:00:00 2001 From: Matteo Manzi <33622899+matteoettam09@users.noreply.github.com> Date: Tue, 18 Oct 2022 19:15:59 +0200 Subject: [PATCH 02/49] Update updating.md --- docs/updating.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/updating.md b/docs/updating.md index 893bc846e..1e5dc8ffe 100644 --- a/docs/updating.md +++ b/docs/updating.md @@ -6,14 +6,14 @@ To update your freqtrade installation, please use one of the below methods, corr Breaking changes / changed behavior will be documented in the changelog that is posted alongside every release. For the develop branch, please follow PR's to avoid being surprised by changes. -## docker-compose +## docker !!! Note "Legacy installations using the `master` image" We're switching from master to stable for the release Images - please adjust your docker-file and replace `freqtradeorg/freqtrade:master` with `freqtradeorg/freqtrade:stable` ``` bash -docker-compose pull -docker-compose up -d +docker compose pull +docker compose up -d ``` ## Installation via setup script From 11d6d0be9e25b1fac1be5cde8addbedeaedf129f Mon Sep 17 00:00:00 2001 From: Matteo Manzi <33622899+matteoettam09@users.noreply.github.com> Date: Tue, 18 Oct 2022 19:22:07 +0200 Subject: [PATCH 03/49] Update sql_cheatsheet.md --- docs/sql_cheatsheet.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sql_cheatsheet.md b/docs/sql_cheatsheet.md index c42cb5575..67c081d4c 100644 --- a/docs/sql_cheatsheet.md +++ b/docs/sql_cheatsheet.md @@ -13,12 +13,12 @@ Feel free to use a visual Database editor like SqliteBrowser if you feel more co sudo apt-get install sqlite3 ``` -### Using sqlite3 via docker-compose +### Using sqlite3 via docker The freqtrade docker image does contain sqlite3, so you can edit the database without having to install anything on the host system. ``` bash -docker-compose exec freqtrade /bin/bash +docker compose exec freqtrade /bin/bash sqlite3 .sqlite ``` From fe3d99b5685ad681347a448681f045c05e7f541e Mon Sep 17 00:00:00 2001 From: Matteo Manzi <33622899+matteoettam09@users.noreply.github.com> Date: Tue, 18 Oct 2022 19:22:49 +0200 Subject: [PATCH 04/49] Update feature_request.md --- .github/ISSUE_TEMPLATE/feature_request.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index a18915462..db335bf09 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -18,7 +18,7 @@ Have you search for this feature before requesting it? It's highly likely that a * Operating system: ____ * Python Version: _____ (`python -V`) * CCXT version: _____ (`pip freeze | grep ccxt`) - * Freqtrade Version: ____ (`freqtrade -V` or `docker-compose run --rm freqtrade -V` for Freqtrade running in docker) + * Freqtrade Version: ____ (`freqtrade -V` or `docker compose run --rm freqtrade -V` for Freqtrade running in docker) ## Describe the enhancement From 67850d92af1c81dfc139b7045ac33f4e2056e9fb Mon Sep 17 00:00:00 2001 From: Matteo Manzi <33622899+matteoettam09@users.noreply.github.com> Date: Tue, 18 Oct 2022 19:24:46 +0200 Subject: [PATCH 05/49] Update question.md --- .github/ISSUE_TEMPLATE/question.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md index 4b02e5f19..9283f0e4f 100644 --- a/.github/ISSUE_TEMPLATE/question.md +++ b/.github/ISSUE_TEMPLATE/question.md @@ -18,7 +18,7 @@ Please do not use the question template to report bugs or to request new feature * Operating system: ____ * Python Version: _____ (`python -V`) * CCXT version: _____ (`pip freeze | grep ccxt`) - * Freqtrade Version: ____ (`freqtrade -V` or `docker-compose run --rm freqtrade -V` for Freqtrade running in docker) + * Freqtrade Version: ____ (`freqtrade -V` or `docker compose run --rm freqtrade -V` for Freqtrade running in docker) ## Your question From 35cc6aa966cc509cc9dc528c813928be4e6f3757 Mon Sep 17 00:00:00 2001 From: Matteo Manzi <33622899+matteoettam09@users.noreply.github.com> Date: Tue, 18 Oct 2022 19:25:37 +0200 Subject: [PATCH 06/49] Update data-analysis.md --- docs/data-analysis.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/data-analysis.md b/docs/data-analysis.md index 926ed3eae..5f01ae38f 100644 --- a/docs/data-analysis.md +++ b/docs/data-analysis.md @@ -5,7 +5,7 @@ You can analyze the results of backtests and trading history easily using Jupyte ## Quick start with docker Freqtrade provides a docker-compose file which starts up a jupyter lab server. -You can run this server using the following command: `docker-compose -f docker/docker-compose-jupyter.yml up` +You can run this server using the following command: `docker compose -f docker/docker-compose-jupyter.yml up` This will create a dockercontainer running jupyter lab, which will be accessible using `https://127.0.0.1:8888/lab`. Please use the link that's printed in the console after startup for simplified login. From 8c39b37223ec3ea449bf277957794f19a5bd9c58 Mon Sep 17 00:00:00 2001 From: Matteo Manzi <33622899+matteoettam09@users.noreply.github.com> Date: Tue, 18 Oct 2022 19:26:09 +0200 Subject: [PATCH 07/49] Update bug_report.md --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 54c9eab50..8637c0d68 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -20,7 +20,7 @@ Please do not use bug reports to request new features. * Operating system: ____ * Python Version: _____ (`python -V`) * CCXT version: _____ (`pip freeze | grep ccxt`) - * Freqtrade Version: ____ (`freqtrade -V` or `docker-compose run --rm freqtrade -V` for Freqtrade running in docker) + * Freqtrade Version: ____ (`freqtrade -V` or `docker compose run --rm freqtrade -V` for Freqtrade running in docker) Note: All issues other than enhancement requests will be closed without further comment if the above template is deleted or not filled out. From 51b410ac1a333e5ae744e68be13b5dca8b3a1748 Mon Sep 17 00:00:00 2001 From: Matteo Manzi <33622899+matteoettam09@users.noreply.github.com> Date: Tue, 18 Oct 2022 19:28:29 +0200 Subject: [PATCH 08/49] Update utils.md --- docs/utils.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/utils.md b/docs/utils.md index ee8793159..2f4604323 100644 --- a/docs/utils.md +++ b/docs/utils.md @@ -654,7 +654,7 @@ Common arguments: You can also use webserver mode via docker. Starting a one-off container requires the configuration of the port explicitly, as ports are not exposed by default. -You can use `docker-compose run --rm -p 127.0.0.1:8080:8080 freqtrade webserver` to start a one-off container that'll be removed once you stop it. This assumes that port 8080 is still available and no other bot is running on that port. +You can use `docker compose run --rm -p 127.0.0.1:8080:8080 freqtrade webserver` to start a one-off container that'll be removed once you stop it. This assumes that port 8080 is still available and no other bot is running on that port. Alternatively, you can reconfigure the docker-compose file to have the command updated: @@ -664,7 +664,7 @@ Alternatively, you can reconfigure the docker-compose file to have the command u --config /freqtrade/user_data/config.json ``` -You can now use `docker-compose up` to start the webserver. +You can now use `docker compose up` to start the webserver. This assumes that the configuration has a webserver enabled and configured for docker (listening port = `0.0.0.0`). !!! Tip From eb81cccedebb79bd363b4d8fb48b49b6700e9749 Mon Sep 17 00:00:00 2001 From: k <> Date: Thu, 1 Dec 2022 16:37:24 +0800 Subject: [PATCH 09/49] add download-data command change directory fix relative config path --- .../templates/strategy_analysis_example.ipynb | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/freqtrade/templates/strategy_analysis_example.ipynb b/freqtrade/templates/strategy_analysis_example.ipynb index 5fb14ab2f..f7d68b41c 100644 --- a/freqtrade/templates/strategy_analysis_example.ipynb +++ b/freqtrade/templates/strategy_analysis_example.ipynb @@ -7,7 +7,7 @@ "# Strategy analysis example\n", "\n", "Debugging a strategy can be time-consuming. Freqtrade offers helper functions to visualize raw data.\n", - "The following assumes you work with SampleStrategy, data for 5m timeframe from Binance and have downloaded them into the data directory in the default location." + "The following assumes you work with SampleStrategy, data for 5m timeframe from Binance and have downloaded them into the data directory in the default location, using command like `freqtrade download-data --exchange binance --trading-mod spot --pairs BTC/USDT --days 7 -t 5m`." ] }, { @@ -23,7 +23,21 @@ "metadata": {}, "outputs": [], "source": [ + "import os\n", "from pathlib import Path\n", + "\n", + "# Change current working directory from `somedir/freqtrade/user_data/notebooks` to project root `somedir/freqtrade`, so relative paths remain consistent.\n", + "if not Path(\"LICENSE\").is_file():\n", + " os.chdir(\"../../\")\n", + "print(Path.cwd())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ "from freqtrade.configuration import Configuration\n", "\n", "# Customize these according to your needs.\n", @@ -31,14 +45,14 @@ "# Initialize empty configuration object\n", "config = Configuration.from_files([])\n", "# Optionally (recommended), use existing configuration file\n", - "# config = Configuration.from_files([\"config.json\"])\n", + "# config = Configuration.from_files([\"user_data/config.json\"])\n", "\n", "# Define some constants\n", "config[\"timeframe\"] = \"5m\"\n", "# Name of the strategy class\n", "config[\"strategy\"] = \"SampleStrategy\"\n", "# Location of the data\n", - "data_location = config['datadir']\n", + "data_location = config[\"datadir\"]\n", "# Pair to analyze - Only use one pair here\n", "pair = \"BTC/USDT\"" ] @@ -56,7 +70,7 @@ "candles = load_pair_history(datadir=data_location,\n", " timeframe=config[\"timeframe\"],\n", " pair=pair,\n", - " data_format = \"hdf5\",\n", + " data_format = \"json\",\n", " candle_type=CandleType.SPOT,\n", " )\n", "\n", @@ -365,7 +379,7 @@ "metadata": { "file_extension": ".py", "kernelspec": { - "display_name": "Python 3.9.7 64-bit ('trade_397')", + "display_name": "Python 3.11.0 64-bit", "language": "python", "name": "python3" }, @@ -379,7 +393,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.7" + "version": "3.11.0" }, "mimetype": "text/x-python", "name": "python", @@ -430,7 +444,7 @@ "version": 3, "vscode": { "interpreter": { - "hash": "675f32a300d6d26767470181ad0b11dd4676bcce7ed1dd2ffe2fbc370c95fc7c" + "hash": "945ba00099661281427cc644a7000ee9eeea5ce6ad3bf937939d3d384b8f3881" } } }, From e734b399296cd88e77d6962281f13f49a9a9b016 Mon Sep 17 00:00:00 2001 From: Emre Date: Mon, 5 Dec 2022 14:54:42 +0300 Subject: [PATCH 10/49] Make model_training_parameters optional --- config_examples/config_freqai.example.json | 4 +--- docs/freqai-configuration.md | 11 ++++------- freqtrade/constants.py | 5 ++--- .../freqai/prediction_models/ReinforcementLearner.py | 2 +- 4 files changed, 8 insertions(+), 14 deletions(-) diff --git a/config_examples/config_freqai.example.json b/config_examples/config_freqai.example.json index 5e564a1fc..dfd54b3d9 100644 --- a/config_examples/config_freqai.example.json +++ b/config_examples/config_freqai.example.json @@ -79,9 +79,7 @@ "test_size": 0.33, "random_state": 1 }, - "model_training_parameters": { - "n_estimators": 1000 - } + "model_training_parameters": {} }, "bot_name": "", "force_entry_enable": true, diff --git a/docs/freqai-configuration.md b/docs/freqai-configuration.md index 5c3bbf90c..10f5838c9 100644 --- a/docs/freqai-configuration.md +++ b/docs/freqai-configuration.md @@ -26,10 +26,7 @@ FreqAI is configured through the typical [Freqtrade config file](configuration.m }, "data_split_parameters" : { "test_size": 0.25 - }, - "model_training_parameters" : { - "n_estimators": 100 - }, + } } ``` @@ -118,7 +115,7 @@ The FreqAI strategy requires including the following lines of code in the standa ``` -Notice how the `populate_any_indicators()` is where [features](freqai-feature-engineering.md#feature-engineering) and labels/targets are added. A full example strategy is available in `templates/FreqaiExampleStrategy.py`. +Notice how the `populate_any_indicators()` is where [features](freqai-feature-engineering.md#feature-engineering) and labels/targets are added. A full example strategy is available in `templates/FreqaiExampleStrategy.py`. Notice also the location of the labels under `if set_generalized_indicators:` at the bottom of the example. This is where single features and labels/targets should be added to the feature set to avoid duplication of them from various configuration parameters that multiply the feature set, such as `include_timeframes`. @@ -182,7 +179,7 @@ The `startup_candle_count` in the FreqAI strategy needs to be set up in the same ## Creating a dynamic target threshold -Deciding when to enter or exit a trade can be done in a dynamic way to reflect current market conditions. FreqAI allows you to return additional information from the training of a model (more info [here](freqai-feature-engineering.md#returning-additional-info-from-training)). For example, the `&*_std/mean` return values describe the statistical distribution of the target/label *during the most recent training*. Comparing a given prediction to these values allows you to know the rarity of the prediction. In `templates/FreqaiExampleStrategy.py`, the `target_roi` and `sell_roi` are defined to be 1.25 z-scores away from the mean which causes predictions that are closer to the mean to be filtered out. +Deciding when to enter or exit a trade can be done in a dynamic way to reflect current market conditions. FreqAI allows you to return additional information from the training of a model (more info [here](freqai-feature-engineering.md#returning-additional-info-from-training)). For example, the `&*_std/mean` return values describe the statistical distribution of the target/label *during the most recent training*. Comparing a given prediction to these values allows you to know the rarity of the prediction. In `templates/FreqaiExampleStrategy.py`, the `target_roi` and `sell_roi` are defined to be 1.25 z-scores away from the mean which causes predictions that are closer to the mean to be filtered out. ```python dataframe["target_roi"] = dataframe["&-s_close_mean"] + dataframe["&-s_close_std"] * 1.25 @@ -230,7 +227,7 @@ If you want to predict multiple targets, you need to define multiple labels usin #### Classifiers -If you are using a classifier, you need to specify a target that has discrete values. FreqAI includes a variety of classifiers, such as the `CatboostClassifier` via the flag `--freqaimodel CatboostClassifier`. If you elects to use a classifier, the classes need to be set using strings. For example, if you want to predict if the price 100 candles into the future goes up or down you would set +If you are using a classifier, you need to specify a target that has discrete values. FreqAI includes a variety of classifiers, such as the `CatboostClassifier` via the flag `--freqaimodel CatboostClassifier`. If you elects to use a classifier, the classes need to be set using strings. For example, if you want to predict if the price 100 candles into the future goes up or down you would set ```python df['&s-up_or_down'] = np.where( df["close"].shift(-100) > df["close"], 'up', 'down') diff --git a/freqtrade/constants.py b/freqtrade/constants.py index d869b89f6..ca1be1d6a 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -608,9 +608,8 @@ CONF_SCHEMA = { "backtest_period_days", "identifier", "feature_parameters", - "data_split_parameters", - "model_training_parameters" - ] + "data_split_parameters" + ] }, }, } diff --git a/freqtrade/freqai/prediction_models/ReinforcementLearner.py b/freqtrade/freqai/prediction_models/ReinforcementLearner.py index 61b01e21b..39901859c 100644 --- a/freqtrade/freqai/prediction_models/ReinforcementLearner.py +++ b/freqtrade/freqai/prediction_models/ReinforcementLearner.py @@ -61,7 +61,7 @@ class ReinforcementLearner(BaseReinforcementLearningModel): model = self.MODELCLASS(self.policy_type, self.train_env, policy_kwargs=policy_kwargs, tensorboard_log=Path( dk.full_path / "tensorboard" / dk.pair.split('/')[0]), - **self.freqai_info['model_training_parameters'] + **self.freqai_info.get('model_training_parameters', {}) ) else: logger.info('Continual training activated - starting training from previously ' From 730fba956b55b67555bf5766532faf7ddc8ba856 Mon Sep 17 00:00:00 2001 From: Emre Date: Mon, 5 Dec 2022 16:16:17 +0300 Subject: [PATCH 11/49] Ensure base tf included in include_timeframes --- freqtrade/freqai/utils.py | 20 ++++++++++++++++++++ freqtrade/strategy/interface.py | 4 +++- tests/freqai/test_freqai_interface.py | 17 ++++++++++++++++- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/freqtrade/freqai/utils.py b/freqtrade/freqai/utils.py index 806e3ca15..7a9d3df76 100644 --- a/freqtrade/freqai/utils.py +++ b/freqtrade/freqai/utils.py @@ -233,3 +233,23 @@ def get_timerange_backtest_live_models(config: Config) -> str: dd = FreqaiDataDrawer(models_path, config) timerange = dd.get_timerange_from_live_historic_predictions() return timerange.timerange_str + + +def ensure_base_tf_in_include_timeframes(config: Config) -> Config: + """ + Ensure that the base timeframe is included in the include_timeframes list + :param config: Configuration dictionary + + :return config: Configuration dictionary + """ + feature_parameters = config.get('freqai', {}).get('feature_parameters', {}) + include_timeframes = feature_parameters.get('include_timeframes', []) + + if config['timeframe'] in include_timeframes: + return config + + include_timeframes = [config['timeframe']] + include_timeframes + config.get('freqai', {}).get('feature_parameters', {}) \ + .update({**feature_parameters, 'include_timeframes': include_timeframes}) + + return config diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 681c5fcbb..48a03e216 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -148,9 +148,11 @@ class IStrategy(ABC, HyperStrategyMixin): def load_freqAI_model(self) -> None: if self.config.get('freqai', {}).get('enabled', False): # Import here to avoid importing this if freqAI is disabled - from freqtrade.freqai.utils import download_all_data_for_training + from freqtrade.freqai.utils import (download_all_data_for_training, + ensure_base_tf_in_include_timeframes) from freqtrade.resolvers.freqaimodel_resolver import FreqaiModelResolver self.freqai = FreqaiModelResolver.load_freqaimodel(self.config) + self.config = ensure_base_tf_in_include_timeframes(self.config) self.freqai_info = self.config["freqai"] # download the desired data in dry/live diff --git a/tests/freqai/test_freqai_interface.py b/tests/freqai/test_freqai_interface.py index c53137093..6f01c66f6 100644 --- a/tests/freqai/test_freqai_interface.py +++ b/tests/freqai/test_freqai_interface.py @@ -9,7 +9,9 @@ from freqtrade.configuration import TimeRange from freqtrade.data.dataprovider import DataProvider from freqtrade.enums import RunMode from freqtrade.freqai.data_kitchen import FreqaiDataKitchen -from freqtrade.freqai.utils import download_all_data_for_training, get_required_data_timerange +from freqtrade.freqai.utils import (download_all_data_for_training, + ensure_base_tf_in_include_timeframes, + get_required_data_timerange) from freqtrade.optimize.backtesting import Backtesting from freqtrade.persistence import Trade from freqtrade.plugins.pairlistmanager import PairListManager @@ -528,6 +530,19 @@ def test_start_set_train_queue(mocker, freqai_conf, caplog): ) +def test_base_tf_in_include_timeframes(mocker, freqai_conf): + freqai_conf['timeframe'] = '5m' + freqai_conf['freqai']['feature_parameters'].update({ + 'include_timeframes': ['15m', '1h'] + }) + updated_conf = ensure_base_tf_in_include_timeframes(freqai_conf) + assert updated_conf['freqai']['feature_parameters']['include_timeframes'] == [ + '5m', '15m', '1h', + ] + last_conf = ensure_base_tf_in_include_timeframes(updated_conf) + assert last_conf == updated_conf + + def test_get_required_data_timerange(mocker, freqai_conf): time_range = get_required_data_timerange(freqai_conf) assert (time_range.stopts - time_range.startts) == 177300 From 189fa64052b0261a272765ed799941e8e001ef4a Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 5 Dec 2022 18:14:16 +0100 Subject: [PATCH 12/49] Add more dynamic to directory change --- docs/strategy_analysis_example.md | 33 +++++++++++++-- .../templates/strategy_analysis_example.ipynb | 40 ++++++++++++++----- 2 files changed, 59 insertions(+), 14 deletions(-) diff --git a/docs/strategy_analysis_example.md b/docs/strategy_analysis_example.md index bae4a9108..e3d2870e2 100644 --- a/docs/strategy_analysis_example.md +++ b/docs/strategy_analysis_example.md @@ -2,12 +2,37 @@ Debugging a strategy can be time-consuming. Freqtrade offers helper functions to visualize raw data. The following assumes you work with SampleStrategy, data for 5m timeframe from Binance and have downloaded them into the data directory in the default location. +Please follow the [documentation](https://www.freqtrade.io/en/stable/data-download/) for more details. ## Setup +### Change Working directory to repository root + ```python +import os from pathlib import Path + +# Change directory +# Modify this cell to insure that the output shows the correct path. +# Define all paths relative to the project root shown in the cell output +project_root = "somedir/freqtrade" +i=0 +try: + os.chdirdir(project_root) + assert Path('LICENSE').is_file() +except: + while i<4 and (not Path('LICENSE').is_file()): + os.chdir(Path(Path.cwd(), '../')) + i+=1 + project_root = Path.cwd() +print(Path.cwd()) +``` + +### Configure Freqtrade environment + + +```python from freqtrade.configuration import Configuration # Customize these according to your needs. @@ -15,14 +40,14 @@ from freqtrade.configuration import Configuration # Initialize empty configuration object config = Configuration.from_files([]) # Optionally (recommended), use existing configuration file -# config = Configuration.from_files(["config.json"]) +# config = Configuration.from_files(["user_data/config.json"]) # Define some constants config["timeframe"] = "5m" # Name of the strategy class config["strategy"] = "SampleStrategy" # Location of the data -data_location = config['datadir'] +data_location = config["datadir"] # Pair to analyze - Only use one pair here pair = "BTC/USDT" ``` @@ -36,12 +61,12 @@ from freqtrade.enums import CandleType candles = load_pair_history(datadir=data_location, timeframe=config["timeframe"], pair=pair, - data_format = "hdf5", + data_format = "json", # Make sure to update this to your data candle_type=CandleType.SPOT, ) # Confirm success -print("Loaded " + str(len(candles)) + f" rows of data for {pair} from {data_location}") +print(f"Loaded {len(candles)} rows of data for {pair} from {data_location}") candles.head() ``` diff --git a/freqtrade/templates/strategy_analysis_example.ipynb b/freqtrade/templates/strategy_analysis_example.ipynb index f7d68b41c..dfbcedb72 100644 --- a/freqtrade/templates/strategy_analysis_example.ipynb +++ b/freqtrade/templates/strategy_analysis_example.ipynb @@ -7,14 +7,17 @@ "# Strategy analysis example\n", "\n", "Debugging a strategy can be time-consuming. Freqtrade offers helper functions to visualize raw data.\n", - "The following assumes you work with SampleStrategy, data for 5m timeframe from Binance and have downloaded them into the data directory in the default location, using command like `freqtrade download-data --exchange binance --trading-mod spot --pairs BTC/USDT --days 7 -t 5m`." + "The following assumes you work with SampleStrategy, data for 5m timeframe from Binance and have downloaded them into the data directory in the default location.\n", + "Please follow the [documentation](https://www.freqtrade.io/en/stable/data-download/) for more details." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Setup" + "## Setup\n", + "\n", + "### Change Working directory to repository root" ] }, { @@ -26,12 +29,29 @@ "import os\n", "from pathlib import Path\n", "\n", - "# Change current working directory from `somedir/freqtrade/user_data/notebooks` to project root `somedir/freqtrade`, so relative paths remain consistent.\n", - "if not Path(\"LICENSE\").is_file():\n", - " os.chdir(\"../../\")\n", + "# Change directory\n", + "# Modify this cell to insure that the output shows the correct path.\n", + "# Define all paths relative to the project root shown in the cell output\n", + "project_root = \"somedir/freqtrade\"\n", + "i=0\n", + "try:\n", + " os.chdirdir(project_root)\n", + " assert Path('LICENSE').is_file()\n", + "except:\n", + " while i<4 and (not Path('LICENSE').is_file()):\n", + " os.chdir(Path(Path.cwd(), '../'))\n", + " i+=1\n", + " project_root = Path.cwd()\n", "print(Path.cwd())" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Configure Freqtrade environment" + ] + }, { "cell_type": "code", "execution_count": null, @@ -70,12 +90,12 @@ "candles = load_pair_history(datadir=data_location,\n", " timeframe=config[\"timeframe\"],\n", " pair=pair,\n", - " data_format = \"json\",\n", + " data_format = \"json\", # Make sure to update this to your data\n", " candle_type=CandleType.SPOT,\n", " )\n", "\n", "# Confirm success\n", - "print(\"Loaded \" + str(len(candles)) + f\" rows of data for {pair} from {data_location}\")\n", + "print(f\"Loaded {len(candles)} rows of data for {pair} from {data_location}\")\n", "candles.head()" ] }, @@ -379,7 +399,7 @@ "metadata": { "file_extension": ".py", "kernelspec": { - "display_name": "Python 3.11.0 64-bit", + "display_name": "Python 3.9.7 64-bit", "language": "python", "name": "python3" }, @@ -393,7 +413,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.0" + "version": "3.9.7" }, "mimetype": "text/x-python", "name": "python", @@ -444,7 +464,7 @@ "version": 3, "vscode": { "interpreter": { - "hash": "945ba00099661281427cc644a7000ee9eeea5ce6ad3bf937939d3d384b8f3881" + "hash": "675f32a300d6d26767470181ad0b11dd4676bcce7ed1dd2ffe2fbc370c95fc7c" } } }, From bc48099e48333d5c657fcbb11831ea8cd1700697 Mon Sep 17 00:00:00 2001 From: Emre Date: Mon, 5 Dec 2022 23:52:48 +0300 Subject: [PATCH 13/49] Revert changes --- freqtrade/freqai/utils.py | 20 -------------------- freqtrade/strategy/interface.py | 4 +--- tests/freqai/test_freqai_interface.py | 17 +---------------- 3 files changed, 2 insertions(+), 39 deletions(-) diff --git a/freqtrade/freqai/utils.py b/freqtrade/freqai/utils.py index 7a9d3df76..806e3ca15 100644 --- a/freqtrade/freqai/utils.py +++ b/freqtrade/freqai/utils.py @@ -233,23 +233,3 @@ def get_timerange_backtest_live_models(config: Config) -> str: dd = FreqaiDataDrawer(models_path, config) timerange = dd.get_timerange_from_live_historic_predictions() return timerange.timerange_str - - -def ensure_base_tf_in_include_timeframes(config: Config) -> Config: - """ - Ensure that the base timeframe is included in the include_timeframes list - :param config: Configuration dictionary - - :return config: Configuration dictionary - """ - feature_parameters = config.get('freqai', {}).get('feature_parameters', {}) - include_timeframes = feature_parameters.get('include_timeframes', []) - - if config['timeframe'] in include_timeframes: - return config - - include_timeframes = [config['timeframe']] + include_timeframes - config.get('freqai', {}).get('feature_parameters', {}) \ - .update({**feature_parameters, 'include_timeframes': include_timeframes}) - - return config diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 48a03e216..681c5fcbb 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -148,11 +148,9 @@ class IStrategy(ABC, HyperStrategyMixin): def load_freqAI_model(self) -> None: if self.config.get('freqai', {}).get('enabled', False): # Import here to avoid importing this if freqAI is disabled - from freqtrade.freqai.utils import (download_all_data_for_training, - ensure_base_tf_in_include_timeframes) + from freqtrade.freqai.utils import download_all_data_for_training from freqtrade.resolvers.freqaimodel_resolver import FreqaiModelResolver self.freqai = FreqaiModelResolver.load_freqaimodel(self.config) - self.config = ensure_base_tf_in_include_timeframes(self.config) self.freqai_info = self.config["freqai"] # download the desired data in dry/live diff --git a/tests/freqai/test_freqai_interface.py b/tests/freqai/test_freqai_interface.py index 6f01c66f6..c53137093 100644 --- a/tests/freqai/test_freqai_interface.py +++ b/tests/freqai/test_freqai_interface.py @@ -9,9 +9,7 @@ from freqtrade.configuration import TimeRange from freqtrade.data.dataprovider import DataProvider from freqtrade.enums import RunMode from freqtrade.freqai.data_kitchen import FreqaiDataKitchen -from freqtrade.freqai.utils import (download_all_data_for_training, - ensure_base_tf_in_include_timeframes, - get_required_data_timerange) +from freqtrade.freqai.utils import download_all_data_for_training, get_required_data_timerange from freqtrade.optimize.backtesting import Backtesting from freqtrade.persistence import Trade from freqtrade.plugins.pairlistmanager import PairListManager @@ -530,19 +528,6 @@ def test_start_set_train_queue(mocker, freqai_conf, caplog): ) -def test_base_tf_in_include_timeframes(mocker, freqai_conf): - freqai_conf['timeframe'] = '5m' - freqai_conf['freqai']['feature_parameters'].update({ - 'include_timeframes': ['15m', '1h'] - }) - updated_conf = ensure_base_tf_in_include_timeframes(freqai_conf) - assert updated_conf['freqai']['feature_parameters']['include_timeframes'] == [ - '5m', '15m', '1h', - ] - last_conf = ensure_base_tf_in_include_timeframes(updated_conf) - assert last_conf == updated_conf - - def test_get_required_data_timerange(mocker, freqai_conf): time_range = get_required_data_timerange(freqai_conf) assert (time_range.stopts - time_range.startts) == 177300 From 26a61afa15bce5d85256e8706534295f8cb033c3 Mon Sep 17 00:00:00 2001 From: Emre Date: Mon, 5 Dec 2022 23:54:15 +0300 Subject: [PATCH 14/49] Move base tf logic to config validation --- freqtrade/configuration/config_validation.py | 7 +++++++ tests/test_configuration.py | 7 ++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/freqtrade/configuration/config_validation.py b/freqtrade/configuration/config_validation.py index bf0657994..606f081ef 100644 --- a/freqtrade/configuration/config_validation.py +++ b/freqtrade/configuration/config_validation.py @@ -355,6 +355,13 @@ def _validate_freqai_include_timeframes(conf: Dict[str, Any]) -> None: f"Main timeframe of {main_tf} must be smaller or equal to FreqAI " f"`include_timeframes`.Offending include-timeframes: {', '.join(offending_lines)}") + # Ensure that the base timeframe is included in the include_timeframes list + if main_tf not in freqai_include_timeframes: + feature_parameters = conf.get('freqai', {}).get('feature_parameters', {}) + include_timeframes = [main_tf] + freqai_include_timeframes + conf.get('freqai', {}).get('feature_parameters', {}) \ + .update({**feature_parameters, 'include_timeframes': include_timeframes}) + def _validate_freqai_backtest(conf: Dict[str, Any]) -> None: if conf.get('runmode', RunMode.OTHER) == RunMode.BACKTEST: diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 1bcff20db..cdf9f2f2e 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -1046,8 +1046,13 @@ def test__validate_freqai_include_timeframes(default_conf, caplog) -> None: # Validation pass conf.update({'timeframe': '1m'}) validate_config_consistency(conf) - conf.update({'analyze_per_epoch': True}) + # Ensure base timeframe is in include_timeframes + conf['freqai']['feature_parameters']['include_timeframes'] = ["5m", "15m"] + validate_config_consistency(conf) + assert conf['freqai']['feature_parameters']['include_timeframes'] == ["1m", "5m", "15m"] + + conf.update({'analyze_per_epoch': True}) with pytest.raises(OperationalException, match=r"Using analyze-per-epoch .* not supported with a FreqAI strategy."): validate_config_consistency(conf) From 227cdb09386153fd7a871e3b72ff46cd2999962e Mon Sep 17 00:00:00 2001 From: Emre Date: Mon, 5 Dec 2022 23:58:04 +0300 Subject: [PATCH 15/49] Change dict update order --- freqtrade/configuration/config_validation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/configuration/config_validation.py b/freqtrade/configuration/config_validation.py index 606f081ef..7e291cb90 100644 --- a/freqtrade/configuration/config_validation.py +++ b/freqtrade/configuration/config_validation.py @@ -360,7 +360,7 @@ def _validate_freqai_include_timeframes(conf: Dict[str, Any]) -> None: feature_parameters = conf.get('freqai', {}).get('feature_parameters', {}) include_timeframes = [main_tf] + freqai_include_timeframes conf.get('freqai', {}).get('feature_parameters', {}) \ - .update({**feature_parameters, 'include_timeframes': include_timeframes}) + .update({'include_timeframes': include_timeframes, **feature_parameters}) def _validate_freqai_backtest(conf: Dict[str, Any]) -> None: From 58604c747e759161f25ad4c90571fbaf6a1c5233 Mon Sep 17 00:00:00 2001 From: initrv Date: Wed, 7 Dec 2022 14:37:55 +0300 Subject: [PATCH 16/49] cleanup tensorboard callback --- freqtrade/freqai/RL/BaseEnvironment.py | 10 ++----- freqtrade/freqai/RL/TensorboardCallback.py | 27 +++++++++---------- .../prediction_models/ReinforcementLearner.py | 14 +++++----- 3 files changed, 21 insertions(+), 30 deletions(-) diff --git a/freqtrade/freqai/RL/BaseEnvironment.py b/freqtrade/freqai/RL/BaseEnvironment.py index a31ded0c6..71b423844 100644 --- a/freqtrade/freqai/RL/BaseEnvironment.py +++ b/freqtrade/freqai/RL/BaseEnvironment.py @@ -137,15 +137,9 @@ class BaseEnvironment(gym.Env): Reset is called at the beginning of every episode """ # custom_info is used for episodic reports and tensorboard logging - self.custom_info["Invalid"] = 0 - self.custom_info["Hold"] = 0 - self.custom_info["Unknown"] = 0 - self.custom_info["pnl_factor"] = 0 - self.custom_info["duration_factor"] = 0 - self.custom_info["reward_exit"] = 0 - self.custom_info["reward_hold"] = 0 + self.custom_info: dict = {} for action in self.actions: - self.custom_info[f"{action.name}"] = 0 + self.custom_info[action.name] = 0 self._done = False diff --git a/freqtrade/freqai/RL/TensorboardCallback.py b/freqtrade/freqai/RL/TensorboardCallback.py index f590bdf84..d03c040d4 100644 --- a/freqtrade/freqai/RL/TensorboardCallback.py +++ b/freqtrade/freqai/RL/TensorboardCallback.py @@ -42,19 +42,18 @@ class TensorboardCallback(BaseCallback): ) def _on_step(self) -> bool: + + local_info = self.locals["infos"][0] custom_info = self.training_env.get_attr("custom_info")[0] - self.logger.record("_state/position", self.locals["infos"][0]["position"]) - self.logger.record("_state/trade_duration", self.locals["infos"][0]["trade_duration"]) - self.logger.record("_state/current_profit_pct", self.locals["infos"] - [0]["current_profit_pct"]) - self.logger.record("_reward/total_profit", self.locals["infos"][0]["total_profit"]) - self.logger.record("_reward/total_reward", self.locals["infos"][0]["total_reward"]) - self.logger.record_mean("_reward/mean_trade_duration", self.locals["infos"] - [0]["trade_duration"]) - self.logger.record("_actions/action", self.locals["infos"][0]["action"]) - self.logger.record("_actions/_Invalid", custom_info["Invalid"]) - self.logger.record("_actions/_Unknown", custom_info["Unknown"]) - self.logger.record("_actions/Hold", custom_info["Hold"]) - for action in self.actions: - self.logger.record(f"_actions/{action.name}", custom_info[action.name]) + + for info in local_info: + if info not in ["episode", "terminal_observation"]: + self.logger.record(f"_info/{info}", local_info[info]) + + for info in custom_info: + if info in [action.name for action in self.actions]: + self.logger.record(f"_actions/{info}", custom_info[info]) + else: + self.logger.record(f"_custom/{info}", custom_info[info]) + return True diff --git a/freqtrade/freqai/prediction_models/ReinforcementLearner.py b/freqtrade/freqai/prediction_models/ReinforcementLearner.py index 47dbaf99e..1383ad15e 100644 --- a/freqtrade/freqai/prediction_models/ReinforcementLearner.py +++ b/freqtrade/freqai/prediction_models/ReinforcementLearner.py @@ -100,7 +100,6 @@ class ReinforcementLearner(BaseReinforcementLearningModel): """ # first, penalize if the action is not valid if not self._is_valid(action): - self.custom_info["Invalid"] += 1 return -2 pnl = self.get_unrealized_profit() @@ -109,15 +108,15 @@ class ReinforcementLearner(BaseReinforcementLearningModel): # reward agent for entering trades if (action == Actions.Long_enter.value and self._position == Positions.Neutral): - self.custom_info[f"{Actions.Long_enter.name}"] += 1 + self.custom_info[Actions.Long_enter.name] += 1 return 25 if (action == Actions.Short_enter.value and self._position == Positions.Neutral): - self.custom_info[f"{Actions.Short_enter.name}"] += 1 + self.custom_info[Actions.Short_enter.name] += 1 return 25 # discourage agent from not entering trades if action == Actions.Neutral.value and self._position == Positions.Neutral: - self.custom_info[f"{Actions.Neutral.name}"] += 1 + self.custom_info[Actions.Neutral.name] += 1 return -1 max_trade_duration = self.rl_config.get('max_trade_duration_candles', 300) @@ -131,22 +130,21 @@ class ReinforcementLearner(BaseReinforcementLearningModel): # discourage sitting in position if (self._position in (Positions.Short, Positions.Long) and action == Actions.Neutral.value): - self.custom_info["Hold"] += 1 + self.custom_info[Actions.Neutral.name] += 1 return -1 * trade_duration / max_trade_duration # close long if action == Actions.Long_exit.value and self._position == Positions.Long: if pnl > self.profit_aim * self.rr: factor *= self.rl_config['model_reward_parameters'].get('win_reward_factor', 2) - self.custom_info[f"{Actions.Long_exit.name}"] += 1 + self.custom_info[Actions.Long_exit.name] += 1 return float(pnl * factor) # close short if action == Actions.Short_exit.value and self._position == Positions.Short: if pnl > self.profit_aim * self.rr: factor *= self.rl_config['model_reward_parameters'].get('win_reward_factor', 2) - self.custom_info[f"{Actions.Short_exit.name}"] += 1 + self.custom_info[Actions.Short_exit.name] += 1 return float(pnl * factor) - self.custom_info["Unknown"] += 1 return 0. From 9b4364ddc3e410ca445cf08d73c606aed4323e6d Mon Sep 17 00:00:00 2001 From: robcaulk Date: Wed, 7 Dec 2022 19:49:14 +0100 Subject: [PATCH 17/49] ensure that add_state_info is deactivated during backtesting --- freqtrade/freqai/RL/BaseEnvironment.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/freqtrade/freqai/RL/BaseEnvironment.py b/freqtrade/freqai/RL/BaseEnvironment.py index a31ded0c6..c217b72dd 100644 --- a/freqtrade/freqai/RL/BaseEnvironment.py +++ b/freqtrade/freqai/RL/BaseEnvironment.py @@ -12,6 +12,7 @@ from gym.utils import seeding from pandas import DataFrame from freqtrade.data.dataprovider import DataProvider +from freqtrade.enums import RunMode logger = logging.getLogger(__name__) @@ -78,6 +79,11 @@ class BaseEnvironment(gym.Env): # set here to default 5Ac, but all children envs can override this self.actions: Type[Enum] = BaseActions self.custom_info: dict = {} + self.live: bool = False + if dp: + self.live = dp.runmode in (RunMode.DRY_RUN, RunMode.LIVE) + if not self.live and self.add_state_info: + logger.warning("add_state_info is not available in backtesting. Deactivating.") def reset_env(self, df: DataFrame, prices: DataFrame, window_size: int, reward_kwargs: dict, starting_point=True): @@ -188,7 +194,7 @@ class BaseEnvironment(gym.Env): """ features_window = self.signal_features[( self._current_tick - self.window_size):self._current_tick] - if self.add_state_info: + if self.add_state_info and self.live: features_and_state = DataFrame(np.zeros((len(features_window), 3)), columns=['current_profit_pct', 'position', From 7b3406914c2a219b877867e08f93c26ab64d9e41 Mon Sep 17 00:00:00 2001 From: robcaulk Date: Wed, 7 Dec 2022 19:49:39 +0100 Subject: [PATCH 18/49] flip add_state_info --- freqtrade/freqai/RL/BaseEnvironment.py | 1 + 1 file changed, 1 insertion(+) diff --git a/freqtrade/freqai/RL/BaseEnvironment.py b/freqtrade/freqai/RL/BaseEnvironment.py index c217b72dd..86c63c382 100644 --- a/freqtrade/freqai/RL/BaseEnvironment.py +++ b/freqtrade/freqai/RL/BaseEnvironment.py @@ -83,6 +83,7 @@ class BaseEnvironment(gym.Env): if dp: self.live = dp.runmode in (RunMode.DRY_RUN, RunMode.LIVE) if not self.live and self.add_state_info: + self.add_state_info = False logger.warning("add_state_info is not available in backtesting. Deactivating.") def reset_env(self, df: DataFrame, prices: DataFrame, window_size: int, From 74e623fe5b4c5931362f149ce88d52ed3cb12cdc Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 8 Dec 2022 08:33:07 +0100 Subject: [PATCH 19/49] Improve kraken test resiliance --- tests/exchange/test_ccxt_compat.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/exchange/test_ccxt_compat.py b/tests/exchange/test_ccxt_compat.py index 280876ae8..7f23c2031 100644 --- a/tests/exchange/test_ccxt_compat.py +++ b/tests/exchange/test_ccxt_compat.py @@ -224,8 +224,13 @@ class TestCCXTExchange(): for val in [1, 2, 5, 25, 100]: l2 = exchange.fetch_l2_order_book(pair, val) if not l2_limit_range or val in l2_limit_range: - assert len(l2['asks']) == val - assert len(l2['bids']) == val + if val > 50: + # Orderbooks are not always this deep. + assert val - 5 < len(l2['asks']) <= val + assert val - 5 < len(l2['bids']) <= val + else: + assert len(l2['asks']) == val + assert len(l2['bids']) == val else: next_limit = exchange.get_next_limit_in_list( val, l2_limit_range, l2_limit_range_required) From 3d3a7033ed34f8c9bed86c729198e8a4b5e0414f Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 8 Dec 2022 08:46:16 +0100 Subject: [PATCH 20/49] Improve Docker documentation wording --- docs/docker_quickstart.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/docker_quickstart.md b/docs/docker_quickstart.md index 6b48a7877..89f737d71 100644 --- a/docs/docker_quickstart.md +++ b/docs/docker_quickstart.md @@ -4,13 +4,15 @@ This page explains how to run the bot with Docker. It is not meant to work out o ## Install Docker -Start by downloading and installing Docker CE for your platform: +Start by downloading and installing Docker / Docker Desktop for your platform: * [Mac](https://docs.docker.com/docker-for-mac/install/) * [Windows](https://docs.docker.com/docker-for-windows/install/) * [Linux](https://docs.docker.com/install/) -To simplify running freqtrade, [`docker compose`](https://docs.docker.com/compose/install/) should be installed and available to follow the below [docker quick start guide](#docker-quick-start). +!!! Info "Docker compose install" + Freqtrade documentation assumes the use of Docker desktop (or the docker compose plugin). + While the docker-compose standalone installation still works, it will require changing all `docker compose` commands from `docker compose` to `docker-compose` to work (e.g. `docker compose up -d` will become `docker-compose up -d`). ## Freqtrade with docker From bbedc4b63efd08a4e4e3b2371a8463e6f6e445b3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 8 Dec 2022 14:15:29 +0100 Subject: [PATCH 21/49] Stop clock to avoid random failures on slow CI runs --- tests/rpc/test_rpc_telegram.py | 70 +++++++++++++++++----------------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 3552d5fe7..1f4665867 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -12,6 +12,7 @@ from unittest.mock import ANY, MagicMock import arrow import pytest +import time_machine from pandas import DataFrame from telegram import Chat, Message, ReplyKeyboardMarkup, Update from telegram.error import BadRequest, NetworkError, TelegramError @@ -2065,41 +2066,42 @@ def test_send_msg_sell_fill_notification(default_conf, mocker, direction, default_conf['telegram']['notification_settings']['exit_fill'] = 'on' telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf) - telegram.send_msg({ - 'type': RPCMessageType.EXIT_FILL, - 'trade_id': 1, - 'exchange': 'Binance', - 'pair': 'KEY/ETH', - 'leverage': leverage, - 'direction': direction, - 'gain': 'loss', - 'limit': 3.201e-05, - 'amount': 1333.3333333333335, - 'order_type': 'market', - 'open_rate': 7.5e-05, - 'close_rate': 3.201e-05, - 'profit_amount': -0.05746268, - 'profit_ratio': -0.57405275, - 'stake_currency': 'ETH', - 'enter_tag': enter_signal, - 'exit_reason': ExitType.STOP_LOSS.value, - 'open_date': arrow.utcnow().shift(days=-1, hours=-2, minutes=-30), - 'close_date': arrow.utcnow(), - }) + with time_machine.travel("2022-09-01 05:00:00 +00:00", tick=False) as t: + telegram.send_msg({ + 'type': RPCMessageType.EXIT_FILL, + 'trade_id': 1, + 'exchange': 'Binance', + 'pair': 'KEY/ETH', + 'leverage': leverage, + 'direction': direction, + 'gain': 'loss', + 'limit': 3.201e-05, + 'amount': 1333.3333333333335, + 'order_type': 'market', + 'open_rate': 7.5e-05, + 'close_rate': 3.201e-05, + 'profit_amount': -0.05746268, + 'profit_ratio': -0.57405275, + 'stake_currency': 'ETH', + 'enter_tag': enter_signal, + 'exit_reason': ExitType.STOP_LOSS.value, + 'open_date': arrow.utcnow().shift(days=-1, hours=-2, minutes=-30), + 'close_date': arrow.utcnow(), + }) - leverage_text = f'*Leverage:* `{leverage}`\n' if leverage and leverage != 1.0 else '' - assert msg_mock.call_args[0][0] == ( - '\N{WARNING SIGN} *Binance (dry):* Exited KEY/ETH (#1)\n' - '*Profit:* `-57.41% (loss: -0.05746268 ETH)`\n' - f'*Enter Tag:* `{enter_signal}`\n' - '*Exit Reason:* `stop_loss`\n' - f"*Direction:* `{direction}`\n" - f"{leverage_text}" - '*Amount:* `1333.33333333`\n' - '*Open Rate:* `0.00007500`\n' - '*Exit Rate:* `0.00003201`\n' - '*Duration:* `1 day, 2:30:00 (1590.0 min)`' - ) + leverage_text = f'*Leverage:* `{leverage}`\n' if leverage and leverage != 1.0 else '' + assert msg_mock.call_args[0][0] == ( + '\N{WARNING SIGN} *Binance (dry):* Exited KEY/ETH (#1)\n' + '*Profit:* `-57.41% (loss: -0.05746268 ETH)`\n' + f'*Enter Tag:* `{enter_signal}`\n' + '*Exit Reason:* `stop_loss`\n' + f"*Direction:* `{direction}`\n" + f"{leverage_text}" + '*Amount:* `1333.33333333`\n' + '*Open Rate:* `0.00007500`\n' + '*Exit Rate:* `0.00003201`\n' + '*Duration:* `1 day, 2:30:00 (1590.0 min)`' + ) def test_send_msg_status_notification(default_conf, mocker) -> None: From 1da8ad69d9501838fee5792b39563d9925ed7ad5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 8 Dec 2022 14:33:07 +0100 Subject: [PATCH 22/49] improve more tests by freezing time --- tests/rpc/test_rpc_telegram.py | 223 +++++++++++++++++---------------- 1 file changed, 112 insertions(+), 111 deletions(-) diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 1f4665867..58977a94a 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -1907,119 +1907,120 @@ def test_send_msg_entry_fill_notification(default_conf, mocker, message_type, en def test_send_msg_sell_notification(default_conf, mocker) -> None: - telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf) + with time_machine.travel("2022-09-01 05:00:00 +00:00", tick=False): + telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf) - old_convamount = telegram._rpc._fiat_converter.convert_amount - telegram._rpc._fiat_converter.convert_amount = lambda a, b, c: -24.812 - telegram.send_msg({ - 'type': RPCMessageType.EXIT, - 'trade_id': 1, - 'exchange': 'Binance', - 'pair': 'KEY/ETH', - 'leverage': 1.0, - 'direction': 'Long', - 'gain': 'loss', - 'order_rate': 3.201e-05, - 'amount': 1333.3333333333335, - 'order_type': 'market', - 'open_rate': 7.5e-05, - 'current_rate': 3.201e-05, - 'profit_amount': -0.05746268, - 'profit_ratio': -0.57405275, - 'stake_currency': 'ETH', - 'fiat_currency': 'USD', - 'enter_tag': 'buy_signal1', - 'exit_reason': ExitType.STOP_LOSS.value, - 'open_date': arrow.utcnow().shift(hours=-1), - 'close_date': arrow.utcnow(), - }) - assert msg_mock.call_args[0][0] == ( - '\N{WARNING SIGN} *Binance (dry):* Exiting KEY/ETH (#1)\n' - '*Unrealized Profit:* `-57.41% (loss: -0.05746268 ETH / -24.812 USD)`\n' - '*Enter Tag:* `buy_signal1`\n' - '*Exit Reason:* `stop_loss`\n' - '*Direction:* `Long`\n' - '*Amount:* `1333.33333333`\n' - '*Open Rate:* `0.00007500`\n' - '*Current Rate:* `0.00003201`\n' - '*Exit Rate:* `0.00003201`\n' - '*Duration:* `1:00:00 (60.0 min)`' - ) - - msg_mock.reset_mock() - telegram.send_msg({ - 'type': RPCMessageType.EXIT, - 'trade_id': 1, - 'exchange': 'Binance', - 'pair': 'KEY/ETH', - 'direction': 'Long', - 'gain': 'loss', - 'order_rate': 3.201e-05, - 'amount': 1333.3333333333335, - 'order_type': 'market', - 'open_rate': 7.5e-05, - 'current_rate': 3.201e-05, - 'cumulative_profit': -0.15746268, - 'profit_amount': -0.05746268, - 'profit_ratio': -0.57405275, - 'stake_currency': 'ETH', - 'fiat_currency': 'USD', - 'enter_tag': 'buy_signal1', - 'exit_reason': ExitType.STOP_LOSS.value, - 'open_date': arrow.utcnow().shift(days=-1, hours=-2, minutes=-30), - 'close_date': arrow.utcnow(), - 'stake_amount': 0.01, - 'sub_trade': True, - }) - assert msg_mock.call_args[0][0] == ( - '\N{WARNING SIGN} *Binance (dry):* Exiting KEY/ETH (#1)\n' - '*Unrealized Sub Profit:* `-57.41% (loss: -0.05746268 ETH / -24.812 USD)`\n' - '*Cumulative Profit:* (`-0.15746268 ETH / -24.812 USD`)\n' - '*Enter Tag:* `buy_signal1`\n' - '*Exit Reason:* `stop_loss`\n' - '*Direction:* `Long`\n' - '*Amount:* `1333.33333333`\n' - '*Open Rate:* `0.00007500`\n' - '*Current Rate:* `0.00003201`\n' - '*Exit Rate:* `0.00003201`\n' - '*Remaining:* `(0.01 ETH, -24.812 USD)`' + old_convamount = telegram._rpc._fiat_converter.convert_amount + telegram._rpc._fiat_converter.convert_amount = lambda a, b, c: -24.812 + telegram.send_msg({ + 'type': RPCMessageType.EXIT, + 'trade_id': 1, + 'exchange': 'Binance', + 'pair': 'KEY/ETH', + 'leverage': 1.0, + 'direction': 'Long', + 'gain': 'loss', + 'order_rate': 3.201e-05, + 'amount': 1333.3333333333335, + 'order_type': 'market', + 'open_rate': 7.5e-05, + 'current_rate': 3.201e-05, + 'profit_amount': -0.05746268, + 'profit_ratio': -0.57405275, + 'stake_currency': 'ETH', + 'fiat_currency': 'USD', + 'enter_tag': 'buy_signal1', + 'exit_reason': ExitType.STOP_LOSS.value, + 'open_date': arrow.utcnow().shift(hours=-1), + 'close_date': arrow.utcnow(), + }) + assert msg_mock.call_args[0][0] == ( + '\N{WARNING SIGN} *Binance (dry):* Exiting KEY/ETH (#1)\n' + '*Unrealized Profit:* `-57.41% (loss: -0.05746268 ETH / -24.812 USD)`\n' + '*Enter Tag:* `buy_signal1`\n' + '*Exit Reason:* `stop_loss`\n' + '*Direction:* `Long`\n' + '*Amount:* `1333.33333333`\n' + '*Open Rate:* `0.00007500`\n' + '*Current Rate:* `0.00003201`\n' + '*Exit Rate:* `0.00003201`\n' + '*Duration:* `1:00:00 (60.0 min)`' ) - msg_mock.reset_mock() - telegram.send_msg({ - 'type': RPCMessageType.EXIT, - 'trade_id': 1, - 'exchange': 'Binance', - 'pair': 'KEY/ETH', - 'direction': 'Long', - 'gain': 'loss', - 'order_rate': 3.201e-05, - 'amount': 1333.3333333333335, - 'order_type': 'market', - 'open_rate': 7.5e-05, - 'current_rate': 3.201e-05, - 'profit_amount': -0.05746268, - 'profit_ratio': -0.57405275, - 'stake_currency': 'ETH', - 'enter_tag': 'buy_signal1', - 'exit_reason': ExitType.STOP_LOSS.value, - 'open_date': arrow.utcnow().shift(days=-1, hours=-2, minutes=-30), - 'close_date': arrow.utcnow(), - }) - assert msg_mock.call_args[0][0] == ( - '\N{WARNING SIGN} *Binance (dry):* Exiting KEY/ETH (#1)\n' - '*Unrealized Profit:* `-57.41% (loss: -0.05746268 ETH)`\n' - '*Enter Tag:* `buy_signal1`\n' - '*Exit Reason:* `stop_loss`\n' - '*Direction:* `Long`\n' - '*Amount:* `1333.33333333`\n' - '*Open Rate:* `0.00007500`\n' - '*Current Rate:* `0.00003201`\n' - '*Exit Rate:* `0.00003201`\n' - '*Duration:* `1 day, 2:30:00 (1590.0 min)`' - ) - # Reset singleton function to avoid random breaks - telegram._rpc._fiat_converter.convert_amount = old_convamount + msg_mock.reset_mock() + telegram.send_msg({ + 'type': RPCMessageType.EXIT, + 'trade_id': 1, + 'exchange': 'Binance', + 'pair': 'KEY/ETH', + 'direction': 'Long', + 'gain': 'loss', + 'order_rate': 3.201e-05, + 'amount': 1333.3333333333335, + 'order_type': 'market', + 'open_rate': 7.5e-05, + 'current_rate': 3.201e-05, + 'cumulative_profit': -0.15746268, + 'profit_amount': -0.05746268, + 'profit_ratio': -0.57405275, + 'stake_currency': 'ETH', + 'fiat_currency': 'USD', + 'enter_tag': 'buy_signal1', + 'exit_reason': ExitType.STOP_LOSS.value, + 'open_date': arrow.utcnow().shift(days=-1, hours=-2, minutes=-30), + 'close_date': arrow.utcnow(), + 'stake_amount': 0.01, + 'sub_trade': True, + }) + assert msg_mock.call_args[0][0] == ( + '\N{WARNING SIGN} *Binance (dry):* Exiting KEY/ETH (#1)\n' + '*Unrealized Sub Profit:* `-57.41% (loss: -0.05746268 ETH / -24.812 USD)`\n' + '*Cumulative Profit:* (`-0.15746268 ETH / -24.812 USD`)\n' + '*Enter Tag:* `buy_signal1`\n' + '*Exit Reason:* `stop_loss`\n' + '*Direction:* `Long`\n' + '*Amount:* `1333.33333333`\n' + '*Open Rate:* `0.00007500`\n' + '*Current Rate:* `0.00003201`\n' + '*Exit Rate:* `0.00003201`\n' + '*Remaining:* `(0.01 ETH, -24.812 USD)`' + ) + + msg_mock.reset_mock() + telegram.send_msg({ + 'type': RPCMessageType.EXIT, + 'trade_id': 1, + 'exchange': 'Binance', + 'pair': 'KEY/ETH', + 'direction': 'Long', + 'gain': 'loss', + 'order_rate': 3.201e-05, + 'amount': 1333.3333333333335, + 'order_type': 'market', + 'open_rate': 7.5e-05, + 'current_rate': 3.201e-05, + 'profit_amount': -0.05746268, + 'profit_ratio': -0.57405275, + 'stake_currency': 'ETH', + 'enter_tag': 'buy_signal1', + 'exit_reason': ExitType.STOP_LOSS.value, + 'open_date': arrow.utcnow().shift(days=-1, hours=-2, minutes=-30), + 'close_date': arrow.utcnow(), + }) + assert msg_mock.call_args[0][0] == ( + '\N{WARNING SIGN} *Binance (dry):* Exiting KEY/ETH (#1)\n' + '*Unrealized Profit:* `-57.41% (loss: -0.05746268 ETH)`\n' + '*Enter Tag:* `buy_signal1`\n' + '*Exit Reason:* `stop_loss`\n' + '*Direction:* `Long`\n' + '*Amount:* `1333.33333333`\n' + '*Open Rate:* `0.00007500`\n' + '*Current Rate:* `0.00003201`\n' + '*Exit Rate:* `0.00003201`\n' + '*Duration:* `1 day, 2:30:00 (1590.0 min)`' + ) + # Reset singleton function to avoid random breaks + telegram._rpc._fiat_converter.convert_amount = old_convamount def test_send_msg_sell_cancel_notification(default_conf, mocker) -> None: @@ -2066,7 +2067,7 @@ def test_send_msg_sell_fill_notification(default_conf, mocker, direction, default_conf['telegram']['notification_settings']['exit_fill'] = 'on' telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf) - with time_machine.travel("2022-09-01 05:00:00 +00:00", tick=False) as t: + with time_machine.travel("2022-09-01 05:00:00 +00:00", tick=False): telegram.send_msg({ 'type': RPCMessageType.EXIT_FILL, 'trade_id': 1, From 980a5a9b521d1a905a7beae383fd9ff8a8fd5302 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 10 Dec 2022 19:54:04 +0100 Subject: [PATCH 23/49] Fix docs typo --- freqtrade/plugins/pairlist/VolumePairList.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/plugins/pairlist/VolumePairList.py b/freqtrade/plugins/pairlist/VolumePairList.py index ad27a93d8..be58ec1a1 100644 --- a/freqtrade/plugins/pairlist/VolumePairList.py +++ b/freqtrade/plugins/pairlist/VolumePairList.py @@ -218,7 +218,7 @@ class VolumePairList(IPairList): else: filtered_tickers[i]['quoteVolume'] = 0 else: - # Tickers mode - filter based on incomming pairlist. + # Tickers mode - filter based on incoming pairlist. filtered_tickers = [v for k, v in tickers.items() if k in pairlist] if self._min_value > 0: From 6b9f3f279587e1097915732ad3ac6e69c00c9bb5 Mon Sep 17 00:00:00 2001 From: Emre Date: Sun, 11 Dec 2022 13:24:24 +0300 Subject: [PATCH 24/49] Fix test validation --- freqtrade/configuration/config_validation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/configuration/config_validation.py b/freqtrade/configuration/config_validation.py index 7e291cb90..606f081ef 100644 --- a/freqtrade/configuration/config_validation.py +++ b/freqtrade/configuration/config_validation.py @@ -360,7 +360,7 @@ def _validate_freqai_include_timeframes(conf: Dict[str, Any]) -> None: feature_parameters = conf.get('freqai', {}).get('feature_parameters', {}) include_timeframes = [main_tf] + freqai_include_timeframes conf.get('freqai', {}).get('feature_parameters', {}) \ - .update({'include_timeframes': include_timeframes, **feature_parameters}) + .update({**feature_parameters, 'include_timeframes': include_timeframes}) def _validate_freqai_backtest(conf: Dict[str, Any]) -> None: From 85f22b5c3029a3f613d0b0da7b61eeef8f6685d5 Mon Sep 17 00:00:00 2001 From: robcaulk Date: Sun, 11 Dec 2022 12:15:19 +0100 Subject: [PATCH 25/49] fix bug in MultiOutput* with conv_width = 1 --- freqtrade/freqai/base_models/BaseClassifierModel.py | 3 +++ freqtrade/freqai/base_models/BaseRegressionModel.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/freqtrade/freqai/base_models/BaseClassifierModel.py b/freqtrade/freqai/base_models/BaseClassifierModel.py index 17bffa85b..a5cea879f 100644 --- a/freqtrade/freqai/base_models/BaseClassifierModel.py +++ b/freqtrade/freqai/base_models/BaseClassifierModel.py @@ -95,6 +95,9 @@ class BaseClassifierModel(IFreqaiModel): self.data_cleaning_predict(dk) predictions = self.model.predict(dk.data_dictionary["prediction_features"]) + if self.CONV_WIDTH == 1: + predictions = np.reshape(predictions, (-1, len(dk.label_list))) + pred_df = DataFrame(predictions, columns=dk.label_list) predictions_prob = self.model.predict_proba(dk.data_dictionary["prediction_features"]) diff --git a/freqtrade/freqai/base_models/BaseRegressionModel.py b/freqtrade/freqai/base_models/BaseRegressionModel.py index 766579cb6..1f9b4f5a6 100644 --- a/freqtrade/freqai/base_models/BaseRegressionModel.py +++ b/freqtrade/freqai/base_models/BaseRegressionModel.py @@ -95,6 +95,9 @@ class BaseRegressionModel(IFreqaiModel): self.data_cleaning_predict(dk) predictions = self.model.predict(dk.data_dictionary["prediction_features"]) + if self.CONV_WIDTH == 1: + predictions = np.reshape(predictions, (-1, len(dk.label_list))) + pred_df = DataFrame(predictions, columns=dk.label_list) pred_df = dk.denormalize_labels_from_metadata(pred_df) From 8c7ec07951eadf53a5722fe7d7489e9a95e5ab46 Mon Sep 17 00:00:00 2001 From: robcaulk Date: Sun, 11 Dec 2022 12:39:31 +0100 Subject: [PATCH 26/49] ensure predict_proba follows suit. Remove all lib specific params from example config --- config_examples/config_freqai.example.json | 1 - freqtrade/freqai/base_models/BaseClassifierModel.py | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/config_examples/config_freqai.example.json b/config_examples/config_freqai.example.json index 5e564a1fc..f58a4468b 100644 --- a/config_examples/config_freqai.example.json +++ b/config_examples/config_freqai.example.json @@ -80,7 +80,6 @@ "random_state": 1 }, "model_training_parameters": { - "n_estimators": 1000 } }, "bot_name": "", diff --git a/freqtrade/freqai/base_models/BaseClassifierModel.py b/freqtrade/freqai/base_models/BaseClassifierModel.py index a5cea879f..ffd42dd1d 100644 --- a/freqtrade/freqai/base_models/BaseClassifierModel.py +++ b/freqtrade/freqai/base_models/BaseClassifierModel.py @@ -101,6 +101,8 @@ class BaseClassifierModel(IFreqaiModel): pred_df = DataFrame(predictions, columns=dk.label_list) predictions_prob = self.model.predict_proba(dk.data_dictionary["prediction_features"]) + if self.CONV_WIDTH == 1: + predictions_prob = np.reshape(predictions_prob, (-1, len(self.model.classes_))) pred_df_prob = DataFrame(predictions_prob, columns=self.model.classes_) pred_df = pd.concat([pred_df, pred_df_prob], axis=1) From cb8fc3c8c7c392b75493d8da7f748760372040a9 Mon Sep 17 00:00:00 2001 From: initrv Date: Sun, 11 Dec 2022 15:37:45 +0300 Subject: [PATCH 27/49] custom info to tensorboard_metrics --- freqtrade/freqai/RL/Base4ActionRLEnv.py | 2 +- freqtrade/freqai/RL/Base5ActionRLEnv.py | 1 + freqtrade/freqai/RL/BaseEnvironment.py | 8 ++++---- freqtrade/freqai/RL/TensorboardCallback.py | 8 ++++---- .../freqai/prediction_models/ReinforcementLearner.py | 6 ------ 5 files changed, 10 insertions(+), 15 deletions(-) diff --git a/freqtrade/freqai/RL/Base4ActionRLEnv.py b/freqtrade/freqai/RL/Base4ActionRLEnv.py index 79616d778..02e182bbd 100644 --- a/freqtrade/freqai/RL/Base4ActionRLEnv.py +++ b/freqtrade/freqai/RL/Base4ActionRLEnv.py @@ -46,9 +46,9 @@ class Base4ActionRLEnv(BaseEnvironment): self._done = True self._update_unrealized_total_profit() - step_reward = self.calculate_reward(action) self.total_reward += step_reward + self.tensorboard_metrics[self.actions._member_names_[action]] += 1 trade_type = None if self.is_tradesignal(action): diff --git a/freqtrade/freqai/RL/Base5ActionRLEnv.py b/freqtrade/freqai/RL/Base5ActionRLEnv.py index 1c09f9386..baf7dde9f 100644 --- a/freqtrade/freqai/RL/Base5ActionRLEnv.py +++ b/freqtrade/freqai/RL/Base5ActionRLEnv.py @@ -49,6 +49,7 @@ class Base5ActionRLEnv(BaseEnvironment): self._update_unrealized_total_profit() step_reward = self.calculate_reward(action) self.total_reward += step_reward + self.tensorboard_metrics[self.actions._member_names_[action]] += 1 trade_type = None if self.is_tradesignal(action): diff --git a/freqtrade/freqai/RL/BaseEnvironment.py b/freqtrade/freqai/RL/BaseEnvironment.py index 71b423844..0da13db7c 100644 --- a/freqtrade/freqai/RL/BaseEnvironment.py +++ b/freqtrade/freqai/RL/BaseEnvironment.py @@ -77,7 +77,7 @@ class BaseEnvironment(gym.Env): # set here to default 5Ac, but all children envs can override this self.actions: Type[Enum] = BaseActions - self.custom_info: dict = {} + self.tensorboard_metrics: dict = {} def reset_env(self, df: DataFrame, prices: DataFrame, window_size: int, reward_kwargs: dict, starting_point=True): @@ -136,10 +136,10 @@ class BaseEnvironment(gym.Env): """ Reset is called at the beginning of every episode """ - # custom_info is used for episodic reports and tensorboard logging - self.custom_info: dict = {} + # tensorboard_metrics is used for episodic reports and tensorboard logging + self.tensorboard_metrics: dict = {} for action in self.actions: - self.custom_info[action.name] = 0 + self.tensorboard_metrics[action.name] = 0 self._done = False diff --git a/freqtrade/freqai/RL/TensorboardCallback.py b/freqtrade/freqai/RL/TensorboardCallback.py index d03c040d4..b596742e9 100644 --- a/freqtrade/freqai/RL/TensorboardCallback.py +++ b/freqtrade/freqai/RL/TensorboardCallback.py @@ -44,16 +44,16 @@ class TensorboardCallback(BaseCallback): def _on_step(self) -> bool: local_info = self.locals["infos"][0] - custom_info = self.training_env.get_attr("custom_info")[0] + tensorboard_metrics = self.training_env.get_attr("tensorboard_metrics")[0] for info in local_info: if info not in ["episode", "terminal_observation"]: self.logger.record(f"_info/{info}", local_info[info]) - for info in custom_info: + for info in tensorboard_metrics: if info in [action.name for action in self.actions]: - self.logger.record(f"_actions/{info}", custom_info[info]) + self.logger.record(f"_actions/{info}", tensorboard_metrics[info]) else: - self.logger.record(f"_custom/{info}", custom_info[info]) + self.logger.record(f"_custom/{info}", tensorboard_metrics[info]) return True diff --git a/freqtrade/freqai/prediction_models/ReinforcementLearner.py b/freqtrade/freqai/prediction_models/ReinforcementLearner.py index 1383ad15e..e015b138a 100644 --- a/freqtrade/freqai/prediction_models/ReinforcementLearner.py +++ b/freqtrade/freqai/prediction_models/ReinforcementLearner.py @@ -108,15 +108,12 @@ class ReinforcementLearner(BaseReinforcementLearningModel): # reward agent for entering trades if (action == Actions.Long_enter.value and self._position == Positions.Neutral): - self.custom_info[Actions.Long_enter.name] += 1 return 25 if (action == Actions.Short_enter.value and self._position == Positions.Neutral): - self.custom_info[Actions.Short_enter.name] += 1 return 25 # discourage agent from not entering trades if action == Actions.Neutral.value and self._position == Positions.Neutral: - self.custom_info[Actions.Neutral.name] += 1 return -1 max_trade_duration = self.rl_config.get('max_trade_duration_candles', 300) @@ -130,21 +127,18 @@ class ReinforcementLearner(BaseReinforcementLearningModel): # discourage sitting in position if (self._position in (Positions.Short, Positions.Long) and action == Actions.Neutral.value): - self.custom_info[Actions.Neutral.name] += 1 return -1 * trade_duration / max_trade_duration # close long if action == Actions.Long_exit.value and self._position == Positions.Long: if pnl > self.profit_aim * self.rr: factor *= self.rl_config['model_reward_parameters'].get('win_reward_factor', 2) - self.custom_info[Actions.Long_exit.name] += 1 return float(pnl * factor) # close short if action == Actions.Short_exit.value and self._position == Positions.Short: if pnl > self.profit_aim * self.rr: factor *= self.rl_config['model_reward_parameters'].get('win_reward_factor', 2) - self.custom_info[Actions.Short_exit.name] += 1 return float(pnl * factor) return 0. From 0fd8e214e4f95a4c2c1929e9b26da43c70fd47dc Mon Sep 17 00:00:00 2001 From: robcaulk Date: Sun, 11 Dec 2022 15:31:29 +0100 Subject: [PATCH 28/49] add documentation for tensorboard_log, change how users interact with tensorboard_log --- docs/freqai-reinforcement-learning.md | 26 +++++++++++++++ freqtrade/freqai/RL/Base4ActionRLEnv.py | 2 +- freqtrade/freqai/RL/Base5ActionRLEnv.py | 2 +- freqtrade/freqai/RL/BaseEnvironment.py | 33 ++++++++++++++++--- .../prediction_models/ReinforcementLearner.py | 1 + 5 files changed, 57 insertions(+), 7 deletions(-) diff --git a/docs/freqai-reinforcement-learning.md b/docs/freqai-reinforcement-learning.md index b1a212a92..b831c90a0 100644 --- a/docs/freqai-reinforcement-learning.md +++ b/docs/freqai-reinforcement-learning.md @@ -247,6 +247,32 @@ where `unique-id` is the `identifier` set in the `freqai` configuration file. Th ![tensorboard](assets/tensorboard.jpg) + +### Custom logging + +FreqAI also provides a built in episodic summary logger called `self.tensorboard_log` for adding custom information to the Tensorboard log. By default, this function is already called once per step inside the environment to record the agent actions. All values accumulated for all steps in a single episode are reported at the conclusion of each episode, followed by a full reset of all metrics to 0 in preparation for the subsequent episode. + + +`self.tensorboard_log` can also be used anywhere inside the environment, for example, it can be added to the `calculate_reward` function to collect more detailed information about how often various parts of the reward were called: + +```py + class MyRLEnv(Base5ActionRLEnv): + """ + User made custom environment. This class inherits from BaseEnvironment and gym.env. + Users can override any functions from those parent classes. Here is an example + of a user customized `calculate_reward()` function. + """ + def calculate_reward(self, action: int) -> float: + if not self._is_valid(action): + self.tensorboard_log("is_valid") + return -2 + +``` + +!!! Note + The `self.tensorboard_log()` function is designed for tracking incremented objects only i.e. events, actions inside the training environment. If the event of interest is a float, the float can be passed as the second argument e.g. `self.tensorboard_log("float_metric1", 0.23)` would add 0.23 to `float_metric`. + + ### Choosing a base environment FreqAI provides two base environments, `Base4ActionEnvironment` and `Base5ActionEnvironment`. As the names imply, the environments are customized for agents that can select from 4 or 5 actions. In the `Base4ActionEnvironment`, the agent can enter long, enter short, hold neutral, or exit position. Meanwhile, in the `Base5ActionEnvironment`, the agent has the same actions as Base4, but instead of a single exit action, it separates exit long and exit short. The main changes stemming from the environment selection include: diff --git a/freqtrade/freqai/RL/Base4ActionRLEnv.py b/freqtrade/freqai/RL/Base4ActionRLEnv.py index 02e182bbd..a3ebfdbfa 100644 --- a/freqtrade/freqai/RL/Base4ActionRLEnv.py +++ b/freqtrade/freqai/RL/Base4ActionRLEnv.py @@ -48,7 +48,7 @@ class Base4ActionRLEnv(BaseEnvironment): self._update_unrealized_total_profit() step_reward = self.calculate_reward(action) self.total_reward += step_reward - self.tensorboard_metrics[self.actions._member_names_[action]] += 1 + self.tensorboard_log(self.actions._member_names_[action]) trade_type = None if self.is_tradesignal(action): diff --git a/freqtrade/freqai/RL/Base5ActionRLEnv.py b/freqtrade/freqai/RL/Base5ActionRLEnv.py index baf7dde9f..22d3cae30 100644 --- a/freqtrade/freqai/RL/Base5ActionRLEnv.py +++ b/freqtrade/freqai/RL/Base5ActionRLEnv.py @@ -49,7 +49,7 @@ class Base5ActionRLEnv(BaseEnvironment): self._update_unrealized_total_profit() step_reward = self.calculate_reward(action) self.total_reward += step_reward - self.tensorboard_metrics[self.actions._member_names_[action]] += 1 + self.tensorboard_log(self.actions._member_names_[action]) trade_type = None if self.is_tradesignal(action): diff --git a/freqtrade/freqai/RL/BaseEnvironment.py b/freqtrade/freqai/RL/BaseEnvironment.py index 0da13db7c..a5cee4def 100644 --- a/freqtrade/freqai/RL/BaseEnvironment.py +++ b/freqtrade/freqai/RL/BaseEnvironment.py @@ -2,7 +2,7 @@ import logging import random from abc import abstractmethod from enum import Enum -from typing import Optional, Type +from typing import Optional, Type, Union import gym import numpy as np @@ -132,14 +132,37 @@ class BaseEnvironment(gym.Env): self.np_random, seed = seeding.np_random(seed) return [seed] + def tensorboard_log(self, metric: str, inc: Union[int, float] = 1): + """ + Function builds the tensorboard_metrics dictionary + to be parsed by the TensorboardCallback. This + function is designed for tracking incremented objects, + events, actions inside the training environment. + For example, a user can call this to track the + frequency of occurence of an `is_valid` call in + their `calculate_reward()`: + + def calculate_reward(self, action: int) -> float: + if not self._is_valid(action): + self.tensorboard_log("is_valid") + return -2 + + :param metric: metric to be tracked and incremented + :param inc: value to increment `metric` by + """ + if metric not in self.tensorboard_metrics: + self.tensorboard_metrics[metric] = inc + else: + self.tensorboard_metrics[metric] += inc + + def reset_tensorboard_log(self): + self.tensorboard_metrics = {} + def reset(self): """ Reset is called at the beginning of every episode """ - # tensorboard_metrics is used for episodic reports and tensorboard logging - self.tensorboard_metrics: dict = {} - for action in self.actions: - self.tensorboard_metrics[action.name] = 0 + self.reset_tensorboard_log() self._done = False diff --git a/freqtrade/freqai/prediction_models/ReinforcementLearner.py b/freqtrade/freqai/prediction_models/ReinforcementLearner.py index e015b138a..38ea67e69 100644 --- a/freqtrade/freqai/prediction_models/ReinforcementLearner.py +++ b/freqtrade/freqai/prediction_models/ReinforcementLearner.py @@ -100,6 +100,7 @@ class ReinforcementLearner(BaseReinforcementLearningModel): """ # first, penalize if the action is not valid if not self._is_valid(action): + self.tensorboard_log("is_valid") return -2 pnl = self.get_unrealized_profit() From 78c40f0535617fc29047262719877e6b151075d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 03:00:40 +0000 Subject: [PATCH 29/49] Bump scikit-learn from 1.1.3 to 1.2.0 Bumps [scikit-learn](https://github.com/scikit-learn/scikit-learn) from 1.1.3 to 1.2.0. - [Release notes](https://github.com/scikit-learn/scikit-learn/releases) - [Commits](https://github.com/scikit-learn/scikit-learn/compare/1.1.3...1.2.0) --- updated-dependencies: - dependency-name: scikit-learn dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-freqai.txt | 2 +- requirements-hyperopt.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements-freqai.txt b/requirements-freqai.txt index 66730e29f..5eafc497b 100644 --- a/requirements-freqai.txt +++ b/requirements-freqai.txt @@ -3,7 +3,7 @@ -r requirements-plot.txt # Required for freqai -scikit-learn==1.1.3 +scikit-learn==1.2.0 joblib==1.2.0 catboost==1.1.1; platform_machine != 'aarch64' lightgbm==3.3.3 diff --git a/requirements-hyperopt.txt b/requirements-hyperopt.txt index 4f59ad1fa..83ba62240 100644 --- a/requirements-hyperopt.txt +++ b/requirements-hyperopt.txt @@ -3,7 +3,7 @@ # Required for hyperopt scipy==1.9.3 -scikit-learn==1.1.3 +scikit-learn==1.2.0 scikit-optimize==0.9.0 filelock==3.8.0 progressbar2==4.2.0 From 434eec73341f9b38e34517b2a63d5125d94eeddb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 03:00:46 +0000 Subject: [PATCH 30/49] Bump blosc from 1.10.6 to 1.11.0 Bumps [blosc](https://github.com/blosc/python-blosc) from 1.10.6 to 1.11.0. - [Release notes](https://github.com/blosc/python-blosc/releases) - [Changelog](https://github.com/Blosc/python-blosc/blob/main/RELEASE_NOTES.rst) - [Commits](https://github.com/blosc/python-blosc/compare/v1.10.6...v1.11.0) --- updated-dependencies: - dependency-name: blosc dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 313e0ff9c..4bd527c90 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,7 +20,7 @@ tabulate==0.9.0 pycoingecko==3.1.0 jinja2==3.1.2 tables==3.7.0 -blosc==1.10.6 +blosc==1.11.0 joblib==1.2.0 pyarrow==10.0.1; platform_machine != 'armv7l' From 63d3a9ced66ecccafd77ec57f20a48b0c427993a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 03:00:49 +0000 Subject: [PATCH 31/49] Bump prompt-toolkit from 3.0.33 to 3.0.36 Bumps [prompt-toolkit](https://github.com/prompt-toolkit/python-prompt-toolkit) from 3.0.33 to 3.0.36. - [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.33...3.0.36) --- updated-dependencies: - dependency-name: prompt-toolkit dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 313e0ff9c..1bdcc82ba 100644 --- a/requirements.txt +++ b/requirements.txt @@ -47,7 +47,7 @@ psutil==5.9.4 colorama==0.4.6 # Building config files interactively questionary==1.10.0 -prompt-toolkit==3.0.33 +prompt-toolkit==3.0.36 # Extensions to datetime library python-dateutil==2.8.2 From a35111e55e55046504a922a952f90e091f28d49d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 03:00:54 +0000 Subject: [PATCH 32/49] Bump nbconvert from 7.2.5 to 7.2.6 Bumps [nbconvert](https://github.com/jupyter/nbconvert) from 7.2.5 to 7.2.6. - [Release notes](https://github.com/jupyter/nbconvert/releases) - [Changelog](https://github.com/jupyter/nbconvert/blob/main/CHANGELOG.md) - [Commits](https://github.com/jupyter/nbconvert/compare/v7.2.5...v7.2.6) --- updated-dependencies: - dependency-name: nbconvert dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 463d2656a..e36419f6c 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -23,7 +23,7 @@ time-machine==2.8.2 httpx==0.23.1 # Convert jupyter notebooks to markdown documents -nbconvert==7.2.5 +nbconvert==7.2.6 # mypy types types-cachetools==5.2.1 From 56256480115c142b00587c6734b78092d4027c3c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 03:01:07 +0000 Subject: [PATCH 33/49] Bump pytest-asyncio from 0.20.2 to 0.20.3 Bumps [pytest-asyncio](https://github.com/pytest-dev/pytest-asyncio) from 0.20.2 to 0.20.3. - [Release notes](https://github.com/pytest-dev/pytest-asyncio/releases) - [Changelog](https://github.com/pytest-dev/pytest-asyncio/blob/master/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest-asyncio/compare/v0.20.2...v0.20.3) --- updated-dependencies: - dependency-name: pytest-asyncio dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 463d2656a..843337c9b 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -12,7 +12,7 @@ flake8-tidy-imports==4.8.0 mypy==0.991 pre-commit==2.20.0 pytest==7.2.0 -pytest-asyncio==0.20.2 +pytest-asyncio==0.20.3 pytest-cov==4.0.0 pytest-mock==3.10.0 pytest-random-order==1.1.0 From 5a7b493d3ec9ccd557071ad70963041b31417bf7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 03:01:11 +0000 Subject: [PATCH 34/49] Bump xgboost from 1.7.1 to 1.7.2 Bumps [xgboost](https://github.com/dmlc/xgboost) from 1.7.1 to 1.7.2. - [Release notes](https://github.com/dmlc/xgboost/releases) - [Changelog](https://github.com/dmlc/xgboost/blob/master/NEWS.md) - [Commits](https://github.com/dmlc/xgboost/compare/v1.7.1...v1.7.2) --- updated-dependencies: - dependency-name: xgboost dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-freqai.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-freqai.txt b/requirements-freqai.txt index 66730e29f..215a312bf 100644 --- a/requirements-freqai.txt +++ b/requirements-freqai.txt @@ -7,5 +7,5 @@ scikit-learn==1.1.3 joblib==1.2.0 catboost==1.1.1; platform_machine != 'aarch64' lightgbm==3.3.3 -xgboost==1.7.1 +xgboost==1.7.2 tensorboard==2.11.0 From 0344203372c84be49a9bd7d3d55c3b3456ce877a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 03:01:32 +0000 Subject: [PATCH 35/49] Bump sqlalchemy from 1.4.44 to 1.4.45 Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 1.4.44 to 1.4.45. - [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] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 313e0ff9c..b36225aa6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,7 +7,7 @@ ccxt==2.2.67 cryptography==38.0.1; platform_machine == 'armv7l' cryptography==38.0.4; platform_machine != 'armv7l' aiohttp==3.8.3 -SQLAlchemy==1.4.44 +SQLAlchemy==1.4.45 python-telegram-bot==13.14 arrow==1.2.3 cachetools==4.2.2 From 2647c35f485406e50c6f8539510e66c83e20cc5b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 03:02:53 +0000 Subject: [PATCH 36/49] Bump pypa/gh-action-pypi-publish from 1.6.1 to 1.6.4 Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.6.1 to 1.6.4. - [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases) - [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/v1.6.1...v1.6.4) --- updated-dependencies: - dependency-name: pypa/gh-action-pypi-publish dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 273fb7ea0..b15451a64 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -410,7 +410,7 @@ jobs: python setup.py sdist bdist_wheel - name: Publish to PyPI (Test) - uses: pypa/gh-action-pypi-publish@v1.6.1 + uses: pypa/gh-action-pypi-publish@v1.6.4 if: (github.event_name == 'release') with: user: __token__ @@ -418,7 +418,7 @@ jobs: repository_url: https://test.pypi.org/legacy/ - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@v1.6.1 + uses: pypa/gh-action-pypi-publish@v1.6.4 if: (github.event_name == 'release') with: user: __token__ From bc2b9981d3dbc782f20ed6730f8f712e02d61594 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 05:30:55 +0000 Subject: [PATCH 37/49] Bump python-telegram-bot from 13.14 to 13.15 Bumps [python-telegram-bot](https://github.com/python-telegram-bot/python-telegram-bot) from 13.14 to 13.15. - [Release notes](https://github.com/python-telegram-bot/python-telegram-bot/releases) - [Changelog](https://github.com/python-telegram-bot/python-telegram-bot/blob/v13.15/CHANGES.rst) - [Commits](https://github.com/python-telegram-bot/python-telegram-bot/compare/v13.14...v13.15) --- updated-dependencies: - dependency-name: python-telegram-bot dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b36225aa6..3b572cce6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ cryptography==38.0.1; platform_machine == 'armv7l' cryptography==38.0.4; platform_machine != 'armv7l' aiohttp==3.8.3 SQLAlchemy==1.4.45 -python-telegram-bot==13.14 +python-telegram-bot==13.15 arrow==1.2.3 cachetools==4.2.2 requests==2.28.1 From 915e0ac62f940e0cb20484d582e8527b13488d3a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 05:31:01 +0000 Subject: [PATCH 38/49] Bump ccxt from 2.2.67 to 2.2.92 Bumps [ccxt](https://github.com/ccxt/ccxt) from 2.2.67 to 2.2.92. - [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/2.2.67...2.2.92) --- updated-dependencies: - dependency-name: ccxt dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b36225aa6..fff69ffac 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ numpy==1.23.5 pandas==1.5.2 pandas-ta==0.3.14b -ccxt==2.2.67 +ccxt==2.2.92 # Pin cryptography for now due to rust build errors with piwheels cryptography==38.0.1; platform_machine == 'armv7l' cryptography==38.0.4; platform_machine != 'armv7l' From de9784267a361ebb541fde4afaa23c8c6310a1fb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Dec 2022 05:39:16 +0000 Subject: [PATCH 39/49] Bump filelock from 3.8.0 to 3.8.2 Bumps [filelock](https://github.com/tox-dev/py-filelock) from 3.8.0 to 3.8.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.8.0...3.8.2) --- updated-dependencies: - dependency-name: filelock dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-hyperopt.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-hyperopt.txt b/requirements-hyperopt.txt index 83ba62240..8fc58812b 100644 --- a/requirements-hyperopt.txt +++ b/requirements-hyperopt.txt @@ -5,5 +5,5 @@ scipy==1.9.3 scikit-learn==1.2.0 scikit-optimize==0.9.0 -filelock==3.8.0 +filelock==3.8.2 progressbar2==4.2.0 From f9b7d35900b50cc786f8fee4943d5e301e3123b8 Mon Sep 17 00:00:00 2001 From: initrv Date: Mon, 12 Dec 2022 14:14:23 +0300 Subject: [PATCH 40/49] add increment param for tensorboard_log --- freqtrade/freqai/RL/BaseEnvironment.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/freqtrade/freqai/RL/BaseEnvironment.py b/freqtrade/freqai/RL/BaseEnvironment.py index 5a90d381e..5a5a950e7 100644 --- a/freqtrade/freqai/RL/BaseEnvironment.py +++ b/freqtrade/freqai/RL/BaseEnvironment.py @@ -139,7 +139,7 @@ class BaseEnvironment(gym.Env): self.np_random, seed = seeding.np_random(seed) return [seed] - def tensorboard_log(self, metric: str, inc: Union[int, float] = 1): + def tensorboard_log(self, metric: str, value: Union[int, float] = 1, inc: bool = True): """ Function builds the tensorboard_metrics dictionary to be parsed by the TensorboardCallback. This @@ -155,12 +155,13 @@ class BaseEnvironment(gym.Env): return -2 :param metric: metric to be tracked and incremented - :param inc: value to increment `metric` by + :param value: value to increment `metric` by + :param inc: sets whether the `value` is incremented or not """ - if metric not in self.tensorboard_metrics: - self.tensorboard_metrics[metric] = inc + if not inc or metric not in self.tensorboard_metrics: + self.tensorboard_metrics[metric] = value else: - self.tensorboard_metrics[metric] += inc + self.tensorboard_metrics[metric] += value def reset_tensorboard_log(self): self.tensorboard_metrics = {} From f940280d5e82d3574628af99f29d1fa0e2dd695a Mon Sep 17 00:00:00 2001 From: initrv Date: Mon, 12 Dec 2022 14:35:44 +0300 Subject: [PATCH 41/49] Fix tensorboard_log incrementing note --- docs/freqai-reinforcement-learning.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/freqai-reinforcement-learning.md b/docs/freqai-reinforcement-learning.md index b831c90a0..f3d6c97f8 100644 --- a/docs/freqai-reinforcement-learning.md +++ b/docs/freqai-reinforcement-learning.md @@ -270,7 +270,7 @@ FreqAI also provides a built in episodic summary logger called `self.tensorboard ``` !!! Note - The `self.tensorboard_log()` function is designed for tracking incremented objects only i.e. events, actions inside the training environment. If the event of interest is a float, the float can be passed as the second argument e.g. `self.tensorboard_log("float_metric1", 0.23)` would add 0.23 to `float_metric`. + The `self.tensorboard_log()` function is designed for tracking incremented objects only i.e. events, actions inside the training environment. If the event of interest is a float, the float can be passed as the second argument e.g. `self.tensorboard_log("float_metric1", 0.23)` would add 0.23 to `float_metric`. In this case you can also disable incrementing using `inc=False` parameter. ### Choosing a base environment From 5c984bf5c23d8e14e7d79f7a12848225f93fe410 Mon Sep 17 00:00:00 2001 From: Emre Date: Mon, 12 Dec 2022 21:33:12 +0300 Subject: [PATCH 42/49] Temporarily downgrade blosc for arm64 --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 5aba43edf..37f1d31e1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,7 +20,8 @@ tabulate==0.9.0 pycoingecko==3.1.0 jinja2==3.1.2 tables==3.7.0 -blosc==1.11.0 +blosc==1.10.6; platform_machine == 'arm64' +blosc==1.11.0; platform_machine != 'arm64' joblib==1.2.0 pyarrow==10.0.1; platform_machine != 'armv7l' From abc3badfb53cf3c5ba56258cd18c0c94517ab8e7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 12 Dec 2022 20:01:54 +0100 Subject: [PATCH 43/49] Improve shutdown behavior closes #7882 --- freqtrade/freqtradebot.py | 9 ++++++++- tests/test_freqtradebot.py | 12 ++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index f9cb28c28..f6c4a52bb 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -155,6 +155,8 @@ class FreqtradeBot(LoggingMixin): self.cancel_all_open_orders() self.check_for_open_trades() + except Exception as e: + logger.warning(f'Exception during cleanup: {e.__class__.__name__} {e}') finally: self.strategy.ft_bot_cleanup() @@ -162,8 +164,13 @@ class FreqtradeBot(LoggingMixin): self.rpc.cleanup() if self.emc: self.emc.shutdown() - Trade.commit() self.exchange.close() + try: + Trade.commit() + except Exception: + # Exeptions here will be happening if the db disappeared. + # At which point we can no longer commit anyway. + pass def startup(self) -> None: """ diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index b71b5b387..faaefcafb 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -88,6 +88,18 @@ def test_bot_cleanup(mocker, default_conf_usdt, caplog) -> None: assert coo_mock.call_count == 1 +def test_bot_cleanup_db_errors(mocker, default_conf_usdt, caplog) -> None: + mocker.patch('freqtrade.freqtradebot.Trade.commit', + side_effect=OperationalException()) + mocker.patch('freqtrade.freqtradebot.FreqtradeBot.check_for_open_trades', + side_effect=OperationalException()) + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) + freqtrade.emc = MagicMock() + freqtrade.emc.shutdown = MagicMock() + freqtrade.cleanup() + assert freqtrade.emc.shutdown.call_count == 1 + + @pytest.mark.parametrize('runmode', [ RunMode.DRY_RUN, RunMode.LIVE From c042d0146e29baee22b42487b9bdded223754b88 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 13 Dec 2022 17:14:11 +0000 Subject: [PATCH 44/49] Don't run gc_setup during tests --- tests/conftest.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/conftest.py b/tests/conftest.py index f3fc908e7..c9af5a171 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -408,6 +408,11 @@ def create_mock_trades_usdt(fee, is_short: Optional[bool] = False, use_db: bool Trade.commit() +@pytest.fixture(autouse=True) +def patch_gc(mocker) -> None: + mocker.patch("freqtrade.main.gc_set_threshold") + + @pytest.fixture(autouse=True) def patch_coingekko(mocker) -> None: """ From fed46d330ff4b8c2ed9aff97b311148f746bb99d Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 13 Dec 2022 18:14:56 +0100 Subject: [PATCH 45/49] Revert "Bump scikit-learn from 1.1.3 to 1.2.0" --- requirements-freqai.txt | 2 +- requirements-hyperopt.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements-freqai.txt b/requirements-freqai.txt index 57dd8dbb4..215a312bf 100644 --- a/requirements-freqai.txt +++ b/requirements-freqai.txt @@ -3,7 +3,7 @@ -r requirements-plot.txt # Required for freqai -scikit-learn==1.2.0 +scikit-learn==1.1.3 joblib==1.2.0 catboost==1.1.1; platform_machine != 'aarch64' lightgbm==3.3.3 diff --git a/requirements-hyperopt.txt b/requirements-hyperopt.txt index 8fc58812b..fcae2cbdd 100644 --- a/requirements-hyperopt.txt +++ b/requirements-hyperopt.txt @@ -3,7 +3,7 @@ # Required for hyperopt scipy==1.9.3 -scikit-learn==1.2.0 +scikit-learn==1.1.3 scikit-optimize==0.9.0 filelock==3.8.2 progressbar2==4.2.0 From 1d92db7805c1f13bafd61177a9f451e1b612751f Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 13 Dec 2022 19:23:37 +0100 Subject: [PATCH 46/49] Change CI to actually run one 2 randomized point. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b15451a64..0a787bc47 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -88,7 +88,7 @@ jobs: run: | 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 --print-all + freqtrade hyperopt --datadir tests/testdata -e 6 --strategy SampleStrategy --hyperopt-loss SharpeHyperOptLossDaily --print-all - name: Flake8 run: | From de19d1cfbba4f7ca0c356ed840093950c74f6434 Mon Sep 17 00:00:00 2001 From: initrv <37817561+initrv@users.noreply.github.com> Date: Wed, 14 Dec 2022 13:36:07 +0300 Subject: [PATCH 47/49] fix doc minimal_roi --- docs/strategy-customization.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index c006bf12c..0fb35ce89 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -363,9 +363,9 @@ class AwesomeStrategy(IStrategy): timeframe = "1d" timeframe_mins = timeframe_to_minutes(timeframe) minimal_roi = { - "0": 0.05, # 5% for the first 3 candles - str(timeframe_mins * 3)): 0.02, # 2% after 3 candles - str(timeframe_mins * 6)): 0.01, # 1% After 6 candles + "0": 0.05, # 5% for the first 3 candles + str(timeframe_mins * 3): 0.02, # 2% after 3 candles + str(timeframe_mins * 6): 0.01, # 1% After 6 candles } ``` From 33dce5cf1024aa506a0e57d8226136b0db434d81 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 15 Dec 2022 06:51:15 +0100 Subject: [PATCH 48/49] Clarify partial exit calculation messaging --- docs/strategy-callbacks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/strategy-callbacks.md b/docs/strategy-callbacks.md index 230968fb0..19bd26a04 100644 --- a/docs/strategy-callbacks.md +++ b/docs/strategy-callbacks.md @@ -773,7 +773,7 @@ class DigDeeperStrategy(IStrategy): * Sell 100@10\$ -> Avg price: 8.5\$, realized profit 150\$, 17.65% * Buy 150@11\$ -> Avg price: 10\$, realized profit 150\$, 17.65% * Sell 100@12\$ -> Avg price: 10\$, total realized profit 350\$, 20% - * Sell 150@14\$ -> Avg price: 10\$, total realized profit 950\$, 40% + * Sell 150@14\$ -> Avg price: 10\$, total realized profit 950\$, 40% <- *This will be the last "Exit" message* The total profit for this trade was 950$ on a 3350$ investment (`100@8$ + 100@9$ + 150@11$`). As such - the final relative profit is 28.35% (`950 / 3350`). From 7a0eadbdf5013c967d45c185da510c231e11dbe9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 15 Dec 2022 07:04:59 +0100 Subject: [PATCH 49/49] Don't recalc profit on closed trades --- freqtrade/rpc/rpc.py | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 334e18dc7..dae23d388 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -167,6 +167,7 @@ class RPC: results = [] for trade in trades: order: Optional[Order] = None + current_profit_fiat: Optional[float] = None if trade.open_order_id: order = trade.select_order_by_order_id(trade.open_order_id) # calculate profit and send message to user @@ -176,23 +177,26 @@ class RPC: trade.pair, side='exit', is_short=trade.is_short, refresh=False) except (ExchangeError, PricingError): current_rate = NAN + if len(trade.select_filled_orders(trade.entry_side)) > 0: + current_profit = trade.calc_profit_ratio( + current_rate) if not isnan(current_rate) else NAN + current_profit_abs = trade.calc_profit( + current_rate) if not isnan(current_rate) else NAN + else: + current_profit = current_profit_abs = current_profit_fiat = 0.0 else: + # Closed trade ... current_rate = trade.close_rate - if len(trade.select_filled_orders(trade.entry_side)) > 0: - current_profit = trade.calc_profit_ratio( - current_rate) if not isnan(current_rate) else NAN - current_profit_abs = trade.calc_profit( - current_rate) if not isnan(current_rate) else NAN - current_profit_fiat: Optional[float] = None - # Calculate fiat profit - if self._fiat_converter: - current_profit_fiat = self._fiat_converter.convert_amount( - current_profit_abs, - self._freqtrade.config['stake_currency'], - self._freqtrade.config['fiat_display_currency'] - ) - else: - current_profit = current_profit_abs = current_profit_fiat = 0.0 + current_profit = trade.close_profit + current_profit_abs = trade.close_profit_abs + + # Calculate fiat profit + if not isnan(current_profit_abs) and self._fiat_converter: + current_profit_fiat = self._fiat_converter.convert_amount( + current_profit_abs, + self._freqtrade.config['stake_currency'], + self._freqtrade.config['fiat_display_currency'] + ) # Calculate guaranteed profit (in case of trailing stop) stoploss_entry_dist = trade.calc_profit(trade.stop_loss)