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. 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 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 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 273fb7ea0..608565fdc 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: | @@ -148,6 +148,19 @@ jobs: if: runner.os == 'macOS' run: | brew update + # homebrew fails to update python due to unlinking failures + # https://github.com/actions/runner-images/issues/6817 + rm /usr/local/bin/2to3 || true + rm /usr/local/bin/2to3-3.11 || true + rm /usr/local/bin/idle3 || true + rm /usr/local/bin/idle3.11 || true + rm /usr/local/bin/pydoc3 || true + rm /usr/local/bin/pydoc3.11 || true + rm /usr/local/bin/python3 || true + rm /usr/local/bin/python3.11 || true + rm /usr/local/bin/python3-config || true + rm /usr/local/bin/python3.11-config || true + brew install hdf5 c-blosc python -m pip install --upgrade pip wheel export LD_LIBRARY_PATH=${HOME}/dependencies/lib:$LD_LIBRARY_PATH @@ -410,7 +423,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 +431,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__ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ccf9d5098..306e4bbda 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,9 +15,9 @@ repos: additional_dependencies: - types-cachetools==5.2.1 - types-filelock==3.2.7 - - types-requests==2.28.11.5 + - types-requests==2.28.11.7 - types-tabulate==0.9.0.0 - - types-python-dateutil==2.8.19.4 + - types-python-dateutil==2.8.19.5 # stages: [push] - repo: https://github.com/pycqa/isort diff --git a/README.md b/README.md index 30ad1ad2a..2ab62793d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # ![freqtrade](https://raw.githubusercontent.com/freqtrade/freqtrade/develop/docs/assets/freqtrade_poweredby.svg) [![Freqtrade CI](https://github.com/freqtrade/freqtrade/workflows/Freqtrade%20CI/badge.svg)](https://github.com/freqtrade/freqtrade/actions/) +[![DOI](https://joss.theoj.org/papers/10.21105/joss.04864/status.svg)](https://doi.org/10.21105/joss.04864) [![Coverage Status](https://coveralls.io/repos/github/freqtrade/freqtrade/badge.svg?branch=develop&service=github)](https://coveralls.io/github/freqtrade/freqtrade?branch=develop) [![Documentation](https://readthedocs.org/projects/freqtrade/badge/)](https://www.freqtrade.io) [![Maintainability](https://api.codeclimate.com/v1/badges/5737e6d668200b7518ff/maintainability)](https://codeclimate.com/github/freqtrade/freqtrade/maintainability) 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/backtesting.md b/docs/backtesting.md index bfe0f4d07..0227df3f6 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -300,7 +300,11 @@ A backtesting result will look like that: | Absolute profit | 0.00762792 BTC | | Total profit % | 76.2% | | CAGR % | 460.87% | +| Sortino | 1.88 | +| Sharpe | 2.97 | +| Calmar | 6.29 | | Profit factor | 1.11 | +| Expectancy | -0.15 | | Avg. stake amount | 0.001 BTC | | Total trade volume | 0.429 BTC | | | | @@ -400,7 +404,11 @@ It contains some useful key metrics about performance of your strategy on backte | Absolute profit | 0.00762792 BTC | | Total profit % | 76.2% | | CAGR % | 460.87% | +| Sortino | 1.88 | +| Sharpe | 2.97 | +| Calmar | 6.29 | | Profit factor | 1.11 | +| Expectancy | -0.15 | | Avg. stake amount | 0.001 BTC | | Total trade volume | 0.429 BTC | | | | @@ -447,6 +455,9 @@ It contains some useful key metrics about performance of your strategy on backte - `Absolute profit`: Profit made in stake currency. - `Total profit %`: Total profit. Aligned to the `TOTAL` row's `Tot Profit %` from the first table. Calculated as `(End capital − Starting capital) / Starting capital`. - `CAGR %`: Compound annual growth rate. +- `Sortino`: Annualized Sortino ratio. +- `Sharpe`: Annualized Sharpe ratio. +- `Calmar`: Annualized Calmar ratio. - `Profit factor`: profit / loss. - `Avg. stake amount`: Average stake amount, either `stake_amount` or the average when using dynamic stake amount. - `Total trade volume`: Volume generated on the exchange to reach the above profit. diff --git a/docs/configuration.md b/docs/configuration.md index 83b23425c..2113be692 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -11,7 +11,7 @@ Per default, the bot loads the configuration from the `config.json` file, locate You can specify a different configuration file used by the bot with the `-c/--config` command-line option. -If you used the [Quick start](installation.md/#quick-start) method for installing +If you used the [Quick start](docker_quickstart.md#docker-quick-start) method for installing the bot, the installation script should have already created the default configuration file (`config.json`) for you. If the default configuration file is not created we recommend to use `freqtrade new-config --config config.json` to generate a basic configuration file. diff --git a/docs/data-analysis.md b/docs/data-analysis.md index 7a6c6bb96..afee9049f 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. diff --git a/docs/docker_quickstart.md b/docs/docker_quickstart.md index 84c1d596a..89f737d71 100644 --- a/docs/docker_quickstart.md +++ b/docs/docker_quickstart.md @@ -4,20 +4,22 @@ 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-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 +33,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 +66,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 +86,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 +118,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 +168,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 +187,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 +196,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 diff --git a/docs/freqai-configuration.md b/docs/freqai-configuration.md index 5c3bbf90c..9d89800be 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 - }, + } } ``` @@ -46,116 +43,113 @@ The FreqAI strategy requires including the following lines of code in the standa def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: - # the model will return all labels created by user in `populate_any_indicators` + # the model will return all labels created by user in `set_freqai_labels()` # (& appended targets), an indication of whether or not the prediction should be accepted, # the target mean/std values for each of the labels created by user in - # `populate_any_indicators()` for each training period. + # `feature_engineering_*` for each training period. dataframe = self.freqai.start(dataframe, metadata, self) return dataframe - def populate_any_indicators( - self, pair, df, tf, informative=None, set_generalized_indicators=False - ): + def feature_engineering_expand_all(self, dataframe, period, **kwargs): """ - Function designed to automatically generate, name and merge features - from user indicated timeframes in the configuration file. User controls the indicators - passed to the training/prediction by prepending indicators with `'%-' + pair ` - (see convention below). I.e. user should not prepend any supporting metrics - (e.g. bb_lowerband below) with % unless they explicitly want to pass that metric to the - model. - :param pair: pair to be used as informative - :param df: strategy dataframe which will receive merges from informatives - :param tf: timeframe of the dataframe which will modify the feature names - :param informative: the dataframe associated with the informative pair + *Only functional with FreqAI enabled strategies* + This function will automatically expand the defined features on the config defined + `indicator_periods_candles`, `include_timeframes`, `include_shifted_candles`, and + `include_corr_pairs`. In other words, a single feature defined in this function + will automatically expand to a total of + `indicator_periods_candles` * `include_timeframes` * `include_shifted_candles` * + `include_corr_pairs` numbers of features added to the model. + + All features must be prepended with `%` to be recognized by FreqAI internals. + + :param df: strategy dataframe which will receive the features + :param period: period of the indicator - usage example: + dataframe["%-ema-period"] = ta.EMA(dataframe, timeperiod=period) """ - if informative is None: - informative = self.dp.get_pair_dataframe(pair, tf) + dataframe["%-rsi-period"] = ta.RSI(dataframe, timeperiod=period) + dataframe["%-mfi-period"] = ta.MFI(dataframe, timeperiod=period) + dataframe["%-adx-period"] = ta.ADX(dataframe, timeperiod=period) + dataframe["%-sma-period"] = ta.SMA(dataframe, timeperiod=period) + dataframe["%-ema-period"] = ta.EMA(dataframe, timeperiod=period) - # first loop is automatically duplicating indicators for time periods - for t in self.freqai_info["feature_parameters"]["indicator_periods_candles"]: - t = int(t) - informative[f"%-{pair}rsi-period_{t}"] = ta.RSI(informative, timeperiod=t) - informative[f"%-{pair}mfi-period_{t}"] = ta.MFI(informative, timeperiod=t) - informative[f"%-{pair}adx-period_{t}"] = ta.ADX(informative, window=t) + return dataframe - indicators = [col for col in informative if col.startswith("%")] - # This loop duplicates and shifts all indicators to add a sense of recency to data - for n in range(self.freqai_info["feature_parameters"]["include_shifted_candles"] + 1): - if n == 0: - continue - informative_shift = informative[indicators].shift(n) - informative_shift = informative_shift.add_suffix("_shift-" + str(n)) - informative = pd.concat((informative, informative_shift), axis=1) + def feature_engineering_expand_basic(self, dataframe, **kwargs): + """ + *Only functional with FreqAI enabled strategies* + This function will automatically expand the defined features on the config defined + `include_timeframes`, `include_shifted_candles`, and `include_corr_pairs`. + In other words, a single feature defined in this function + will automatically expand to a total of + `include_timeframes` * `include_shifted_candles` * `include_corr_pairs` + numbers of features added to the model. - df = merge_informative_pair(df, informative, self.config["timeframe"], tf, ffill=True) - skip_columns = [ - (s + "_" + tf) for s in ["date", "open", "high", "low", "close", "volume"] - ] - df = df.drop(columns=skip_columns) + Features defined here will *not* be automatically duplicated on user defined + `indicator_periods_candles` - # Add generalized indicators here (because in live, it will call this - # function to populate indicators during training). Notice how we ensure not to - # add them multiple times - if set_generalized_indicators: + All features must be prepended with `%` to be recognized by FreqAI internals. - # user adds targets here by prepending them with &- (see convention below) - # If user wishes to use multiple targets, a multioutput prediction model - # needs to be used such as templates/CatboostPredictionMultiModel.py - df["&-s_close"] = ( - df["close"] - .shift(-self.freqai_info["feature_parameters"]["label_period_candles"]) - .rolling(self.freqai_info["feature_parameters"]["label_period_candles"]) - .mean() - / df["close"] - - 1 + :param df: strategy dataframe which will receive the features + dataframe["%-pct-change"] = dataframe["close"].pct_change() + dataframe["%-ema-200"] = ta.EMA(dataframe, timeperiod=200) + """ + dataframe["%-pct-change"] = dataframe["close"].pct_change() + dataframe["%-raw_volume"] = dataframe["volume"] + dataframe["%-raw_price"] = dataframe["close"] + return dataframe + + def feature_engineering_standard(self, dataframe, **kwargs): + """ + *Only functional with FreqAI enabled strategies* + This optional function will be called once with the dataframe of the base timeframe. + This is the final function to be called, which means that the dataframe entering this + function will contain all the features and columns created by all other + freqai_feature_engineering_* functions. + + This function is a good place to do custom exotic feature extractions (e.g. tsfresh). + This function is a good place for any feature that should not be auto-expanded upon + (e.g. day of the week). + + All features must be prepended with `%` to be recognized by FreqAI internals. + + :param df: strategy dataframe which will receive the features + usage example: dataframe["%-day_of_week"] = (dataframe["date"].dt.dayofweek + 1) / 7 + """ + dataframe["%-day_of_week"] = (dataframe["date"].dt.dayofweek + 1) / 7 + dataframe["%-hour_of_day"] = (dataframe["date"].dt.hour + 1) / 25 + return dataframe + + def set_freqai_targets(self, dataframe, **kwargs): + """ + *Only functional with FreqAI enabled strategies* + Required function to set the targets for the model. + All targets must be prepended with `&` to be recognized by the FreqAI internals. + + :param df: strategy dataframe which will receive the targets + usage example: dataframe["&-target"] = dataframe["close"].shift(-1) / dataframe["close"] + """ + dataframe["&-s_close"] = ( + dataframe["close"] + .shift(-self.freqai_info["feature_parameters"]["label_period_candles"]) + .rolling(self.freqai_info["feature_parameters"]["label_period_candles"]) + .mean() + / dataframe["close"] + - 1 ) - - return df - - ``` -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`. +Notice how the `feature_engineering_*()` is where [features](freqai-feature-engineering.md#feature-engineering) are added. Meanwhile `set_freqai_targets()` adds the labels/targets. A full example strategy is available in `templates/FreqaiExampleStrategy.py`. !!! Note The `self.freqai.start()` function cannot be called outside the `populate_indicators()`. !!! Note - Features **must** be defined in `populate_any_indicators()`. Defining FreqAI features in `populate_indicators()` - will cause the algorithm to fail in live/dry mode. In order to add generalized features that are not associated with a specific pair or timeframe, the following structure inside `populate_any_indicators()` should be used - (as exemplified in `freqtrade/templates/FreqaiExampleStrategy.py`): - - ```python - def populate_any_indicators(self, pair, df, tf, informative=None, set_generalized_indicators=False): - - ... - - # Add generalized indicators here (because in live, it will call only this function to populate - # indicators for retraining). Notice how we ensure not to add them multiple times by associating - # these generalized indicators to the basepair/timeframe - if set_generalized_indicators: - df['%-day_of_week'] = (df["date"].dt.dayofweek + 1) / 7 - df['%-hour_of_day'] = (df['date'].dt.hour + 1) / 25 - - # user adds targets here by prepending them with &- (see convention below) - # If user wishes to use multiple targets, a multioutput prediction model - # needs to be used such as templates/CatboostPredictionMultiModel.py - df["&-s_close"] = ( - df["close"] - .shift(-self.freqai_info["feature_parameters"]["label_period_candles"]) - .rolling(self.freqai_info["feature_parameters"]["label_period_candles"]) - .mean() - / df["close"] - - 1 - ) - ``` - - Please see the example script located in `freqtrade/templates/FreqaiExampleStrategy.py` for a full example of `populate_any_indicators()`. + Features **must** be defined in `feature_engineering_*()`. Defining FreqAI features in `populate_indicators()` + will cause the algorithm to fail in live/dry mode. In order to add generalized features that are not associated with a specific pair or timeframe, you should use `feature_engineering_standard()` + (as exemplified in `freqtrade/templates/FreqaiExampleStrategy.py`). ## Important dataframe key patterns @@ -163,11 +157,11 @@ Below are the values you can expect to include/use inside a typical strategy dat | DataFrame Key | Description | |------------|-------------| -| `df['&*']` | Any dataframe column prepended with `&` in `populate_any_indicators()` is treated as a training target (label) inside FreqAI (typically following the naming convention `&-s*`). For example, to predict the close price 40 candles into the future, you would set `df['&-s_close'] = df['close'].shift(-self.freqai_info["feature_parameters"]["label_period_candles"])` with `"label_period_candles": 40` in the config. FreqAI makes the predictions and gives them back under the same key (`df['&-s_close']`) to be used in `populate_entry/exit_trend()`.
**Datatype:** Depends on the output of the model. +| `df['&*']` | Any dataframe column prepended with `&` in `set_freqai_targets()` is treated as a training target (label) inside FreqAI (typically following the naming convention `&-s*`). For example, to predict the close price 40 candles into the future, you would set `df['&-s_close'] = df['close'].shift(-self.freqai_info["feature_parameters"]["label_period_candles"])` with `"label_period_candles": 40` in the config. FreqAI makes the predictions and gives them back under the same key (`df['&-s_close']`) to be used in `populate_entry/exit_trend()`.
**Datatype:** Depends on the output of the model. | `df['&*_std/mean']` | Standard deviation and mean values of the defined labels during training (or live tracking with `fit_live_predictions_candles`). Commonly used to understand the rarity of a prediction (use the z-score as shown in `templates/FreqaiExampleStrategy.py` and explained [here](#creating-a-dynamic-target-threshold) to evaluate how often a particular prediction was observed during training or historically with `fit_live_predictions_candles`).
**Datatype:** Float. | `df['do_predict']` | Indication of an outlier data point. The return value is integer between -2 and 2, which lets you know if the prediction is trustworthy or not. `do_predict==1` means that the prediction is trustworthy. If the Dissimilarity Index (DI, see details [here](freqai-feature-engineering.md#identifying-outliers-with-the-dissimilarity-index-di)) of the input data point is above the threshold defined in the config, FreqAI will subtract 1 from `do_predict`, resulting in `do_predict==0`. If `use_SVM_to_remove_outliers()` is active, the Support Vector Machine (SVM, see details [here](freqai-feature-engineering.md#identifying-outliers-using-a-support-vector-machine-svm)) may also detect outliers in training and prediction data. In this case, the SVM will also subtract 1 from `do_predict`. If the input data point was considered an outlier by the SVM but not by the DI, or vice versa, the result will be `do_predict==0`. If both the DI and the SVM considers the input data point to be an outlier, the result will be `do_predict==-1`. As with the SVM, if `use_DBSCAN_to_remove_outliers` is active, DBSCAN (see details [here](freqai-feature-engineering.md#identifying-outliers-with-dbscan)) may also detect outliers and subtract 1 from `do_predict`. Hence, if both the SVM and DBSCAN are active and identify a datapoint that was above the DI threshold as an outlier, the result will be `do_predict==-2`. A particular case is when `do_predict == 2`, which means that the model has expired due to exceeding `expired_hours`.
**Datatype:** Integer between -2 and 2. | `df['DI_values']` | Dissimilarity Index (DI) values are proxies for the level of confidence FreqAI has in the prediction. A lower DI means the prediction is close to the training data, i.e., higher prediction confidence. See details about the DI [here](freqai-feature-engineering.md#identifying-outliers-with-the-dissimilarity-index-di).
**Datatype:** Float. -| `df['%*']` | Any dataframe column prepended with `%` in `populate_any_indicators()` is treated as a training feature. For example, you can include the RSI in the training feature set (similar to in `templates/FreqaiExampleStrategy.py`) by setting `df['%-rsi']`. See more details on how this is done [here](freqai-feature-engineering.md).
**Note:** Since the number of features prepended with `%` can multiply very quickly (10s of thousands of features are easily engineered using the multiplictative functionality of, e.g., `include_shifted_candles` and `include_timeframes` as described in the [parameter table](freqai-parameter-table.md)), these features are removed from the dataframe that is returned from FreqAI to the strategy. To keep a particular type of feature for plotting purposes, you would prepend it with `%%`.
**Datatype:** Depends on the output of the model. +| `df['%*']` | Any dataframe column prepended with `%` in `feature_engineering_*()` is treated as a training feature. For example, you can include the RSI in the training feature set (similar to in `templates/FreqaiExampleStrategy.py`) by setting `df['%-rsi']`. See more details on how this is done [here](freqai-feature-engineering.md).
**Note:** Since the number of features prepended with `%` can multiply very quickly (10s of thousands of features are easily engineered using the multiplictative functionality of, e.g., `include_shifted_candles` and `include_timeframes` as described in the [parameter table](freqai-parameter-table.md)), these features are removed from the dataframe that is returned from FreqAI to the strategy. To keep a particular type of feature for plotting purposes, you would prepend it with `%%`.
**Datatype:** Depends on the output of the model. ## Setting the `startup_candle_count` @@ -182,7 +176,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 +224,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/docs/freqai-feature-engineering.md b/docs/freqai-feature-engineering.md index 3462955cc..6c8c5bb46 100644 --- a/docs/freqai-feature-engineering.md +++ b/docs/freqai-feature-engineering.md @@ -2,96 +2,130 @@ ## Defining the features -Low level feature engineering is performed in the user strategy within a function called `populate_any_indicators()`. That function sets the `base features` such as, `RSI`, `MFI`, `EMA`, `SMA`, time of day, volume, etc. The `base features` can be custom indicators or they can be imported from any technical-analysis library that you can find. One important syntax rule is that all `base features` string names are prepended with `%-{pair}`, while labels/targets are prepended with `&`. +Low level feature engineering is performed in the user strategy within a set of functions called `feature_engineering_*`. These function set the `base features` such as, `RSI`, `MFI`, `EMA`, `SMA`, time of day, volume, etc. The `base features` can be custom indicators or they can be imported from any technical-analysis library that you can find. FreqAI is equipped with a set of functions to simplify rapid large-scale feature engineering: -!!! Note - Adding the full pair string, e.g. XYZ/USD, in the feature name enables improved performance for dataframe caching on the backend. If you decide *not* to add the full pair string in the feature string, FreqAI will operate in a reduced performance mode. +| Function | Description | +|---------------|-------------| +| `feature_engineering__expand_all()` | This optional function will automatically expand the defined features on the config defined `indicator_periods_candles`, `include_timeframes`, `include_shifted_candles`, and `include_corr_pairs`. +| `feature_engineering__expand_basic()` | This optional function will automatically expand the defined features on the config defined `include_timeframes`, `include_shifted_candles`, and `include_corr_pairs`. Note: this function does *not* expand across `include_periods_candles`. +| `feature_engineering_standard()` | This optional function will be called once with the dataframe of the base timeframe. This is the final function to be called, which means that the dataframe entering this function will contain all the features and columns from the base asset created by the other `feature_engineering_expand` functions. This function is a good place to do custom exotic feature extractions (e.g. tsfresh). This function is also a good place for any feature that should not be auto-expanded upon (e.g. day of the week). +| `set_freqai_targets()` | Required function to set the targets for the model. All targets must be prepended with `&` to be recognized by the FreqAI internals. Meanwhile, high level feature engineering is handled within `"feature_parameters":{}` in the FreqAI config. Within this file, it is possible to decide large scale feature expansions on top of the `base_features` such as "including correlated pairs" or "including informative timeframes" or even "including recent candles." -It is advisable to start from the template `populate_any_indicators()` in the source provided example strategy (found in `templates/FreqaiExampleStrategy.py`) to ensure that the feature definitions are following the correct conventions. Here is an example of how to set the indicators and labels in the strategy: +It is advisable to start from the template `feature_engineering_*` functions in the source provided example strategy (found in `templates/FreqaiExampleStrategy.py`) to ensure that the feature definitions are following the correct conventions. Here is an example of how to set the indicators and labels in the strategy: ```python - def populate_any_indicators( - self, pair, df, tf, informative=None, set_generalized_indicators=False - ): + def feature_engineering_expand_all(self, dataframe, period, **kwargs): """ - Function designed to automatically generate, name, and merge features - from user-indicated timeframes in the configuration file. The user controls the indicators - passed to the training/prediction by prepending indicators with `'%-' + pair ` - (see convention below). I.e., the user should not prepend any supporting metrics - (e.g., bb_lowerband below) with % unless they explicitly want to pass that metric to the - model. - :param pair: pair to be used as informative - :param df: strategy dataframe which will receive merges from informatives - :param tf: timeframe of the dataframe which will modify the feature names - :param informative: the dataframe associated with the informative pair + *Only functional with FreqAI enabled strategies* + This function will automatically expand the defined features on the config defined + `indicator_periods_candles`, `include_timeframes`, `include_shifted_candles`, and + `include_corr_pairs`. In other words, a single feature defined in this function + will automatically expand to a total of + `indicator_periods_candles` * `include_timeframes` * `include_shifted_candles` * + `include_corr_pairs` numbers of features added to the model. + + All features must be prepended with `%` to be recognized by FreqAI internals. + + :param df: strategy dataframe which will receive the features + :param period: period of the indicator - usage example: + dataframe["%-ema-period"] = ta.EMA(dataframe, timeperiod=period) """ - if informative is None: - informative = self.dp.get_pair_dataframe(pair, tf) + dataframe["%-rsi-period"] = ta.RSI(dataframe, timeperiod=period) + dataframe["%-mfi-period"] = ta.MFI(dataframe, timeperiod=period) + dataframe["%-adx-period"] = ta.ADX(dataframe, timeperiod=period) + dataframe["%-sma-period"] = ta.SMA(dataframe, timeperiod=period) + dataframe["%-ema-period"] = ta.EMA(dataframe, timeperiod=period) - # first loop is automatically duplicating indicators for time periods - for t in self.freqai_info["feature_parameters"]["indicator_periods_candles"]: - t = int(t) - informative[f"%-{pair}rsi-period_{t}"] = ta.RSI(informative, timeperiod=t) - informative[f"%-{pair}mfi-period_{t}"] = ta.MFI(informative, timeperiod=t) - informative[f"%-{pair}adx-period_{t}"] = ta.ADX(informative, window=t) + bollinger = qtpylib.bollinger_bands( + qtpylib.typical_price(dataframe), window=period, stds=2.2 + ) + dataframe["bb_lowerband-period"] = bollinger["lower"] + dataframe["bb_middleband-period"] = bollinger["mid"] + dataframe["bb_upperband-period"] = bollinger["upper"] - bollinger = qtpylib.bollinger_bands( - qtpylib.typical_price(informative), window=t, stds=2.2 + dataframe["%-bb_width-period"] = ( + dataframe["bb_upperband-period"] + - dataframe["bb_lowerband-period"] + ) / dataframe["bb_middleband-period"] + dataframe["%-close-bb_lower-period"] = ( + dataframe["close"] / dataframe["bb_lowerband-period"] + ) + + dataframe["%-roc-period"] = ta.ROC(dataframe, timeperiod=period) + + dataframe["%-relative_volume-period"] = ( + dataframe["volume"] / dataframe["volume"].rolling(period).mean() + ) + + return dataframe + + def feature_engineering_expand_basic(self, dataframe, **kwargs): + """ + *Only functional with FreqAI enabled strategies* + This function will automatically expand the defined features on the config defined + `include_timeframes`, `include_shifted_candles`, and `include_corr_pairs`. + In other words, a single feature defined in this function + will automatically expand to a total of + `include_timeframes` * `include_shifted_candles` * `include_corr_pairs` + numbers of features added to the model. + + Features defined here will *not* be automatically duplicated on user defined + `indicator_periods_candles` + + All features must be prepended with `%` to be recognized by FreqAI internals. + + :param df: strategy dataframe which will receive the features + dataframe["%-pct-change"] = dataframe["close"].pct_change() + dataframe["%-ema-200"] = ta.EMA(dataframe, timeperiod=200) + """ + dataframe["%-pct-change"] = dataframe["close"].pct_change() + dataframe["%-raw_volume"] = dataframe["volume"] + dataframe["%-raw_price"] = dataframe["close"] + return dataframe + + def feature_engineering_standard(self, dataframe, **kwargs): + """ + *Only functional with FreqAI enabled strategies* + This optional function will be called once with the dataframe of the base timeframe. + This is the final function to be called, which means that the dataframe entering this + function will contain all the features and columns created by all other + freqai_feature_engineering_* functions. + + This function is a good place to do custom exotic feature extractions (e.g. tsfresh). + This function is a good place for any feature that should not be auto-expanded upon + (e.g. day of the week). + + All features must be prepended with `%` to be recognized by FreqAI internals. + + :param df: strategy dataframe which will receive the features + usage example: dataframe["%-day_of_week"] = (dataframe["date"].dt.dayofweek + 1) / 7 + """ + dataframe["%-day_of_week"] = (dataframe["date"].dt.dayofweek + 1) / 7 + dataframe["%-hour_of_day"] = (dataframe["date"].dt.hour + 1) / 25 + return dataframe + + def set_freqai_targets(self, dataframe, **kwargs): + """ + *Only functional with FreqAI enabled strategies* + Required function to set the targets for the model. + All targets must be prepended with `&` to be recognized by the FreqAI internals. + + :param df: strategy dataframe which will receive the targets + usage example: dataframe["&-target"] = dataframe["close"].shift(-1) / dataframe["close"] + """ + dataframe["&-s_close"] = ( + dataframe["close"] + .shift(-self.freqai_info["feature_parameters"]["label_period_candles"]) + .rolling(self.freqai_info["feature_parameters"]["label_period_candles"]) + .mean() + / dataframe["close"] + - 1 ) - informative[f"{pair}bb_lowerband-period_{t}"] = bollinger["lower"] - informative[f"{pair}bb_middleband-period_{t}"] = bollinger["mid"] - informative[f"{pair}bb_upperband-period_{t}"] = bollinger["upper"] - - informative[f"%-{pair}bb_width-period_{t}"] = ( - informative[f"{pair}bb_upperband-period_{t}"] - - informative[f"{pair}bb_lowerband-period_{t}"] - ) / informative[f"{pair}bb_middleband-period_{t}"] - informative[f"%-{pair}close-bb_lower-period_{t}"] = ( - informative["close"] / informative[f"{pair}bb_lowerband-period_{t}"] - ) - - informative[f"%-{pair}relative_volume-period_{t}"] = ( - informative["volume"] / informative["volume"].rolling(t).mean() - ) - - indicators = [col for col in informative if col.startswith("%")] - # This loop duplicates and shifts all indicators to add a sense of recency to data - for n in range(self.freqai_info["feature_parameters"]["include_shifted_candles"] + 1): - if n == 0: - continue - informative_shift = informative[indicators].shift(n) - informative_shift = informative_shift.add_suffix("_shift-" + str(n)) - informative = pd.concat((informative, informative_shift), axis=1) - - df = merge_informative_pair(df, informative, self.config["timeframe"], tf, ffill=True) - skip_columns = [ - (s + "_" + tf) for s in ["date", "open", "high", "low", "close", "volume"] - ] - df = df.drop(columns=skip_columns) - - # Add generalized indicators here (because in live, it will call this - # function to populate indicators during training). Notice how we ensure not to - # add them multiple times - if set_generalized_indicators: - df["%-day_of_week"] = (df["date"].dt.dayofweek + 1) / 7 - df["%-hour_of_day"] = (df["date"].dt.hour + 1) / 25 - - # user adds targets here by prepending them with &- (see convention below) - # If user wishes to use multiple targets, a multioutput prediction model - # needs to be used such as templates/CatboostPredictionMultiModel.py - df["&-s_close"] = ( - df["close"] - .shift(-self.freqai_info["feature_parameters"]["label_period_candles"]) - .rolling(self.freqai_info["feature_parameters"]["label_period_candles"]) - .mean() - / df["close"] - - 1 - ) - - return df + + return dataframe ``` In the presented example, the user does not wish to pass the `bb_lowerband` as a feature to the model, @@ -118,13 +152,13 @@ After having defined the `base features`, the next step is to expand upon them u } ``` -The `include_timeframes` in the config above are the timeframes (`tf`) of each call to `populate_any_indicators()` in the strategy. In the presented case, the user is asking for the `5m`, `15m`, and `4h` timeframes of the `rsi`, `mfi`, `roc`, and `bb_width` to be included in the feature set. +The `include_timeframes` in the config above are the timeframes (`tf`) of each call to `feature_engineering_expand_*()` in the strategy. In the presented case, the user is asking for the `5m`, `15m`, and `4h` timeframes of the `rsi`, `mfi`, `roc`, and `bb_width` to be included in the feature set. -You can ask for each of the defined features to be included also for informative pairs using the `include_corr_pairlist`. This means that the feature set will include all the features from `populate_any_indicators` on all the `include_timeframes` for each of the correlated pairs defined in the config (`ETH/USD`, `LINK/USD`, and `BNB/USD` in the presented example). +You can ask for each of the defined features to be included also for informative pairs using the `include_corr_pairlist`. This means that the feature set will include all the features from `feature_engineering_expand_*()` on all the `include_timeframes` for each of the correlated pairs defined in the config (`ETH/USD`, `LINK/USD`, and `BNB/USD` in the presented example). `include_shifted_candles` indicates the number of previous candles to include in the feature set. For example, `include_shifted_candles: 2` tells FreqAI to include the past 2 candles for each of the features in the feature set. -In total, the number of features the user of the presented example strat has created is: length of `include_timeframes` * no. features in `populate_any_indicators()` * length of `include_corr_pairlist` * no. `include_shifted_candles` * length of `indicator_periods_candles` +In total, the number of features the user of the presented example strat has created is: length of `include_timeframes` * no. features in `feature_engineering_expand_*()` * length of `include_corr_pairlist` * no. `include_shifted_candles` * length of `indicator_periods_candles` $= 3 * 3 * 3 * 2 * 2 = 108$. ### Returning additional info from training diff --git a/docs/freqai-parameter-table.md b/docs/freqai-parameter-table.md index d05ce80f3..046fa8008 100644 --- a/docs/freqai-parameter-table.md +++ b/docs/freqai-parameter-table.md @@ -15,7 +15,7 @@ Mandatory parameters are marked as **Required** and have to be set in one of the | `identifier` | **Required.**
A unique ID for the current model. If models are saved to disk, the `identifier` allows for reloading specific pre-trained models/data.
**Datatype:** String. | `live_retrain_hours` | Frequency of retraining during dry/live runs.
**Datatype:** Float > 0.
Default: `0` (models retrain as often as possible). | `expiration_hours` | Avoid making predictions if a model is more than `expiration_hours` old.
**Datatype:** Positive integer.
Default: `0` (models never expire). -| `purge_old_models` | Delete obsolete models.
**Datatype:** Boolean.
Default: `False` (all historic models remain on disk). +| `purge_old_models` | Delete all unused models during live runs (not relevant to backtesting). If set to false (not default), dry/live runs will accumulate all unused models to disk. If
**Datatype:** Boolean.
Default: `True`. | `save_backtest_models` | Save models to disk when running backtesting. Backtesting operates most efficiently by saving the prediction data and reusing them directly for subsequent runs (when you wish to tune entry/exit parameters). Saving backtesting models to disk also allows to use the same model files for starting a dry/live instance with the same model `identifier`.
**Datatype:** Boolean.
Default: `False` (no models are saved). | `fit_live_predictions_candles` | Number of historical candles to use for computing target (label) statistics from prediction data, instead of from the training dataset (more information can be found [here](freqai-configuration.md#creating-a-dynamic-target-threshold)).
**Datatype:** Positive integer. | `follow_mode` | Use a `follower` that will look for models associated with a specific `identifier` and load those for inferencing. A `follower` will **not** train new models.
**Datatype:** Boolean.
Default: `False`. @@ -29,12 +29,12 @@ Mandatory parameters are marked as **Required** and have to be set in one of the |------------|-------------| | | **Feature parameters within the `freqai.feature_parameters` sub dictionary** | `feature_parameters` | A dictionary containing the parameters used to engineer the feature set. Details and examples are shown [here](freqai-feature-engineering.md).
**Datatype:** Dictionary. -| `include_timeframes` | A list of timeframes that all indicators in `populate_any_indicators` will be created for. The list is added as features to the base indicators dataset.
**Datatype:** List of timeframes (strings). -| `include_corr_pairlist` | A list of correlated coins that FreqAI will add as additional features to all `pair_whitelist` coins. All indicators set in `populate_any_indicators` during feature engineering (see details [here](freqai-feature-engineering.md)) will be created for each correlated coin. The correlated coins features are added to the base indicators dataset.
**Datatype:** List of assets (strings). -| `label_period_candles` | Number of candles into the future that the labels are created for. This is used in `populate_any_indicators` (see `templates/FreqaiExampleStrategy.py` for detailed usage). You can create custom labels and choose whether to make use of this parameter or not.
**Datatype:** Positive integer. +| `include_timeframes` | A list of timeframes that all indicators in `feature_engineering_expand_*()` will be created for. The list is added as features to the base indicators dataset.
**Datatype:** List of timeframes (strings). +| `include_corr_pairlist` | A list of correlated coins that FreqAI will add as additional features to all `pair_whitelist` coins. All indicators set in `feature_engineering_expand_*()` during feature engineering (see details [here](freqai-feature-engineering.md)) will be created for each correlated coin. The correlated coins features are added to the base indicators dataset.
**Datatype:** List of assets (strings). +| `label_period_candles` | Number of candles into the future that the labels are created for. This is used in `feature_engineering_expand_all()` (see `templates/FreqaiExampleStrategy.py` for detailed usage). You can create custom labels and choose whether to make use of this parameter or not.
**Datatype:** Positive integer. | `include_shifted_candles` | Add features from previous candles to subsequent candles with the intent of adding historical information. If used, FreqAI will duplicate and shift all features from the `include_shifted_candles` previous candles so that the information is available for the subsequent candle.
**Datatype:** Positive integer. | `weight_factor` | Weight training data points according to their recency (see details [here](freqai-feature-engineering.md#weighting-features-for-temporal-importance)).
**Datatype:** Positive float (typically < 1). -| `indicator_max_period_candles` | **No longer used (#7325)**. Replaced by `startup_candle_count` which is set in the [strategy](freqai-configuration.md#building-a-freqai-strategy). `startup_candle_count` is timeframe independent and defines the maximum *period* used in `populate_any_indicators()` for indicator creation. FreqAI uses this parameter together with the maximum timeframe in `include_time_frames` to calculate how many data points to download such that the first data point does not include a NaN.
**Datatype:** Positive integer. +| `indicator_max_period_candles` | **No longer used (#7325)**. Replaced by `startup_candle_count` which is set in the [strategy](freqai-configuration.md#building-a-freqai-strategy). `startup_candle_count` is timeframe independent and defines the maximum *period* used in `feature_engineering_*()` for indicator creation. FreqAI uses this parameter together with the maximum timeframe in `include_time_frames` to calculate how many data points to download such that the first data point does not include a NaN.
**Datatype:** Positive integer. | `indicator_periods_candles` | Time periods to calculate indicators for. The indicators are added to the base indicator dataset.
**Datatype:** List of positive integers. | `principal_component_analysis` | Automatically reduce the dimensionality of the data set using Principal Component Analysis. See details about how it works [here](#reducing-data-dimensionality-with-principal-component-analysis)
**Datatype:** Boolean.
Default: `False`. | `plot_feature_importances` | Create a feature importance plot for each model for the top/bottom `plot_feature_importances` number of features. Plot is stored in `user_data/models//sub-train-_.html`.
**Datatype:** Integer.
Default: `0`. diff --git a/docs/freqai-reinforcement-learning.md b/docs/freqai-reinforcement-learning.md index b1a212a92..4442a2f4f 100644 --- a/docs/freqai-reinforcement-learning.md +++ b/docs/freqai-reinforcement-learning.md @@ -34,65 +34,36 @@ Setting up and running a Reinforcement Learning model is the same as running a R freqtrade trade --freqaimodel ReinforcementLearner --strategy MyRLStrategy --config config.json ``` -where `ReinforcementLearner` will use the templated `ReinforcementLearner` from `freqai/prediction_models/ReinforcementLearner` (or a custom user defined one located in `user_data/freqaimodels`). The strategy, on the other hand, follows the same base [feature engineering](freqai-feature-engineering.md) with `populate_any_indicators` as a typical Regressor: +where `ReinforcementLearner` will use the templated `ReinforcementLearner` from `freqai/prediction_models/ReinforcementLearner` (or a custom user defined one located in `user_data/freqaimodels`). The strategy, on the other hand, follows the same base [feature engineering](freqai-feature-engineering.md) with `feature_engineering_*` as a typical Regressor. The difference lies in the creation of the targets, Reinforcement Learning doesnt require them. However, FreqAI requires a default (neutral) value to be set in the action column: ```python - def populate_any_indicators( - self, pair, df, tf, informative=None, set_generalized_indicators=False - ): + def set_freqai_targets(self, dataframe, **kwargs): + """ + *Only functional with FreqAI enabled strategies* + Required function to set the targets for the model. + All targets must be prepended with `&` to be recognized by the FreqAI internals. - if informative is None: - informative = self.dp.get_pair_dataframe(pair, tf) + More details about feature engineering available: - # first loop is automatically duplicating indicators for time periods - for t in self.freqai_info["feature_parameters"]["indicator_periods_candles"]: + https://www.freqtrade.io/en/latest/freqai-feature-engineering - t = int(t) - informative[f"%-{pair}rsi-period_{t}"] = ta.RSI(informative, timeperiod=t) - informative[f"%-{pair}mfi-period_{t}"] = ta.MFI(informative, timeperiod=t) - informative[f"%-{pair}adx-period_{t}"] = ta.ADX(informative, window=t) - - # The following raw price values are necessary for RL models - informative[f"%-{pair}raw_close"] = informative["close"] - informative[f"%-{pair}raw_open"] = informative["open"] - informative[f"%-{pair}raw_high"] = informative["high"] - informative[f"%-{pair}raw_low"] = informative["low"] - - indicators = [col for col in informative if col.startswith("%")] - # This loop duplicates and shifts all indicators to add a sense of recency to data - for n in range(self.freqai_info["feature_parameters"]["include_shifted_candles"] + 1): - if n == 0: - continue - informative_shift = informative[indicators].shift(n) - informative_shift = informative_shift.add_suffix("_shift-" + str(n)) - informative = pd.concat((informative, informative_shift), axis=1) - - df = merge_informative_pair(df, informative, self.config["timeframe"], tf, ffill=True) - skip_columns = [ - (s + "_" + tf) for s in ["date", "open", "high", "low", "close", "volume"] - ] - df = df.drop(columns=skip_columns) - - # Add generalized indicators here (because in live, it will call this - # function to populate indicators during training). Notice how we ensure not to - # add them multiple times - if set_generalized_indicators: - - # For RL, there are no direct targets to set. This is filler (neutral) - # until the agent sends an action. - df["&-action"] = 0 - - return df + :param df: strategy dataframe which will receive the targets + usage example: dataframe["&-target"] = dataframe["close"].shift(-1) / dataframe["close"] + """ + # For RL, there are no direct targets to set. This is filler (neutral) + # until the agent sends an action. + df["&-action"] = 0 ``` Most of the function remains the same as for typical Regressors, however, the function above shows how the strategy must pass the raw price data to the agent so that it has access to raw OHLCV in the training environment: ```python + def feature_engineering_standard(): # The following features are necessary for RL models - informative[f"%-{pair}raw_close"] = informative["close"] - informative[f"%-{pair}raw_open"] = informative["open"] - informative[f"%-{pair}raw_high"] = informative["high"] - informative[f"%-{pair}raw_low"] = informative["low"] + informative[f"%-raw_close"] = informative["close"] + informative[f"%-raw_open"] = informative["open"] + informative[f"%-raw_high"] = informative["high"] + informative[f"%-raw_low"] = informative["low"] ``` Finally, there is no explicit "label" to make - instead it is necessary to assign the `&-action` column which will contain the agent's actions when accessed in `populate_entry/exit_trends()`. In the present example, the neutral action to 0. This value should align with the environment used. FreqAI provides two environments, both use 0 as the neutral action. @@ -247,14 +218,40 @@ 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`. In this case you can also disable incrementing using `inc=False` parameter. + + ### 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: +FreqAI provides three base environments, `Base3ActionRLEnvironment`, `Base4ActionEnvironment` and `Base5ActionEnvironment`. As the names imply, the environments are customized for agents that can select from 3, 4 or 5 actions. The `Base3ActionEnvironment` is the simplest, the agent can select from hold, long, or short. This environment can also be used for long-only bots (it automatically follows the `can_short` flag from the strategy), where long is the enter condition and short is the exit condition. Meanwhile, in the `Base4ActionEnvironment`, the agent can enter long, enter short, hold neutral, or exit position. Finally, 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: * the actions available in the `calculate_reward` * the actions consumed by the user strategy -Both of the FreqAI provided environments inherit from an action/position agnostic environment object called the `BaseEnvironment`, which contains all shared logic. The architecture is designed to be easily customized. The simplest customization is the `calculate_reward()` (see details [here](#creating-a-custom-reward-function)). However, the customizations can be further extended into any of the functions inside the environment. You can do this by simply overriding those functions inside your `MyRLEnv` in the prediction model file. Or for more advanced customizations, it is encouraged to create an entirely new environment inherited from `BaseEnvironment`. +All of the FreqAI provided environments inherit from an action/position agnostic environment object called the `BaseEnvironment`, which contains all shared logic. The architecture is designed to be easily customized. The simplest customization is the `calculate_reward()` (see details [here](#creating-a-custom-reward-function)). However, the customizations can be further extended into any of the functions inside the environment. You can do this by simply overriding those functions inside your `MyRLEnv` in the prediction model file. Or for more advanced customizations, it is encouraged to create an entirely new environment inherited from `BaseEnvironment`. !!! Note - FreqAI does not provide by default, a long-only training environment. However, creating one should be as simple as copy-pasting one of the built in environments and removing the `short` actions (and all associated references to those). + Only the `Base3ActionRLEnv` can do long-only training/trading (set the user strategy attribute `can_short = False`). diff --git a/docs/freqai-running.md b/docs/freqai-running.md index b046e7bb8..a75e30e83 100644 --- a/docs/freqai-running.md +++ b/docs/freqai-running.md @@ -67,6 +67,10 @@ Backtesting mode requires [downloading the necessary data](#downloading-data-to- *want* to retrain a new model with the same config file, you should simply change the `identifier`. This way, you can return to using any model you wish by simply specifying the `identifier`. +!!! Note + Backtesting calls `set_freqai_targets()` one time for each backtest window (where the number of windows is the full backtest timerange divided by the `backtest_period_days` parameter). Doing this means that the targets simulate dry/live behavior without look ahead bias. However, the definition of the features in `feature_engineering_*()` is performed once on the entire backtest timerange. This means that you should be sure that features do look-ahead into the future. + More details about look-ahead bias can be found in [Common Mistakes](strategy-customization.md#common-mistakes-when-developing-strategies). + --- ### Saving prediction data @@ -135,7 +139,7 @@ freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --strategy FreqaiExampleSt `hyperopt` requires you to have the data pre-downloaded in the same fashion as if you were doing [backtesting](#backtesting). In addition, you must consider some restrictions when trying to hyperopt FreqAI strategies: - The `--analyze-per-epoch` hyperopt parameter is not compatible with FreqAI. -- It's not possible to hyperopt indicators in the `populate_any_indicators()` function. This means that you cannot optimize model parameters using hyperopt. Apart from this exception, it is possible to optimize all other [spaces](hyperopt.md#running-hyperopt-with-smaller-search-space). +- It's not possible to hyperopt indicators in the `feature_engineering_*()` and `set_freqai_targets()` functions. This means that you cannot optimize model parameters using hyperopt. Apart from this exception, it is possible to optimize all other [spaces](hyperopt.md#running-hyperopt-with-smaller-search-space). - The backtesting instructions also apply to hyperopt. The best method for combining hyperopt and FreqAI is to focus on hyperopting entry/exit thresholds/criteria. You need to focus on hyperopting parameters that are not used in your features. For example, you should not try to hyperopt rolling window lengths in the feature creation, or any part of the FreqAI config which changes predictions. In order to efficiently hyperopt the FreqAI strategy, FreqAI stores predictions as dataframes and reuses them. Hence the requirement to hyperopt entry/exit thresholds/criteria only. diff --git a/docs/freqai.md b/docs/freqai.md index efa279704..d13d43f66 100644 --- a/docs/freqai.md +++ b/docs/freqai.md @@ -72,11 +72,25 @@ pip install -r requirements-freqai.txt If you are using docker, a dedicated tag with FreqAI dependencies is available as `:freqai`. As such - you can replace the image line in your docker-compose file with `image: freqtradeorg/freqtrade:develop_freqai`. This image contains the regular FreqAI dependencies. Similar to native installs, Catboost will not be available on ARM based devices. - ### FreqAI position in open-source machine learning landscape Forecasting chaotic time-series based systems, such as equity/cryptocurrency markets, requires a broad set of tools geared toward testing a wide range of hypotheses. Fortunately, a recent maturation of robust machine learning libraries (e.g. `scikit-learn`) has opened up a wide range of research possibilities. Scientists from a diverse range of fields can now easily prototype their studies on an abundance of established machine learning algorithms. Similarly, these user-friendly libraries enable "citzen scientists" to use their basic Python skills for data-exploration. However, leveraging these machine learning libraries on historical and live chaotic data sources can be logistically difficult and expensive. Additionally, robust data-collection, storage, and handling presents a disparate challenge. [`FreqAI`](#freqai) aims to provide a generalized and extensible open-sourced framework geared toward live deployments of adaptive modeling for market forecasting. The `FreqAI` framework is effectively a sandbox for the rich world of open-source machine learning libraries. Inside the `FreqAI` sandbox, users find they can combine a wide variety of third-party libraries to test creative hypotheses on a free live 24/7 chaotic data source - cryptocurrency exchange data. +### Citing FreqAI + +FreqAI is [published in the Journal of Open Source Software](https://joss.theoj.org/papers/10.21105/joss.04864). If you find FreqAI useful in your research, please use the following citation: + +```bibtex +@article{Caulk2022, + doi = {10.21105/joss.04864}, + url = {https://doi.org/10.21105/joss.04864}, + year = {2022}, publisher = {The Open Journal}, + volume = {7}, number = {80}, pages = {4864}, + author = {Robert A. Caulk and Elin Törnquist and Matthias Voppichler and Andrew R. Lawless and Ryan McMullan and Wagner Costa Santos and Timothy C. Pogue and Johan van der Vlugt and Stefan P. Gehring and Pascal Schmidt}, + title = {FreqAI: generalizing adaptive modeling for chaotic time-series market forecasts}, + journal = {Journal of Open Source Software} } +``` + ## Common pitfalls FreqAI cannot be combined with dynamic `VolumePairlists` (or any pairlist filter that adds and removes pairs dynamically). @@ -99,6 +113,8 @@ Code review and software architecture brainstorming: Software development: Wagner Costa @wagnercosta +Emre Suzen @aemr3 +Timothy Pogue @wizrds Beta testing and bug reporting: -Stefan Gehring @bloodhunter4rc, @longyu, Andrew Lawless @paranoidandy, Pascal Schmidt @smidelis, Ryan McMullan @smarmau, Juha Nykänen @suikula, Johan van der Vlugt @jooopiert, Richárd Józsa @richardjosza, Timothy Pogue @wizrds +Stefan Gehring @bloodhunter4rc, @longyu, Andrew Lawless @paranoidandy, Pascal Schmidt @smidelis, Ryan McMullan @smarmau, Juha Nykänen @suikula, Johan van der Vlugt @jooopiert, Richárd Józsa @richardjosza diff --git a/docs/hyperopt.md b/docs/hyperopt.md index 6b6c2a772..e72b850ca 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -365,7 +365,7 @@ class MyAwesomeStrategy(IStrategy): timeframe = '15m' minimal_roi = { "0": 0.10 - }, + } # Define the parameter spaces buy_ema_short = IntParameter(3, 50, default=5) buy_ema_long = IntParameter(15, 200, default=50) @@ -400,7 +400,7 @@ class MyAwesomeStrategy(IStrategy): return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: - conditions = [] + conditions = [] conditions.append(qtpylib.crossed_above( dataframe[f'ema_long_{self.buy_ema_long.value}'], dataframe[f'ema_short_{self.buy_ema_short.value}'] )) diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index d61718c7d..5fda038bd 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -23,6 +23,7 @@ You may also use something like `.*DOWN/BTC` or `.*UP/BTC` to exclude leveraged * [`StaticPairList`](#static-pair-list) (default, if not configured differently) * [`VolumePairList`](#volume-pair-list) * [`ProducerPairList`](#producerpairlist) +* [`RemotePairList`](#remotepairlist) * [`AgeFilter`](#agefilter) * [`OffsetFilter`](#offsetfilter) * [`PerformanceFilter`](#performancefilter) @@ -173,6 +174,48 @@ You can limit the length of the pairlist with the optional parameter `number_ass `ProducerPairList` can also be used multiple times in sequence, combining the pairs from multiple producers. Obviously in complex such configurations, the Producer may not provide data for all pairs, so the strategy must be fit for this. +#### RemotePairList + +It allows the user to fetch a pairlist from a remote server or a locally stored json file within the freqtrade directory, enabling dynamic updates and customization of the trading pairlist. + +The RemotePairList is defined in the pairlists section of the configuration settings. It uses the following configuration options: + +```json +"pairlists": [ + { + "method": "RemotePairList", + "pairlist_url": "https://example.com/pairlist", + "number_assets": 10, + "refresh_period": 1800, + "keep_pairlist_on_failure": true, + "read_timeout": 60, + "bearer_token": "my-bearer-token" + } +] +``` + +The `pairlist_url` option specifies the URL of the remote server where the pairlist is located, or the path to a local file (if file:/// is prepended). This allows the user to use either a remote server or a local file as the source for the pairlist. + +The user is responsible for providing a server or local file that returns a JSON object with the following structure: + +```json +{ + "pairs": ["XRP/USDT", "ETH/USDT", "LTC/USDT"], + "refresh_period": 1800, +} +``` + +The `pairs` property should contain a list of strings with the trading pairs to be used by the bot. The `refresh_period` property is optional and specifies the number of seconds that the pairlist should be cached before being refreshed. + +The optional `keep_pairlist_on_failure` specifies whether the previous received pairlist should be used if the remote server is not reachable or returns an error. The default value is true. + +The optional `read_timeout` specifies the maximum amount of time (in seconds) to wait for a response from the remote source, The default value is 60. + +The optional `bearer_token` will be included in the requests Authorization Header. + +!!! Note + In case of a server error the last received pairlist will be kept if `keep_pairlist_on_failure` is set to true, when set to false a empty pairlist is returned. + #### AgeFilter Removes pairs that have been listed on the exchange for less than `min_days_listed` days (defaults to `10`) or more than `max_days_listed` days (defaults `None` mean infinity). diff --git a/docs/index.md b/docs/index.md index 5459eac1b..40b9e98ad 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,6 +1,7 @@ ![freqtrade](assets/freqtrade_poweredby.svg) [![Freqtrade CI](https://github.com/freqtrade/freqtrade/workflows/Freqtrade%20CI/badge.svg)](https://github.com/freqtrade/freqtrade/actions/) +[![DOI](https://joss.theoj.org/papers/10.21105/joss.04864/status.svg)](https://doi.org/10.21105/joss.04864) [![Coverage Status](https://coveralls.io/repos/github/freqtrade/freqtrade/badge.svg?branch=develop&service=github)](https://coveralls.io/github/freqtrade/freqtrade?branch=develop) [![Maintainability](https://api.codeclimate.com/v1/badges/5737e6d668200b7518ff/maintainability)](https://codeclimate.com/github/freqtrade/freqtrade/maintainability) diff --git a/docs/leverage.md b/docs/leverage.md index 429aff86c..0a265277e 100644 --- a/docs/leverage.md +++ b/docs/leverage.md @@ -92,6 +92,8 @@ One account is used to share collateral between markets (trading pairs). Margin "margin_mode": "cross" ``` +Please read the [exchange specific notes](exchanges.md) for exchanges that support this mode and how they differ. + ## Set leverage to use Different strategies and risk profiles will require different levels of leverage. diff --git a/docs/overrides/main.html b/docs/overrides/main.html index dfc5264be..cba627ead 100644 --- a/docs/overrides/main.html +++ b/docs/overrides/main.html @@ -11,9 +11,6 @@ {% endif %}