Merge branch 'feat/short' into lev-exchange

This commit is contained in:
Sam Germain 2021-09-14 18:27:51 -06:00
commit 47677ccd91
38 changed files with 166 additions and 1730 deletions

View File

@ -87,7 +87,7 @@ jobs:
run: | run: |
cp config_examples/config_bittrex.example.json config.json cp config_examples/config_bittrex.example.json config.json
freqtrade create-userdir --userdir user_data freqtrade create-userdir --userdir user_data
freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt SampleHyperOpt --hyperopt-loss SharpeHyperOptLossDaily --print-all freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt-loss SharpeHyperOptLossDaily --print-all
- name: Flake8 - name: Flake8
run: | run: |
@ -180,7 +180,7 @@ jobs:
run: | run: |
cp config_examples/config_bittrex.example.json config.json cp config_examples/config_bittrex.example.json config.json
freqtrade create-userdir --userdir user_data freqtrade create-userdir --userdir user_data
freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt SampleHyperOpt --hyperopt-loss SharpeHyperOptLossDaily --print-all freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt-loss SharpeHyperOptLossDaily --print-all
- name: Flake8 - name: Flake8
run: | run: |
@ -247,7 +247,7 @@ jobs:
run: | run: |
cp config_examples/config_bittrex.example.json config.json cp config_examples/config_bittrex.example.json config.json
freqtrade create-userdir --userdir user_data freqtrade create-userdir --userdir user_data
freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt SampleHyperOpt --hyperopt-loss SharpeHyperOptLossDaily --print-all freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt-loss SharpeHyperOptLossDaily --print-all
- name: Flake8 - name: Flake8
run: | run: |

View File

@ -33,7 +33,7 @@ jobs:
- script: - script:
- cp config_examples/config_bittrex.example.json config.json - cp config_examples/config_bittrex.example.json config.json
- freqtrade create-userdir --userdir user_data - freqtrade create-userdir --userdir user_data
- freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt SampleHyperOpt --hyperopt-loss SharpeHyperOptLossDaily - freqtrade hyperopt --datadir tests/testdata -e 5 --strategy SampleStrategy --hyperopt-loss SharpeHyperOptLossDaily
name: hyperopt name: hyperopt
- script: flake8 - script: flake8
name: flake8 name: flake8

View File

@ -79,22 +79,22 @@ For any other type of installation please refer to [Installation doc](https://ww
``` ```
usage: freqtrade [-h] [-V] usage: freqtrade [-h] [-V]
{trade,create-userdir,new-config,new-hyperopt,new-strategy,download-data,convert-data,convert-trade-data,backtesting,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-hyperopts,list-markets,list-pairs,list-strategies,list-timeframes,show-trades,test-pairlist,plot-dataframe,plot-profit} {trade,create-userdir,new-config,new-strategy,download-data,convert-data,convert-trade-data,list-data,backtesting,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-hyperopts,list-markets,list-pairs,list-strategies,list-timeframes,show-trades,test-pairlist,install-ui,plot-dataframe,plot-profit,webserver}
... ...
Free, open source crypto trading bot Free, open source crypto trading bot
positional arguments: positional arguments:
{trade,create-userdir,new-config,new-hyperopt,new-strategy,download-data,convert-data,convert-trade-data,backtesting,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-hyperopts,list-markets,list-pairs,list-strategies,list-timeframes,show-trades,test-pairlist,plot-dataframe,plot-profit} {trade,create-userdir,new-config,new-strategy,download-data,convert-data,convert-trade-data,list-data,backtesting,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-hyperopts,list-markets,list-pairs,list-strategies,list-timeframes,show-trades,test-pairlist,install-ui,plot-dataframe,plot-profit,webserver}
trade Trade module. trade Trade module.
create-userdir Create user-data directory. create-userdir Create user-data directory.
new-config Create new config new-config Create new config
new-hyperopt Create new hyperopt
new-strategy Create new strategy new-strategy Create new strategy
download-data Download backtesting data. download-data Download backtesting data.
convert-data Convert candle (OHLCV) data from one format to convert-data Convert candle (OHLCV) data from one format to
another. another.
convert-trade-data Convert trade data from one format to another. convert-trade-data Convert trade data from one format to another.
list-data List downloaded data.
backtesting Backtesting module. backtesting Backtesting module.
edge Edge module. edge Edge module.
hyperopt Hyperopt module. hyperopt Hyperopt module.
@ -108,8 +108,10 @@ positional arguments:
list-timeframes Print available timeframes for the exchange. list-timeframes Print available timeframes for the exchange.
show-trades Show trades. show-trades Show trades.
test-pairlist Test your pairlist configuration. test-pairlist Test your pairlist configuration.
install-ui Install FreqUI
plot-dataframe Plot candles with indicators. plot-dataframe Plot candles with indicators.
plot-profit Generate plot showing profits. plot-profit Generate plot showing profits.
webserver Webserver module.
optional arguments: optional arguments:
-h, --help show this help message and exit -h, --help show this help message and exit

View File

@ -67,10 +67,10 @@ Currently, the arguments are:
This function needs to return a floating point number (`float`). Smaller numbers will be interpreted as better results. The parameters and balancing for this is up to you. This function needs to return a floating point number (`float`). Smaller numbers will be interpreted as better results. The parameters and balancing for this is up to you.
!!! Note !!! Note
This function is called once per iteration - so please make sure to have this as optimized as possible to not slow hyperopt down unnecessarily. This function is called once per epoch - so please make sure to have this as optimized as possible to not slow hyperopt down unnecessarily.
!!! Note !!! Note "`*args` and `**kwargs`"
Please keep the arguments `*args` and `**kwargs` in the interface to allow us to extend this interface later. Please keep the arguments `*args` and `**kwargs` in the interface to allow us to extend this interface in the future.
## Overriding pre-defined spaces ## Overriding pre-defined spaces
@ -82,8 +82,22 @@ class MyAwesomeStrategy(IStrategy):
# Define a custom stoploss space. # Define a custom stoploss space.
def stoploss_space(): def stoploss_space():
return [SKDecimal(-0.05, -0.01, decimals=3, name='stoploss')] return [SKDecimal(-0.05, -0.01, decimals=3, name='stoploss')]
# Define custom ROI space
def roi_space() -> List[Dimension]:
return [
Integer(10, 120, name='roi_t1'),
Integer(10, 60, name='roi_t2'),
Integer(10, 40, name='roi_t3'),
SKDecimal(0.01, 0.04, decimals=3, name='roi_p1'),
SKDecimal(0.01, 0.07, decimals=3, name='roi_p2'),
SKDecimal(0.01, 0.20, decimals=3, name='roi_p3'),
]
``` ```
!!! Note
All overrides are optional and can be mixed/matched as necessary.
## Space options ## Space options
For the additional spaces, scikit-optimize (in combination with Freqtrade) provides the following space types: For the additional spaces, scikit-optimize (in combination with Freqtrade) provides the following space types:
@ -105,281 +119,3 @@ from freqtrade.optimize.space import Categorical, Dimension, Integer, SKDecimal,
Assuming the definition of a rather small space (`SKDecimal(0.10, 0.15, decimals=2, name='xxx')`) - SKDecimal will have 5 possibilities (`[0.10, 0.11, 0.12, 0.13, 0.14, 0.15]`). Assuming the definition of a rather small space (`SKDecimal(0.10, 0.15, decimals=2, name='xxx')`) - SKDecimal will have 5 possibilities (`[0.10, 0.11, 0.12, 0.13, 0.14, 0.15]`).
A corresponding real space `Real(0.10, 0.15 name='xxx')` on the other hand has an almost unlimited number of possibilities (`[0.10, 0.010000000001, 0.010000000002, ... 0.014999999999, 0.01500000000]`). A corresponding real space `Real(0.10, 0.15 name='xxx')` on the other hand has an almost unlimited number of possibilities (`[0.10, 0.010000000001, 0.010000000002, ... 0.014999999999, 0.01500000000]`).
---
## Legacy Hyperopt
This Section explains the configuration of an explicit Hyperopt file (separate to the strategy).
!!! Warning "Deprecated / legacy mode"
Since the 2021.4 release you no longer have to write a separate hyperopt class, but all strategies can be hyperopted.
Please read the [main hyperopt page](hyperopt.md) for more details.
### Prepare hyperopt file
Configuring an explicit hyperopt file is similar to writing your own strategy, and many tasks will be similar.
!!! Tip "About this page"
For this page, we will be using a fictional strategy called `AwesomeStrategy` - which will be optimized using the `AwesomeHyperopt` class.
#### Create a Custom Hyperopt File
The simplest way to get started is to use the following command, which will create a new hyperopt file from a template, which will be located under `user_data/hyperopts/AwesomeHyperopt.py`.
Let assume you want a hyperopt file `AwesomeHyperopt.py`:
``` bash
freqtrade new-hyperopt --hyperopt AwesomeHyperopt
```
#### Legacy Hyperopt checklist
Checklist on all tasks / possibilities in hyperopt
Depending on the space you want to optimize, only some of the below are required:
* fill `buy_strategy_generator` - for buy signal optimization
* fill `indicator_space` - for buy signal optimization
* fill `sell_strategy_generator` - for sell signal optimization
* fill `sell_indicator_space` - for sell signal optimization
!!! Note
`populate_indicators` needs to create all indicators any of thee spaces may use, otherwise hyperopt will not work.
Optional in hyperopt - can also be loaded from a strategy (recommended):
* `populate_indicators` - fallback to create indicators
* `populate_buy_trend` - fallback if not optimizing for buy space. should come from strategy
* `populate_sell_trend` - fallback if not optimizing for sell space. should come from strategy
!!! Note
You always have to provide a strategy to Hyperopt, even if your custom Hyperopt class contains all methods.
Assuming the optional methods are not in your hyperopt file, please use `--strategy AweSomeStrategy` which contains these methods so hyperopt can use these methods instead.
Rarely you may also need to override:
* `roi_space` - for custom ROI optimization (if you need the ranges for the ROI parameters in the optimization hyperspace that differ from default)
* `generate_roi_table` - for custom ROI optimization (if you need the ranges for the values in the ROI table that differ from default or the number of entries (steps) in the ROI table which differs from the default 4 steps)
* `stoploss_space` - for custom stoploss optimization (if you need the range for the stoploss parameter in the optimization hyperspace that differs from default)
* `trailing_space` - for custom trailing stop optimization (if you need the ranges for the trailing stop parameters in the optimization hyperspace that differ from default)
#### Defining a buy signal optimization
Let's say you are curious: should you use MACD crossings or lower Bollinger
Bands to trigger your buys. And you also wonder should you use RSI or ADX to
help with those buy decisions. If you decide to use RSI or ADX, which values
should I use for them? So let's use hyperparameter optimization to solve this
mystery.
We will start by defining a search space:
```python
def indicator_space() -> List[Dimension]:
"""
Define your Hyperopt space for searching strategy parameters
"""
return [
Integer(20, 40, name='adx-value'),
Integer(20, 40, name='rsi-value'),
Categorical([True, False], name='adx-enabled'),
Categorical([True, False], name='rsi-enabled'),
Categorical(['bb_lower', 'macd_cross_signal'], name='trigger')
]
```
Above definition says: I have five parameters I want you to randomly combine
to find the best combination. Two of them are integer values (`adx-value` and `rsi-value`) and I want you test in the range of values 20 to 40.
Then we have three category variables. First two are either `True` or `False`.
We use these to either enable or disable the ADX and RSI guards.
The last one we call `trigger` and use it to decide which buy trigger we want to use.
So let's write the buy strategy generator using these values:
```python
@staticmethod
def buy_strategy_generator(params: Dict[str, Any]) -> Callable:
"""
Define the buy strategy parameters to be used by Hyperopt.
"""
def populate_buy_trend(dataframe: DataFrame, metadata: dict) -> DataFrame:
conditions = []
# GUARDS AND TRENDS
if 'adx-enabled' in params and params['adx-enabled']:
conditions.append(dataframe['adx'] > params['adx-value'])
if 'rsi-enabled' in params and params['rsi-enabled']:
conditions.append(dataframe['rsi'] < params['rsi-value'])
# TRIGGERS
if 'trigger' in params:
if params['trigger'] == 'bb_lower':
conditions.append(dataframe['close'] < dataframe['bb_lowerband'])
if params['trigger'] == 'macd_cross_signal':
conditions.append(qtpylib.crossed_above(
dataframe['macd'], dataframe['macdsignal']
))
# Check that volume is not 0
conditions.append(dataframe['volume'] > 0)
if conditions:
dataframe.loc[
reduce(lambda x, y: x & y, conditions),
'buy'] = 1
return dataframe
return populate_buy_trend
```
Hyperopt will now call `populate_buy_trend()` many times (`epochs`) with different value combinations.
It will use the given historical data and make buys based on the buy signals generated with the above function.
Based on the results, hyperopt will tell you which parameter combination produced the best results (based on the configured [loss function](#loss-functions)).
!!! Note
The above setup expects to find ADX, RSI and Bollinger Bands in the populated indicators.
When you want to test an indicator that isn't used by the bot currently, remember to
add it to the `populate_indicators()` method in your strategy or hyperopt file.
#### Sell optimization
Similar to the buy-signal above, sell-signals can also be optimized.
Place the corresponding settings into the following methods
* Inside `sell_indicator_space()` - the parameters hyperopt shall be optimizing.
* Within `sell_strategy_generator()` - populate the nested method `populate_sell_trend()` to apply the parameters.
The configuration and rules are the same than for buy signals.
To avoid naming collisions in the search-space, please prefix all sell-spaces with `sell-`.
### Execute Hyperopt
Once you have updated your hyperopt configuration you can run it.
Because hyperopt tries a lot of combinations to find the best parameters it will take time to get a good result. More time usually results in better results.
We strongly recommend to use `screen` or `tmux` to prevent any connection loss.
```bash
freqtrade hyperopt --config config.json --hyperopt <hyperoptname> --hyperopt-loss <hyperoptlossname> --strategy <strategyname> -e 500 --spaces all
```
Use `<hyperoptname>` as the name of the custom hyperopt used.
The `-e` option will set how many evaluations hyperopt will do. Since hyperopt uses Bayesian search, running too many epochs at once may not produce greater results. Experience has shown that best results are usually not improving much after 500-1000 epochs.
Doing multiple runs (executions) with a few 1000 epochs and different random state will most likely produce different results.
The `--spaces all` option determines that all possible parameters should be optimized. Possibilities are listed below.
!!! Note
Hyperopt will store hyperopt results with the timestamp of the hyperopt start time.
Reading commands (`hyperopt-list`, `hyperopt-show`) can use `--hyperopt-filename <filename>` to read and display older hyperopt results.
You can find a list of filenames with `ls -l user_data/hyperopt_results/`.
#### Running Hyperopt using methods from a strategy
Hyperopt can reuse `populate_indicators`, `populate_buy_trend`, `populate_sell_trend` from your strategy, assuming these methods are **not** in your custom hyperopt file, and a strategy is provided.
```bash
freqtrade hyperopt --hyperopt AwesomeHyperopt --hyperopt-loss SharpeHyperOptLossDaily --strategy AwesomeStrategy
```
### Understand the Hyperopt Result
Once Hyperopt is completed you can use the result to create a new strategy.
Given the following result from hyperopt:
```
Best result:
44/100: 135 trades. Avg profit 0.57%. Total profit 0.03871918 BTC (0.7722%). Avg duration 180.4 mins. Objective: 1.94367
Buy hyperspace params:
{ 'adx-value': 44,
'rsi-value': 29,
'adx-enabled': False,
'rsi-enabled': True,
'trigger': 'bb_lower'}
```
You should understand this result like:
* The buy trigger that worked best was `bb_lower`.
* You should not use ADX because `adx-enabled: False`)
* You should **consider** using the RSI indicator (`rsi-enabled: True` and the best value is `29.0` (`rsi-value: 29.0`)
You have to look inside your strategy file into `buy_strategy_generator()`
method, what those values match to.
So for example you had `rsi-value: 29.0` so we would look at `rsi`-block, that translates to the following code block:
```python
(dataframe['rsi'] < 29.0)
```
Translating your whole hyperopt result as the new buy-signal would then look like:
```python
def populate_buy_trend(self, dataframe: DataFrame) -> DataFrame:
dataframe.loc[
(
(dataframe['rsi'] < 29.0) & # rsi-value
dataframe['close'] < dataframe['bb_lowerband'] # trigger
),
'buy'] = 1
return dataframe
```
### Validate backtesting results
Once the optimized parameters and conditions have been implemented into your strategy, you should backtest the strategy to make sure everything is working as expected.
To achieve same results (number of trades, their durations, profit, etc.) than during Hyperopt, please use same configuration and parameters (timerange, timeframe, ...) used for hyperopt `--dmmp`/`--disable-max-market-positions` and `--eps`/`--enable-position-stacking` for Backtesting.
Should results not match, please double-check to make sure you transferred all conditions correctly.
Pay special care to the stoploss (and trailing stoploss) parameters, as these are often set in configuration files, which override changes to the strategy.
You should also carefully review the log of your backtest to ensure that there were no parameters inadvertently set by the configuration (like `stoploss` or `trailing_stop`).
### Sharing methods with your strategy
Hyperopt classes provide access to the Strategy via the `strategy` class attribute.
This can be a great way to reduce code duplication if used correctly, but will also complicate usage for inexperienced users.
``` python
from pandas import DataFrame
from freqtrade.strategy.interface import IStrategy
import freqtrade.vendor.qtpylib.indicators as qtpylib
class MyAwesomeStrategy(IStrategy):
buy_params = {
'rsi-value': 30,
'adx-value': 35,
}
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
return self.buy_strategy_generator(self.buy_params, dataframe, metadata)
@staticmethod
def buy_strategy_generator(params, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe.loc[
(
qtpylib.crossed_above(dataframe['rsi'], params['rsi-value']) &
dataframe['adx'] > params['adx-value']) &
dataframe['volume'] > 0
)
, 'buy'] = 1
return dataframe
class MyAwesomeHyperOpt(IHyperOpt):
...
@staticmethod
def buy_strategy_generator(params: Dict[str, Any]) -> Callable:
"""
Define the buy strategy parameters to be used by Hyperopt.
"""
def populate_buy_trend(dataframe: DataFrame, metadata: dict) -> DataFrame:
# Call strategy's buy strategy generator
return self.StrategyClass.buy_strategy_generator(params, dataframe, metadata)
return populate_buy_trend
```

View File

@ -12,22 +12,22 @@ This page explains the different parameters of the bot and how to run it.
``` ```
usage: freqtrade [-h] [-V] usage: freqtrade [-h] [-V]
{trade,create-userdir,new-config,new-hyperopt,new-strategy,download-data,convert-data,convert-trade-data,backtesting,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-hyperopts,list-markets,list-pairs,list-strategies,list-timeframes,show-trades,test-pairlist,plot-dataframe,plot-profit} {trade,create-userdir,new-config,new-strategy,download-data,convert-data,convert-trade-data,list-data,backtesting,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-hyperopts,list-markets,list-pairs,list-strategies,list-timeframes,show-trades,test-pairlist,install-ui,plot-dataframe,plot-profit,webserver}
... ...
Free, open source crypto trading bot Free, open source crypto trading bot
positional arguments: positional arguments:
{trade,create-userdir,new-config,new-hyperopt,new-strategy,download-data,convert-data,convert-trade-data,backtesting,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-hyperopts,list-markets,list-pairs,list-strategies,list-timeframes,show-trades,test-pairlist,plot-dataframe,plot-profit} {trade,create-userdir,new-config,new-strategy,download-data,convert-data,convert-trade-data,list-data,backtesting,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-hyperopts,list-markets,list-pairs,list-strategies,list-timeframes,show-trades,test-pairlist,install-ui,plot-dataframe,plot-profit,webserver}
trade Trade module. trade Trade module.
create-userdir Create user-data directory. create-userdir Create user-data directory.
new-config Create new config new-config Create new config
new-hyperopt Create new hyperopt
new-strategy Create new strategy new-strategy Create new strategy
download-data Download backtesting data. download-data Download backtesting data.
convert-data Convert candle (OHLCV) data from one format to convert-data Convert candle (OHLCV) data from one format to
another. another.
convert-trade-data Convert trade data from one format to another. convert-trade-data Convert trade data from one format to another.
list-data List downloaded data.
backtesting Backtesting module. backtesting Backtesting module.
edge Edge module. edge Edge module.
hyperopt Hyperopt module. hyperopt Hyperopt module.
@ -41,8 +41,10 @@ positional arguments:
list-timeframes Print available timeframes for the exchange. list-timeframes Print available timeframes for the exchange.
show-trades Show trades. show-trades Show trades.
test-pairlist Test your pairlist configuration. test-pairlist Test your pairlist configuration.
install-ui Install FreqUI
plot-dataframe Plot candles with indicators. plot-dataframe Plot candles with indicators.
plot-profit Generate plot showing profits. plot-profit Generate plot showing profits.
webserver Webserver module.
optional arguments: optional arguments:
-h, --help show this help message and exit -h, --help show this help message and exit

View File

@ -38,3 +38,8 @@ Since only quoteVolume can be compared between assets, the other options (bidVol
Using `order_book_min` and `order_book_max` used to allow stepping the orderbook and trying to find the next ROI slot - trying to place sell-orders early. Using `order_book_min` and `order_book_max` used to allow stepping the orderbook and trying to find the next ROI slot - trying to place sell-orders early.
As this does however increase risk and provides no benefit, it's been removed for maintainability purposes in 2021.7. As this does however increase risk and provides no benefit, it's been removed for maintainability purposes in 2021.7.
### Legacy Hyperopt mode
Using separate hyperopt files was deprecated in 2021.4 and was removed in 2021.9.
Please switch to the new [Parametrized Strategies](hyperopt.md) to benefit from the new hyperopt interface.

View File

@ -167,7 +167,7 @@ Since hyperopt uses Bayesian search, running for too many epochs may not produce
It's therefore recommended to run between 500-1000 epochs over and over until you hit at least 10.000 epochs in total (or are satisfied with the result). You can best judge by looking at the results - if the bot keeps discovering better strategies, it's best to keep on going. It's therefore recommended to run between 500-1000 epochs over and over until you hit at least 10.000 epochs in total (or are satisfied with the result). You can best judge by looking at the results - if the bot keeps discovering better strategies, it's best to keep on going.
```bash ```bash
freqtrade hyperopt --hyperopt SampleHyperopt --hyperopt-loss SharpeHyperOptLossDaily --strategy SampleStrategy -e 1000 freqtrade hyperopt --hyperopt-loss SharpeHyperOptLossDaily --strategy SampleStrategy -e 1000
``` ```
### Why does it take a long time to run hyperopt? ### Why does it take a long time to run hyperopt?

View File

@ -44,9 +44,8 @@ usage: freqtrade hyperopt [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH]
[--data-format-ohlcv {json,jsongz,hdf5}] [--data-format-ohlcv {json,jsongz,hdf5}]
[--max-open-trades INT] [--max-open-trades INT]
[--stake-amount STAKE_AMOUNT] [--fee FLOAT] [--stake-amount STAKE_AMOUNT] [--fee FLOAT]
[-p PAIRS [PAIRS ...]] [--hyperopt NAME] [-p PAIRS [PAIRS ...]] [--hyperopt-path PATH]
[--hyperopt-path PATH] [--eps] [--dmmp] [--eps] [--dmmp] [--enable-protections]
[--enable-protections]
[--dry-run-wallet DRY_RUN_WALLET] [-e INT] [--dry-run-wallet DRY_RUN_WALLET] [-e INT]
[--spaces {all,buy,sell,roi,stoploss,trailing,protection,default} [{all,buy,sell,roi,stoploss,trailing,protection,default} ...]] [--spaces {all,buy,sell,roi,stoploss,trailing,protection,default} [{all,buy,sell,roi,stoploss,trailing,protection,default} ...]]
[--print-all] [--no-color] [--print-json] [-j JOBS] [--print-all] [--no-color] [--print-json] [-j JOBS]
@ -73,10 +72,8 @@ optional arguments:
-p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...] -p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...]
Limit command to these pairs. Pairs are space- Limit command to these pairs. Pairs are space-
separated. separated.
--hyperopt NAME Specify hyperopt class name which will be used by the --hyperopt-path PATH Specify additional lookup path for Hyperopt Loss
bot. functions.
--hyperopt-path PATH Specify additional lookup path for Hyperopt and
Hyperopt Loss functions.
--eps, --enable-position-stacking --eps, --enable-position-stacking
Allow buying the same pair multiple times (position Allow buying the same pair multiple times (position
stacking). stacking).
@ -558,7 +555,7 @@ For example, to use one month of data, pass `--timerange 20210101-20210201` (fro
Full command: Full command:
```bash ```bash
freqtrade hyperopt --hyperopt <hyperoptname> --strategy <strategyname> --timerange 20210101-20210201 freqtrade hyperopt --strategy <strategyname> --timerange 20210101-20210201
``` ```
### Running Hyperopt with Smaller Search Space ### Running Hyperopt with Smaller Search Space
@ -684,7 +681,7 @@ If you have the `generate_roi_table()` and `roi_space()` methods in your custom
Override the `roi_space()` method if you need components of the ROI tables to vary in other ranges. Override the `generate_roi_table()` and `roi_space()` methods and implement your own custom approach for generation of the ROI tables during hyperoptimization if you need a different structure of the ROI tables or other amount of rows (steps). Override the `roi_space()` method if you need components of the ROI tables to vary in other ranges. Override the `generate_roi_table()` and `roi_space()` methods and implement your own custom approach for generation of the ROI tables during hyperoptimization if you need a different structure of the ROI tables or other amount of rows (steps).
A sample for these methods can be found in [sample_hyperopt_advanced.py](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/templates/sample_hyperopt_advanced.py). A sample for these methods can be found in the [overriding pre-defined spaces section](advanced-hyperopt.md#overriding-pre-defined-spaces).
!!! Note "Reduced search space" !!! Note "Reduced search space"
To limit the search space further, Decimals are limited to 3 decimal places (a precision of 0.001). This is usually sufficient, every value more precise than this will usually result in overfitted results. You can however [overriding pre-defined spaces](advanced-hyperopt.md#pverriding-pre-defined-spaces) to change this to your needs. To limit the search space further, Decimals are limited to 3 decimal places (a precision of 0.001). This is usually sufficient, every value more precise than this will usually result in overfitted results. You can however [overriding pre-defined spaces](advanced-hyperopt.md#pverriding-pre-defined-spaces) to change this to your needs.
@ -726,7 +723,7 @@ If you are optimizing stoploss values, Freqtrade creates the 'stoploss' optimiza
If you have the `stoploss_space()` method in your custom hyperopt file, remove it in order to utilize Stoploss hyperoptimization space generated by Freqtrade by default. If you have the `stoploss_space()` method in your custom hyperopt file, remove it in order to utilize Stoploss hyperoptimization space generated by Freqtrade by default.
Override the `stoploss_space()` method and define the desired range in it if you need stoploss values to vary in other range during hyperoptimization. A sample for this method can be found in [user_data/hyperopts/sample_hyperopt_advanced.py](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/templates/sample_hyperopt_advanced.py). Override the `stoploss_space()` method and define the desired range in it if you need stoploss values to vary in other range during hyperoptimization. A sample for this method can be found in the [overriding pre-defined spaces section](advanced-hyperopt.md#overriding-pre-defined-spaces).
!!! Note "Reduced search space" !!! Note "Reduced search space"
To limit the search space further, Decimals are limited to 3 decimal places (a precision of 0.001). This is usually sufficient, every value more precise than this will usually result in overfitted results. You can however [overriding pre-defined spaces](advanced-hyperopt.md#pverriding-pre-defined-spaces) to change this to your needs. To limit the search space further, Decimals are limited to 3 decimal places (a precision of 0.001). This is usually sufficient, every value more precise than this will usually result in overfitted results. You can however [overriding pre-defined spaces](advanced-hyperopt.md#pverriding-pre-defined-spaces) to change this to your needs.
@ -764,10 +761,10 @@ As stated in the comment, you can also use it as the values of the corresponding
If you are optimizing trailing stop values, Freqtrade creates the 'trailing' optimization hyperspace for you. By default, the `trailing_stop` parameter is always set to True in that hyperspace, the value of the `trailing_only_offset_is_reached` vary between True and False, the values of the `trailing_stop_positive` and `trailing_stop_positive_offset` parameters vary in the ranges 0.02...0.35 and 0.01...0.1 correspondingly, which is sufficient in most cases. If you are optimizing trailing stop values, Freqtrade creates the 'trailing' optimization hyperspace for you. By default, the `trailing_stop` parameter is always set to True in that hyperspace, the value of the `trailing_only_offset_is_reached` vary between True and False, the values of the `trailing_stop_positive` and `trailing_stop_positive_offset` parameters vary in the ranges 0.02...0.35 and 0.01...0.1 correspondingly, which is sufficient in most cases.
Override the `trailing_space()` method and define the desired range in it if you need values of the trailing stop parameters to vary in other ranges during hyperoptimization. A sample for this method can be found in [user_data/hyperopts/sample_hyperopt_advanced.py](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/templates/sample_hyperopt_advanced.py). Override the `trailing_space()` method and define the desired range in it if you need values of the trailing stop parameters to vary in other ranges during hyperoptimization. A sample for this method can be found in the [overriding pre-defined spaces section](advanced-hyperopt.md#overriding-pre-defined-spaces).
!!! Note "Reduced search space" !!! Note "Reduced search space"
To limit the search space further, Decimals are limited to 3 decimal places (a precision of 0.001). This is usually sufficient, every value more precise than this will usually result in overfitted results. You can however [overriding pre-defined spaces](advanced-hyperopt.md#pverriding-pre-defined-spaces) to change this to your needs. To limit the search space further, Decimals are limited to 3 decimal places (a precision of 0.001). This is usually sufficient, every value more precise than this will usually result in overfitted results. You can however [overriding pre-defined spaces](advanced-hyperopt.md#overriding-pre-defined-spaces) to change this to your needs.
### Reproducible results ### Reproducible results

View File

@ -26,9 +26,7 @@ optional arguments:
├── data ├── data
├── hyperopt_results ├── hyperopt_results
├── hyperopts ├── hyperopts
│   ├── sample_hyperopt_advanced.py
│   ├── sample_hyperopt_loss.py │   ├── sample_hyperopt_loss.py
│   └── sample_hyperopt.py
├── notebooks ├── notebooks
│   └── strategy_analysis_example.ipynb │   └── strategy_analysis_example.ipynb
├── plot ├── plot
@ -111,46 +109,11 @@ Using the advanced template (populates all optional functions and methods)
freqtrade new-strategy --strategy AwesomeStrategy --template advanced freqtrade new-strategy --strategy AwesomeStrategy --template advanced
``` ```
## Create new hyperopt ## List Strategies
Creates a new hyperopt from a template similar to SampleHyperopt. Use the `list-strategies` subcommand to see all strategies in one particular directory.
The file will be named inline with your class name, and will not overwrite existing files.
Results will be located in `user_data/hyperopts/<classname>.py`. This subcommand is useful for finding problems in your environment with loading strategies: modules with strategies that contain errors and failed to load are printed in red (LOAD FAILED), while strategies with duplicate names are printed in yellow (DUPLICATE NAME).
``` output
usage: freqtrade new-hyperopt [-h] [--userdir PATH] [--hyperopt NAME]
[--template {full,minimal,advanced}]
optional arguments:
-h, --help show this help message and exit
--userdir PATH, --user-data-dir PATH
Path to userdata directory.
--hyperopt NAME Specify hyperopt class name which will be used by the
bot.
--template {full,minimal,advanced}
Use a template which is either `minimal`, `full`
(containing multiple sample indicators) or `advanced`.
Default: `full`.
```
### Sample usage of new-hyperopt
```bash
freqtrade new-hyperopt --hyperopt AwesomeHyperopt
```
With custom user directory
```bash
freqtrade new-hyperopt --userdir ~/.freqtrade/ --hyperopt AwesomeHyperopt
```
## List Strategies and List Hyperopts
Use the `list-strategies` subcommand to see all strategies in one particular directory and the `list-hyperopts` subcommand to list custom Hyperopts.
These subcommands are useful for finding problems in your environment with loading strategies or hyperopt classes: modules with strategies or hyperopt classes that contain errors and failed to load are printed in red (LOAD FAILED), while strategies or hyperopt classes with duplicate names are printed in yellow (DUPLICATE NAME).
``` ```
usage: freqtrade list-strategies [-h] [-v] [--logfile FILE] [-V] [-c PATH] usage: freqtrade list-strategies [-h] [-v] [--logfile FILE] [-V] [-c PATH]
@ -164,34 +127,6 @@ optional arguments:
--no-color Disable colorization of hyperopt results. May be --no-color Disable colorization of hyperopt results. May be
useful if you are redirecting output to a file. useful if you are redirecting output to a file.
Common arguments:
-v, --verbose Verbose mode (-vv for more, -vvv to get all messages).
--logfile FILE Log to the file specified. Special values are:
'syslog', 'journald'. See the documentation for more
details.
-V, --version show program's version number and exit
-c PATH, --config PATH
Specify configuration file (default: `config.json`).
Multiple --config options may be used. Can be set to
`-` to read config from stdin.
-d PATH, --datadir PATH
Path to directory with historical backtesting data.
--userdir PATH, --user-data-dir PATH
Path to userdata directory.
```
```
usage: freqtrade list-hyperopts [-h] [-v] [--logfile FILE] [-V] [-c PATH]
[-d PATH] [--userdir PATH]
[--hyperopt-path PATH] [-1] [--no-color]
optional arguments:
-h, --help show this help message and exit
--hyperopt-path PATH Specify additional lookup path for Hyperopt and
Hyperopt Loss functions.
-1, --one-column Print output in one column.
--no-color Disable colorization of hyperopt results. May be
useful if you are redirecting output to a file.
Common arguments: Common arguments:
-v, --verbose Verbose mode (-vv for more, -vvv to get all messages). -v, --verbose Verbose mode (-vv for more, -vvv to get all messages).
--logfile FILE Log to the file specified. Special values are: --logfile FILE Log to the file specified. Special values are:
@ -211,18 +146,16 @@ Common arguments:
!!! Warning !!! Warning
Using these commands will try to load all python files from a directory. This can be a security risk if untrusted files reside in this directory, since all module-level code is executed. Using these commands will try to load all python files from a directory. This can be a security risk if untrusted files reside in this directory, since all module-level code is executed.
Example: Search default strategies and hyperopts directories (within the default userdir). Example: Search default strategies directories (within the default userdir).
``` bash ``` bash
freqtrade list-strategies freqtrade list-strategies
freqtrade list-hyperopts
``` ```
Example: Search strategies and hyperopts directory within the userdir. Example: Search strategies directory within the userdir.
``` bash ``` bash
freqtrade list-strategies --userdir ~/.freqtrade/ freqtrade list-strategies --userdir ~/.freqtrade/
freqtrade list-hyperopts --userdir ~/.freqtrade/
``` ```
Example: Search dedicated strategy path. Example: Search dedicated strategy path.
@ -231,12 +164,6 @@ Example: Search dedicated strategy path.
freqtrade list-strategies --strategy-path ~/.freqtrade/strategies/ freqtrade list-strategies --strategy-path ~/.freqtrade/strategies/
``` ```
Example: Search dedicated hyperopt path.
``` bash
freqtrade list-hyperopt --hyperopt-path ~/.freqtrade/hyperopts/
```
## List Exchanges ## List Exchanges
Use the `list-exchanges` subcommand to see the exchanges available for the bot. Use the `list-exchanges` subcommand to see the exchanges available for the bot.

View File

@ -11,11 +11,11 @@ from freqtrade.commands.build_config_commands import start_new_config
from freqtrade.commands.data_commands import (start_convert_data, start_download_data, from freqtrade.commands.data_commands import (start_convert_data, start_download_data,
start_list_data) start_list_data)
from freqtrade.commands.deploy_commands import (start_create_userdir, start_install_ui, from freqtrade.commands.deploy_commands import (start_create_userdir, start_install_ui,
start_new_hyperopt, start_new_strategy) start_new_strategy)
from freqtrade.commands.hyperopt_commands import start_hyperopt_list, start_hyperopt_show from freqtrade.commands.hyperopt_commands import start_hyperopt_list, start_hyperopt_show
from freqtrade.commands.list_commands import (start_list_exchanges, start_list_hyperopts, from freqtrade.commands.list_commands import (start_list_exchanges, start_list_markets,
start_list_markets, start_list_strategies, start_list_strategies, start_list_timeframes,
start_list_timeframes, start_show_trades) start_show_trades)
from freqtrade.commands.optimize_commands import start_backtesting, start_edge, start_hyperopt from freqtrade.commands.optimize_commands import start_backtesting, start_edge, start_hyperopt
from freqtrade.commands.pairlist_commands import start_test_pairlist from freqtrade.commands.pairlist_commands import start_test_pairlist
from freqtrade.commands.plot_commands import start_plot_dataframe, start_plot_profit from freqtrade.commands.plot_commands import start_plot_dataframe, start_plot_profit

View File

@ -55,8 +55,6 @@ ARGS_BUILD_CONFIG = ["config"]
ARGS_BUILD_STRATEGY = ["user_data_dir", "strategy", "template"] ARGS_BUILD_STRATEGY = ["user_data_dir", "strategy", "template"]
ARGS_BUILD_HYPEROPT = ["user_data_dir", "hyperopt", "template"]
ARGS_CONVERT_DATA = ["pairs", "format_from", "format_to", "erase"] ARGS_CONVERT_DATA = ["pairs", "format_from", "format_to", "erase"]
ARGS_CONVERT_DATA_OHLCV = ARGS_CONVERT_DATA + ["timeframes"] ARGS_CONVERT_DATA_OHLCV = ARGS_CONVERT_DATA + ["timeframes"]
@ -92,10 +90,10 @@ ARGS_HYPEROPT_SHOW = ["hyperopt_list_best", "hyperopt_list_profitable", "hyperop
NO_CONF_REQURIED = ["convert-data", "convert-trade-data", "download-data", "list-timeframes", NO_CONF_REQURIED = ["convert-data", "convert-trade-data", "download-data", "list-timeframes",
"list-markets", "list-pairs", "list-strategies", "list-data", "list-markets", "list-pairs", "list-strategies", "list-data",
"list-hyperopts", "hyperopt-list", "hyperopt-show", "hyperopt-list", "hyperopt-show",
"plot-dataframe", "plot-profit", "show-trades"] "plot-dataframe", "plot-profit", "show-trades"]
NO_CONF_ALLOWED = ["create-userdir", "list-exchanges", "new-hyperopt", "new-strategy"] NO_CONF_ALLOWED = ["create-userdir", "list-exchanges", "new-strategy"]
class Arguments: class Arguments:
@ -174,12 +172,11 @@ class Arguments:
from freqtrade.commands import (start_backtesting, start_convert_data, start_create_userdir, from freqtrade.commands import (start_backtesting, start_convert_data, start_create_userdir,
start_download_data, start_edge, start_hyperopt, start_download_data, start_edge, start_hyperopt,
start_hyperopt_list, start_hyperopt_show, start_install_ui, start_hyperopt_list, start_hyperopt_show, start_install_ui,
start_list_data, start_list_exchanges, start_list_hyperopts, start_list_data, start_list_exchanges, start_list_markets,
start_list_markets, start_list_strategies, start_list_strategies, start_list_timeframes,
start_list_timeframes, start_new_config, start_new_hyperopt, start_new_config, start_new_strategy, start_plot_dataframe,
start_new_strategy, start_plot_dataframe, start_plot_profit, start_plot_profit, start_show_trades, start_test_pairlist,
start_show_trades, start_test_pairlist, start_trading, start_trading, start_webserver)
start_webserver)
subparsers = self.parser.add_subparsers(dest='command', subparsers = self.parser.add_subparsers(dest='command',
# Use custom message when no subhandler is added # Use custom message when no subhandler is added
@ -206,12 +203,6 @@ class Arguments:
build_config_cmd.set_defaults(func=start_new_config) build_config_cmd.set_defaults(func=start_new_config)
self._build_args(optionlist=ARGS_BUILD_CONFIG, parser=build_config_cmd) self._build_args(optionlist=ARGS_BUILD_CONFIG, parser=build_config_cmd)
# add new-hyperopt subcommand
build_hyperopt_cmd = subparsers.add_parser('new-hyperopt',
help="Create new hyperopt")
build_hyperopt_cmd.set_defaults(func=start_new_hyperopt)
self._build_args(optionlist=ARGS_BUILD_HYPEROPT, parser=build_hyperopt_cmd)
# add new-strategy subcommand # add new-strategy subcommand
build_strategy_cmd = subparsers.add_parser('new-strategy', build_strategy_cmd = subparsers.add_parser('new-strategy',
help="Create new strategy") help="Create new strategy")
@ -300,15 +291,6 @@ class Arguments:
list_exchanges_cmd.set_defaults(func=start_list_exchanges) list_exchanges_cmd.set_defaults(func=start_list_exchanges)
self._build_args(optionlist=ARGS_LIST_EXCHANGES, parser=list_exchanges_cmd) self._build_args(optionlist=ARGS_LIST_EXCHANGES, parser=list_exchanges_cmd)
# Add list-hyperopts subcommand
list_hyperopts_cmd = subparsers.add_parser(
'list-hyperopts',
help='Print available hyperopt classes.',
parents=[_common_parser],
)
list_hyperopts_cmd.set_defaults(func=start_list_hyperopts)
self._build_args(optionlist=ARGS_LIST_HYPEROPTS, parser=list_hyperopts_cmd)
# Add list-markets subcommand # Add list-markets subcommand
list_markets_cmd = subparsers.add_parser( list_markets_cmd = subparsers.add_parser(
'list-markets', 'list-markets',

View File

@ -1,7 +1,7 @@
""" """
Definition of cli arguments used in arguments.py Definition of cli arguments used in arguments.py
""" """
from argparse import ArgumentTypeError from argparse import SUPPRESS, ArgumentTypeError
from freqtrade import __version__, constants from freqtrade import __version__, constants
from freqtrade.constants import HYPEROPT_LOSS_BUILTIN from freqtrade.constants import HYPEROPT_LOSS_BUILTIN
@ -203,13 +203,13 @@ AVAILABLE_CLI_OPTIONS = {
# Hyperopt # Hyperopt
"hyperopt": Arg( "hyperopt": Arg(
'--hyperopt', '--hyperopt',
help='Specify hyperopt class name which will be used by the bot.', help=SUPPRESS,
metavar='NAME', metavar='NAME',
required=False, required=False,
), ),
"hyperopt_path": Arg( "hyperopt_path": Arg(
'--hyperopt-path', '--hyperopt-path',
help='Specify additional lookup path for Hyperopt and Hyperopt Loss functions.', help='Specify additional lookup path for Hyperopt Loss functions.',
metavar='PATH', metavar='PATH',
), ),
"epochs": Arg( "epochs": Arg(

View File

@ -7,7 +7,7 @@ import requests
from freqtrade.configuration import setup_utils_configuration from freqtrade.configuration import setup_utils_configuration
from freqtrade.configuration.directory_operations import copy_sample_files, create_userdata_dir from freqtrade.configuration.directory_operations import copy_sample_files, create_userdata_dir
from freqtrade.constants import USERPATH_HYPEROPTS, USERPATH_STRATEGIES from freqtrade.constants import USERPATH_STRATEGIES
from freqtrade.enums import RunMode from freqtrade.enums import RunMode
from freqtrade.exceptions import OperationalException from freqtrade.exceptions import OperationalException
from freqtrade.misc import render_template, render_template_with_fallback from freqtrade.misc import render_template, render_template_with_fallback
@ -87,56 +87,6 @@ def start_new_strategy(args: Dict[str, Any]) -> None:
raise OperationalException("`new-strategy` requires --strategy to be set.") raise OperationalException("`new-strategy` requires --strategy to be set.")
def deploy_new_hyperopt(hyperopt_name: str, hyperopt_path: Path, subtemplate: str) -> None:
"""
Deploys a new hyperopt template to hyperopt_path
"""
fallback = 'full'
buy_guards = render_template_with_fallback(
templatefile=f"subtemplates/hyperopt_buy_guards_{subtemplate}.j2",
templatefallbackfile=f"subtemplates/hyperopt_buy_guards_{fallback}.j2",
)
sell_guards = render_template_with_fallback(
templatefile=f"subtemplates/hyperopt_sell_guards_{subtemplate}.j2",
templatefallbackfile=f"subtemplates/hyperopt_sell_guards_{fallback}.j2",
)
buy_space = render_template_with_fallback(
templatefile=f"subtemplates/hyperopt_buy_space_{subtemplate}.j2",
templatefallbackfile=f"subtemplates/hyperopt_buy_space_{fallback}.j2",
)
sell_space = render_template_with_fallback(
templatefile=f"subtemplates/hyperopt_sell_space_{subtemplate}.j2",
templatefallbackfile=f"subtemplates/hyperopt_sell_space_{fallback}.j2",
)
strategy_text = render_template(templatefile='base_hyperopt.py.j2',
arguments={"hyperopt": hyperopt_name,
"buy_guards": buy_guards,
"sell_guards": sell_guards,
"buy_space": buy_space,
"sell_space": sell_space,
})
logger.info(f"Writing hyperopt to `{hyperopt_path}`.")
hyperopt_path.write_text(strategy_text)
def start_new_hyperopt(args: Dict[str, Any]) -> None:
config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE)
if 'hyperopt' in args and args['hyperopt']:
new_path = config['user_data_dir'] / USERPATH_HYPEROPTS / (args['hyperopt'] + '.py')
if new_path.exists():
raise OperationalException(f"`{new_path}` already exists. "
"Please choose another Hyperopt Name.")
deploy_new_hyperopt(args['hyperopt'], new_path, args['template'])
else:
raise OperationalException("`new-hyperopt` requires --hyperopt to be set.")
def clean_ui_subdir(directory: Path): def clean_ui_subdir(directory: Path):
if directory.is_dir(): if directory.is_dir():
logger.info("Removing UI directory content.") logger.info("Removing UI directory content.")

View File

@ -10,7 +10,7 @@ from colorama import init as colorama_init
from tabulate import tabulate from tabulate import tabulate
from freqtrade.configuration import setup_utils_configuration from freqtrade.configuration import setup_utils_configuration
from freqtrade.constants import USERPATH_HYPEROPTS, USERPATH_STRATEGIES from freqtrade.constants import USERPATH_STRATEGIES
from freqtrade.enums import RunMode from freqtrade.enums import RunMode
from freqtrade.exceptions import OperationalException from freqtrade.exceptions import OperationalException
from freqtrade.exchange import market_is_active, validate_exchanges from freqtrade.exchange import market_is_active, validate_exchanges
@ -92,25 +92,6 @@ def start_list_strategies(args: Dict[str, Any]) -> None:
_print_objs_tabular(strategy_objs, config.get('print_colorized', False)) _print_objs_tabular(strategy_objs, config.get('print_colorized', False))
def start_list_hyperopts(args: Dict[str, Any]) -> None:
"""
Print files with HyperOpt custom classes available in the directory
"""
from freqtrade.resolvers.hyperopt_resolver import HyperOptResolver
config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE)
directory = Path(config.get('hyperopt_path', config['user_data_dir'] / USERPATH_HYPEROPTS))
hyperopt_objs = HyperOptResolver.search_all_objects(directory, not args['print_one_column'])
# Sort alphabetically
hyperopt_objs = sorted(hyperopt_objs, key=lambda x: x['name'])
if args['print_one_column']:
print('\n'.join([s['name'] for s in hyperopt_objs]))
else:
_print_objs_tabular(hyperopt_objs, config.get('print_colorized', False))
def start_list_timeframes(args: Dict[str, Any]) -> None: def start_list_timeframes(args: Dict[str, Any]) -> None:
""" """
Print timeframes available on Exchange Print timeframes available on Exchange

View File

@ -69,9 +69,7 @@ DUST_PER_COIN = {
# Source files with destination directories within user-directory # Source files with destination directories within user-directory
USER_DATA_FILES = { USER_DATA_FILES = {
'sample_strategy.py': USERPATH_STRATEGIES, 'sample_strategy.py': USERPATH_STRATEGIES,
'sample_hyperopt_advanced.py': USERPATH_HYPEROPTS,
'sample_hyperopt_loss.py': USERPATH_HYPEROPTS, 'sample_hyperopt_loss.py': USERPATH_HYPEROPTS,
'sample_hyperopt.py': USERPATH_HYPEROPTS,
'strategy_analysis_example.ipynb': USERPATH_NOTEBOOKS, 'strategy_analysis_example.ipynb': USERPATH_NOTEBOOKS,
} }

View File

@ -22,6 +22,7 @@ from pandas import DataFrame
from freqtrade.constants import DATETIME_PRINT_FORMAT, FTHYPT_FILEVERSION, LAST_BT_RESULT_FN from freqtrade.constants import DATETIME_PRINT_FORMAT, FTHYPT_FILEVERSION, LAST_BT_RESULT_FN
from freqtrade.data.converter import trim_dataframes from freqtrade.data.converter import trim_dataframes
from freqtrade.data.history import get_timerange from freqtrade.data.history import get_timerange
from freqtrade.exceptions import OperationalException
from freqtrade.misc import deep_merge_dicts, file_dump_json, plural from freqtrade.misc import deep_merge_dicts, file_dump_json, plural
from freqtrade.optimize.backtesting import Backtesting from freqtrade.optimize.backtesting import Backtesting
# Import IHyperOpt and IHyperOptLoss to allow unpickling classes from these modules # Import IHyperOpt and IHyperOptLoss to allow unpickling classes from these modules
@ -30,7 +31,7 @@ from freqtrade.optimize.hyperopt_interface import IHyperOpt # noqa: F401
from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss # noqa: F401 from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss # noqa: F401
from freqtrade.optimize.hyperopt_tools import HyperoptTools, hyperopt_serializer from freqtrade.optimize.hyperopt_tools import HyperoptTools, hyperopt_serializer
from freqtrade.optimize.optimize_reports import generate_strategy_stats from freqtrade.optimize.optimize_reports import generate_strategy_stats
from freqtrade.resolvers.hyperopt_resolver import HyperOptLossResolver, HyperOptResolver from freqtrade.resolvers.hyperopt_resolver import HyperOptLossResolver
# Suppress scikit-learn FutureWarnings from skopt # Suppress scikit-learn FutureWarnings from skopt
@ -78,10 +79,10 @@ class Hyperopt:
if not self.config.get('hyperopt'): if not self.config.get('hyperopt'):
self.custom_hyperopt = HyperOptAuto(self.config) self.custom_hyperopt = HyperOptAuto(self.config)
self.auto_hyperopt = True
else: else:
self.custom_hyperopt = HyperOptResolver.load_hyperopt(self.config) raise OperationalException(
self.auto_hyperopt = False "Using separate Hyperopt files has been removed in 2021.9. Please convert "
"your existing Hyperopt file to the new Hyperoptable strategy interface")
self.backtesting._set_strategy(self.backtesting.strategylist[0]) self.backtesting._set_strategy(self.backtesting.strategylist[0])
self.custom_hyperopt.strategy = self.backtesting.strategy self.custom_hyperopt.strategy = self.backtesting.strategy
@ -103,31 +104,6 @@ class Hyperopt:
self.num_epochs_saved = 0 self.num_epochs_saved = 0
self.current_best_epoch: Optional[Dict[str, Any]] = None self.current_best_epoch: Optional[Dict[str, Any]] = None
if not self.auto_hyperopt:
# Populate "fallback" functions here
# (hasattr is slow so should not be run during "regular" operations)
if hasattr(self.custom_hyperopt, 'populate_indicators'):
logger.warning(
"DEPRECATED: Using `populate_indicators()` in the hyperopt file is deprecated. "
"Please move these methods to your strategy."
)
self.backtesting.strategy.populate_indicators = ( # type: ignore
self.custom_hyperopt.populate_indicators) # type: ignore
if hasattr(self.custom_hyperopt, 'populate_buy_trend'):
logger.warning(
"DEPRECATED: Using `populate_buy_trend()` in the hyperopt file is deprecated. "
"Please move these methods to your strategy."
)
self.backtesting.strategy.populate_buy_trend = ( # type: ignore
self.custom_hyperopt.populate_buy_trend) # type: ignore
if hasattr(self.custom_hyperopt, 'populate_sell_trend'):
logger.warning(
"DEPRECATED: Using `populate_sell_trend()` in the hyperopt file is deprecated. "
"Please move these methods to your strategy."
)
self.backtesting.strategy.populate_sell_trend = ( # type: ignore
self.custom_hyperopt.populate_sell_trend) # type: ignore
# Use max_open_trades for hyperopt as well, except --disable-max-market-positions is set # Use max_open_trades for hyperopt as well, except --disable-max-market-positions is set
if self.config.get('use_max_market_positions', True): if self.config.get('use_max_market_positions', True):
self.max_open_trades = self.config['max_open_trades'] self.max_open_trades = self.config['max_open_trades']
@ -256,7 +232,7 @@ class Hyperopt:
""" """
Assign the dimensions in the hyperoptimization space. Assign the dimensions in the hyperoptimization space.
""" """
if self.auto_hyperopt and HyperoptTools.has_space(self.config, 'protection'): if HyperoptTools.has_space(self.config, 'protection'):
# Protections can only be optimized when using the Parameter interface # Protections can only be optimized when using the Parameter interface
logger.debug("Hyperopt has 'protection' space") logger.debug("Hyperopt has 'protection' space")
# Enable Protections if protection space is selected. # Enable Protections if protection space is selected.
@ -285,6 +261,15 @@ class Hyperopt:
self.dimensions = (self.buy_space + self.sell_space + self.protection_space self.dimensions = (self.buy_space + self.sell_space + self.protection_space
+ self.roi_space + self.stoploss_space + self.trailing_space) + self.roi_space + self.stoploss_space + self.trailing_space)
def assign_params(self, params_dict: Dict, category: str) -> None:
"""
Assign hyperoptable parameters
"""
for attr_name, attr in self.backtesting.strategy.enumerate_parameters(category):
if attr.optimize:
# noinspection PyProtectedMember
attr.value = params_dict[attr_name]
def generate_optimizer(self, raw_params: List[Any], iteration=None) -> Dict: def generate_optimizer(self, raw_params: List[Any], iteration=None) -> Dict:
""" """
Used Optimize function. Used Optimize function.
@ -296,18 +281,13 @@ class Hyperopt:
# Apply parameters # Apply parameters
if HyperoptTools.has_space(self.config, 'buy'): if HyperoptTools.has_space(self.config, 'buy'):
self.backtesting.strategy.advise_buy = ( # type: ignore self.assign_params(params_dict, 'buy')
self.custom_hyperopt.buy_strategy_generator(params_dict))
if HyperoptTools.has_space(self.config, 'sell'): if HyperoptTools.has_space(self.config, 'sell'):
self.backtesting.strategy.advise_sell = ( # type: ignore self.assign_params(params_dict, 'sell')
self.custom_hyperopt.sell_strategy_generator(params_dict))
if HyperoptTools.has_space(self.config, 'protection'): if HyperoptTools.has_space(self.config, 'protection'):
for attr_name, attr in self.backtesting.strategy.enumerate_parameters('protection'): self.assign_params(params_dict, 'protection')
if attr.optimize:
# noinspection PyProtectedMember
attr.value = params_dict[attr_name]
if HyperoptTools.has_space(self.config, 'roi'): if HyperoptTools.has_space(self.config, 'roi'):
self.backtesting.strategy.minimal_roi = ( # type: ignore self.backtesting.strategy.minimal_roi = ( # type: ignore
@ -517,7 +497,6 @@ class Hyperopt:
f"saved to '{self.results_file}'.") f"saved to '{self.results_file}'.")
if self.current_best_epoch: if self.current_best_epoch:
if self.auto_hyperopt:
HyperoptTools.try_export_params( HyperoptTools.try_export_params(
self.config, self.config,
self.backtesting.strategy.get_strategy_name(), self.backtesting.strategy.get_strategy_name(),

View File

@ -4,9 +4,9 @@ This module implements a convenience auto-hyperopt class, which can be used toge
that implement IHyperStrategy interface. that implement IHyperStrategy interface.
""" """
from contextlib import suppress from contextlib import suppress
from typing import Any, Callable, Dict, List from typing import Callable, Dict, List
from pandas import DataFrame from freqtrade.exceptions import OperationalException
with suppress(ImportError): with suppress(ImportError):
@ -15,6 +15,14 @@ with suppress(ImportError):
from freqtrade.optimize.hyperopt_interface import IHyperOpt from freqtrade.optimize.hyperopt_interface import IHyperOpt
def _format_exception_message(space: str) -> str:
raise OperationalException(
f"The '{space}' space is included into the hyperoptimization "
f"but no parameter for this space was not found in your Strategy. "
f"Please make sure to have parameters for this space enabled for optimization "
f"or remove the '{space}' space from hyperoptimization.")
class HyperOptAuto(IHyperOpt): class HyperOptAuto(IHyperOpt):
""" """
This class delegates functionality to Strategy(IHyperStrategy) and Strategy.HyperOpt classes. This class delegates functionality to Strategy(IHyperStrategy) and Strategy.HyperOpt classes.
@ -22,26 +30,6 @@ class HyperOptAuto(IHyperOpt):
sell_indicator_space methods, but other hyperopt methods can be overridden as well. sell_indicator_space methods, but other hyperopt methods can be overridden as well.
""" """
def buy_strategy_generator(self, params: Dict[str, Any]) -> Callable:
def populate_buy_trend(dataframe: DataFrame, metadata: dict):
for attr_name, attr in self.strategy.enumerate_parameters('buy'):
if attr.optimize:
# noinspection PyProtectedMember
attr.value = params[attr_name]
return self.strategy.populate_buy_trend(dataframe, metadata)
return populate_buy_trend
def sell_strategy_generator(self, params: Dict[str, Any]) -> Callable:
def populate_sell_trend(dataframe: DataFrame, metadata: dict):
for attr_name, attr in self.strategy.enumerate_parameters('sell'):
if attr.optimize:
# noinspection PyProtectedMember
attr.value = params[attr_name]
return self.strategy.populate_sell_trend(dataframe, metadata)
return populate_sell_trend
def _get_func(self, name) -> Callable: def _get_func(self, name) -> Callable:
""" """
Return a function defined in Strategy.HyperOpt class, or one defined in super() class. Return a function defined in Strategy.HyperOpt class, or one defined in super() class.
@ -60,21 +48,22 @@ class HyperOptAuto(IHyperOpt):
if attr.optimize: if attr.optimize:
yield attr.get_space(attr_name) yield attr.get_space(attr_name)
def _get_indicator_space(self, category, fallback_method_name): def _get_indicator_space(self, category):
# TODO: is this necessary, or can we call "generate_space" directly?
indicator_space = list(self._generate_indicator_space(category)) indicator_space = list(self._generate_indicator_space(category))
if len(indicator_space) > 0: if len(indicator_space) > 0:
return indicator_space return indicator_space
else: else:
return self._get_func(fallback_method_name)() _format_exception_message(category)
def indicator_space(self) -> List['Dimension']: def indicator_space(self) -> List['Dimension']:
return self._get_indicator_space('buy', 'indicator_space') return self._get_indicator_space('buy')
def sell_indicator_space(self) -> List['Dimension']: def sell_indicator_space(self) -> List['Dimension']:
return self._get_indicator_space('sell', 'sell_indicator_space') return self._get_indicator_space('sell')
def protection_space(self) -> List['Dimension']: def protection_space(self) -> List['Dimension']:
return self._get_indicator_space('protection', 'protection_space') return self._get_indicator_space('protection')
def generate_roi_table(self, params: Dict) -> Dict[int, float]: def generate_roi_table(self, params: Dict) -> Dict[int, float]:
return self._get_func('generate_roi_table')(params) return self._get_func('generate_roi_table')(params)

View File

@ -5,11 +5,10 @@ This module defines the interface to apply for hyperopt
import logging import logging
import math import math
from abc import ABC from abc import ABC
from typing import Any, Callable, Dict, List from typing import Dict, List
from skopt.space import Categorical, Dimension, Integer from skopt.space import Categorical, Dimension, Integer
from freqtrade.exceptions import OperationalException
from freqtrade.exchange import timeframe_to_minutes from freqtrade.exchange import timeframe_to_minutes
from freqtrade.misc import round_dict from freqtrade.misc import round_dict
from freqtrade.optimize.space import SKDecimal from freqtrade.optimize.space import SKDecimal
@ -19,13 +18,6 @@ from freqtrade.strategy import IStrategy
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def _format_exception_message(method: str, space: str) -> str:
return (f"The '{space}' space is included into the hyperoptimization "
f"but {method}() method is not found in your "
f"custom Hyperopt class. You should either implement this "
f"method or remove the '{space}' space from hyperoptimization.")
class IHyperOpt(ABC): class IHyperOpt(ABC):
""" """
Interface for freqtrade hyperopt Interface for freqtrade hyperopt
@ -45,37 +37,6 @@ class IHyperOpt(ABC):
IHyperOpt.ticker_interval = str(config['timeframe']) # DEPRECATED IHyperOpt.ticker_interval = str(config['timeframe']) # DEPRECATED
IHyperOpt.timeframe = str(config['timeframe']) IHyperOpt.timeframe = str(config['timeframe'])
def buy_strategy_generator(self, params: Dict[str, Any]) -> Callable:
"""
Create a buy strategy generator.
"""
raise OperationalException(_format_exception_message('buy_strategy_generator', 'buy'))
def sell_strategy_generator(self, params: Dict[str, Any]) -> Callable:
"""
Create a sell strategy generator.
"""
raise OperationalException(_format_exception_message('sell_strategy_generator', 'sell'))
def protection_space(self) -> List[Dimension]:
"""
Create a protection space.
Only supported by the Parameter interface.
"""
raise OperationalException(_format_exception_message('indicator_space', 'protection'))
def indicator_space(self) -> List[Dimension]:
"""
Create an indicator space.
"""
raise OperationalException(_format_exception_message('indicator_space', 'buy'))
def sell_indicator_space(self) -> List[Dimension]:
"""
Create a sell indicator space.
"""
raise OperationalException(_format_exception_message('sell_indicator_space', 'sell'))
def generate_roi_table(self, params: Dict) -> Dict[int, float]: def generate_roi_table(self, params: Dict) -> Dict[int, float]:
""" """
Create a ROI table. Create a ROI table.

View File

@ -9,7 +9,6 @@ from typing import Dict
from freqtrade.constants import HYPEROPT_LOSS_BUILTIN, USERPATH_HYPEROPTS from freqtrade.constants import HYPEROPT_LOSS_BUILTIN, USERPATH_HYPEROPTS
from freqtrade.exceptions import OperationalException from freqtrade.exceptions import OperationalException
from freqtrade.optimize.hyperopt_interface import IHyperOpt
from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss
from freqtrade.resolvers import IResolver from freqtrade.resolvers import IResolver
@ -17,43 +16,6 @@ from freqtrade.resolvers import IResolver
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class HyperOptResolver(IResolver):
"""
This class contains all the logic to load custom hyperopt class
"""
object_type = IHyperOpt
object_type_str = "Hyperopt"
user_subdir = USERPATH_HYPEROPTS
initial_search_path = None
@staticmethod
def load_hyperopt(config: Dict) -> IHyperOpt:
"""
Load the custom hyperopt class from config parameter
:param config: configuration dictionary
"""
if not config.get('hyperopt'):
raise OperationalException("No Hyperopt set. Please use `--hyperopt` to specify "
"the Hyperopt class to use.")
hyperopt_name = config['hyperopt']
hyperopt = HyperOptResolver.load_object(hyperopt_name, config,
kwargs={'config': config},
extra_dir=config.get('hyperopt_path'))
if not hasattr(hyperopt, 'populate_indicators'):
logger.info("Hyperopt class does not provide populate_indicators() method. "
"Using populate_indicators from the strategy.")
if not hasattr(hyperopt, 'populate_buy_trend'):
logger.info("Hyperopt class does not provide populate_buy_trend() method. "
"Using populate_buy_trend from the strategy.")
if not hasattr(hyperopt, 'populate_sell_trend'):
logger.info("Hyperopt class does not provide populate_sell_trend() method. "
"Using populate_sell_trend from the strategy.")
return hyperopt
class HyperOptLossResolver(IResolver): class HyperOptLossResolver(IResolver):
""" """
This class contains all the logic to load custom hyperopt loss class This class contains all the logic to load custom hyperopt loss class

View File

@ -1,137 +0,0 @@
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
# --- Do not remove these libs ---
from functools import reduce
from typing import Any, Callable, Dict, List
import numpy as np # noqa
import pandas as pd # noqa
from pandas import DataFrame
from skopt.space import Categorical, Dimension, Integer, Real # noqa
from freqtrade.optimize.hyperopt_interface import IHyperOpt
# --------------------------------
# Add your lib to import here
import talib.abstract as ta # noqa
import freqtrade.vendor.qtpylib.indicators as qtpylib
class {{ hyperopt }}(IHyperOpt):
"""
This is a Hyperopt template to get you started.
More information in the documentation: https://www.freqtrade.io/en/latest/hyperopt/
You should:
- Add any lib you need to build your hyperopt.
You must keep:
- The prototypes for the methods: populate_indicators, indicator_space, buy_strategy_generator.
The methods roi_space, generate_roi_table and stoploss_space are not required
and are provided by default.
However, you may override them if you need 'roi' and 'stoploss' spaces that
differ from the defaults offered by Freqtrade.
Sample implementation of these methods will be copied to `user_data/hyperopts` when
creating the user-data directory using `freqtrade create-userdir --userdir user_data`,
or is available online under the following URL:
https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/templates/sample_hyperopt_advanced.py.
"""
@staticmethod
def indicator_space() -> List[Dimension]:
"""
Define your Hyperopt space for searching buy strategy parameters.
"""
return [
{{ buy_space | indent(12) }}
]
@staticmethod
def buy_strategy_generator(params: Dict[str, Any]) -> Callable:
"""
Define the buy strategy parameters to be used by Hyperopt.
"""
def populate_buy_trend(dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Buy strategy Hyperopt will build and use.
"""
conditions = []
# GUARDS AND TRENDS
{{ buy_guards | indent(12) }}
# TRIGGERS
if 'trigger' in params:
if params['trigger'] == 'bb_lower':
conditions.append(dataframe['close'] < dataframe['bb_lowerband'])
if params['trigger'] == 'macd_cross_signal':
conditions.append(qtpylib.crossed_above(
dataframe['macd'], dataframe['macdsignal']
))
if params['trigger'] == 'sar_reversal':
conditions.append(qtpylib.crossed_above(
dataframe['close'], dataframe['sar']
))
# Check that the candle had volume
conditions.append(dataframe['volume'] > 0)
if conditions:
dataframe.loc[
reduce(lambda x, y: x & y, conditions),
'buy'] = 1
return dataframe
return populate_buy_trend
@staticmethod
def sell_indicator_space() -> List[Dimension]:
"""
Define your Hyperopt space for searching sell strategy parameters.
"""
return [
{{ sell_space | indent(12) }}
]
@staticmethod
def sell_strategy_generator(params: Dict[str, Any]) -> Callable:
"""
Define the sell strategy parameters to be used by Hyperopt.
"""
def populate_sell_trend(dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Sell strategy Hyperopt will build and use.
"""
conditions = []
# GUARDS AND TRENDS
{{ sell_guards | indent(12) }}
# TRIGGERS
if 'sell-trigger' in params:
if params['sell-trigger'] == 'sell-bb_upper':
conditions.append(dataframe['close'] > dataframe['bb_upperband'])
if params['sell-trigger'] == 'sell-macd_cross_signal':
conditions.append(qtpylib.crossed_above(
dataframe['macdsignal'], dataframe['macd']
))
if params['sell-trigger'] == 'sell-sar_reversal':
conditions.append(qtpylib.crossed_above(
dataframe['sar'], dataframe['close']
))
# Check that the candle had volume
conditions.append(dataframe['volume'] > 0)
if conditions:
dataframe.loc[
reduce(lambda x, y: x & y, conditions),
'sell'] = 1
return dataframe
return populate_sell_trend

View File

@ -1,180 +0,0 @@
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
# isort: skip_file
# --- Do not remove these libs ---
from functools import reduce
from typing import Any, Callable, Dict, List
import numpy as np # noqa
import pandas as pd # noqa
from pandas import DataFrame
from skopt.space import Categorical, Dimension, Integer, Real # noqa
from freqtrade.optimize.hyperopt_interface import IHyperOpt
# --------------------------------
# Add your lib to import here
import talib.abstract as ta # noqa
import freqtrade.vendor.qtpylib.indicators as qtpylib
class SampleHyperOpt(IHyperOpt):
"""
This is a sample Hyperopt to inspire you.
More information in the documentation: https://www.freqtrade.io/en/latest/hyperopt/
You should:
- Rename the class name to some unique name.
- Add any methods you want to build your hyperopt.
- Add any lib you need to build your hyperopt.
An easier way to get a new hyperopt file is by using
`freqtrade new-hyperopt --hyperopt MyCoolHyperopt`.
You must keep:
- The prototypes for the methods: populate_indicators, indicator_space, buy_strategy_generator.
The methods roi_space, generate_roi_table and stoploss_space are not required
and are provided by default.
However, you may override them if you need 'roi' and 'stoploss' spaces that
differ from the defaults offered by Freqtrade.
Sample implementation of these methods will be copied to `user_data/hyperopts` when
creating the user-data directory using `freqtrade create-userdir --userdir user_data`,
or is available online under the following URL:
https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/templates/sample_hyperopt_advanced.py.
"""
@staticmethod
def indicator_space() -> List[Dimension]:
"""
Define your Hyperopt space for searching buy strategy parameters.
"""
return [
Integer(10, 25, name='mfi-value'),
Integer(15, 45, name='fastd-value'),
Integer(20, 50, name='adx-value'),
Integer(20, 40, name='rsi-value'),
Categorical([True, False], name='mfi-enabled'),
Categorical([True, False], name='fastd-enabled'),
Categorical([True, False], name='adx-enabled'),
Categorical([True, False], name='rsi-enabled'),
Categorical(['boll', 'macd_cross_signal', 'sar_reversal'], name='trigger')
]
@staticmethod
def buy_strategy_generator(params: Dict[str, Any]) -> Callable:
"""
Define the buy strategy parameters to be used by Hyperopt.
"""
def populate_buy_trend(dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Buy strategy Hyperopt will build and use.
"""
long_conditions = []
# GUARDS AND TRENDS
if 'mfi-enabled' in params and params['mfi-enabled']:
long_conditions.append(dataframe['mfi'] < params['mfi-value'])
if 'fastd-enabled' in params and params['fastd-enabled']:
long_conditions.append(dataframe['fastd'] < params['fastd-value'])
if 'adx-enabled' in params and params['adx-enabled']:
long_conditions.append(dataframe['adx'] > params['adx-value'])
if 'rsi-enabled' in params and params['rsi-enabled']:
long_conditions.append(dataframe['rsi'] < params['rsi-value'])
# TRIGGERS
if 'trigger' in params:
if params['trigger'] == 'boll':
long_conditions.append(dataframe['close'] < dataframe['bb_lowerband'])
if params['trigger'] == 'macd_cross_signal':
long_conditions.append(qtpylib.crossed_above(
dataframe['macd'],
dataframe['macdsignal']
))
if params['trigger'] == 'sar_reversal':
long_conditions.append(qtpylib.crossed_above(
dataframe['close'],
dataframe['sar']
))
# Check that volume is not 0
long_conditions.append(dataframe['volume'] > 0)
if long_conditions:
dataframe.loc[
reduce(lambda x, y: x & y, long_conditions),
'buy'] = 1
return dataframe
return populate_buy_trend
@staticmethod
def sell_indicator_space() -> List[Dimension]:
"""
Define your Hyperopt space for searching sell strategy parameters.
"""
return [
Integer(75, 100, name='sell-mfi-value'),
Integer(50, 100, name='sell-fastd-value'),
Integer(50, 100, name='sell-adx-value'),
Integer(60, 100, name='sell-rsi-value'),
Categorical([True, False], name='sell-mfi-enabled'),
Categorical([True, False], name='sell-fastd-enabled'),
Categorical([True, False], name='sell-adx-enabled'),
Categorical([True, False], name='sell-rsi-enabled'),
Categorical(['sell-boll',
'sell-macd_cross_signal',
'sell-sar_reversal'],
name='sell-trigger'
)
]
@staticmethod
def sell_strategy_generator(params: Dict[str, Any]) -> Callable:
"""
Define the sell strategy parameters to be used by Hyperopt.
"""
def populate_sell_trend(dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Sell strategy Hyperopt will build and use.
"""
exit_long_conditions = []
# GUARDS AND TRENDS
if 'sell-mfi-enabled' in params and params['sell-mfi-enabled']:
exit_long_conditions.append(dataframe['mfi'] > params['sell-mfi-value'])
if 'sell-fastd-enabled' in params and params['sell-fastd-enabled']:
exit_long_conditions.append(dataframe['fastd'] > params['sell-fastd-value'])
if 'sell-adx-enabled' in params and params['sell-adx-enabled']:
exit_long_conditions.append(dataframe['adx'] < params['sell-adx-value'])
if 'sell-rsi-enabled' in params and params['sell-rsi-enabled']:
exit_long_conditions.append(dataframe['rsi'] > params['sell-rsi-value'])
# TRIGGERS
if 'sell-trigger' in params:
if params['sell-trigger'] == 'sell-boll':
exit_long_conditions.append(dataframe['close'] > dataframe['bb_upperband'])
if params['sell-trigger'] == 'sell-macd_cross_signal':
exit_long_conditions.append(qtpylib.crossed_above(
dataframe['macdsignal'],
dataframe['macd']
))
if params['sell-trigger'] == 'sell-sar_reversal':
exit_long_conditions.append(qtpylib.crossed_above(
dataframe['sar'],
dataframe['close']
))
# Check that volume is not 0
exit_long_conditions.append(dataframe['volume'] > 0)
if exit_long_conditions:
dataframe.loc[
reduce(lambda x, y: x & y, exit_long_conditions),
'sell'] = 1
return dataframe
return populate_sell_trend

View File

@ -1,272 +0,0 @@
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
# isort: skip_file
# --- Do not remove these libs ---
from functools import reduce
from typing import Any, Callable, Dict, List
import numpy as np # noqa
import pandas as pd # noqa
from pandas import DataFrame
from freqtrade.optimize.space import Categorical, Dimension, Integer, SKDecimal, Real # noqa
from freqtrade.optimize.hyperopt_interface import IHyperOpt
# --------------------------------
# Add your lib to import here
import talib.abstract as ta # noqa
import freqtrade.vendor.qtpylib.indicators as qtpylib
class AdvancedSampleHyperOpt(IHyperOpt):
"""
This is a sample hyperopt to inspire you.
Feel free to customize it.
More information in the documentation: https://www.freqtrade.io/en/latest/hyperopt/
You should:
- Rename the class name to some unique name.
- Add any methods you want to build your hyperopt.
- Add any lib you need to build your hyperopt.
You must keep:
- The prototypes for the methods: populate_indicators, indicator_space, buy_strategy_generator.
The methods roi_space, generate_roi_table and stoploss_space are not required
and are provided by default.
However, you may override them if you need the
'roi' and the 'stoploss' spaces that differ from the defaults offered by Freqtrade.
This sample illustrates how to override these methods.
"""
@staticmethod
def populate_indicators(dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
This method can also be loaded from the strategy, if it doesn't exist in the hyperopt class.
"""
dataframe['adx'] = ta.ADX(dataframe)
macd = ta.MACD(dataframe)
dataframe['macd'] = macd['macd']
dataframe['macdsignal'] = macd['macdsignal']
dataframe['mfi'] = ta.MFI(dataframe)
dataframe['rsi'] = ta.RSI(dataframe)
stoch_fast = ta.STOCHF(dataframe)
dataframe['fastd'] = stoch_fast['fastd']
dataframe['minus_di'] = ta.MINUS_DI(dataframe)
# Bollinger bands
bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
dataframe['bb_lowerband'] = bollinger['lower']
dataframe['bb_upperband'] = bollinger['upper']
dataframe['sar'] = ta.SAR(dataframe)
return dataframe
@staticmethod
def indicator_space() -> List[Dimension]:
"""
Define your Hyperopt space for searching buy strategy parameters.
"""
return [
Integer(10, 25, name='mfi-value'),
Integer(15, 45, name='fastd-value'),
Integer(20, 50, name='adx-value'),
Integer(20, 40, name='rsi-value'),
Categorical([True, False], name='mfi-enabled'),
Categorical([True, False], name='fastd-enabled'),
Categorical([True, False], name='adx-enabled'),
Categorical([True, False], name='rsi-enabled'),
Categorical(['boll', 'macd_cross_signal', 'sar_reversal'], name='trigger')
]
@staticmethod
def buy_strategy_generator(params: Dict[str, Any]) -> Callable:
"""
Define the buy strategy parameters to be used by hyperopt
"""
def populate_buy_trend(dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Buy strategy Hyperopt will build and use
"""
long_conditions = []
# GUARDS AND TRENDS
if 'mfi-enabled' in params and params['mfi-enabled']:
long_conditions.append(dataframe['mfi'] < params['mfi-value'])
if 'fastd-enabled' in params and params['fastd-enabled']:
long_conditions.append(dataframe['fastd'] < params['fastd-value'])
if 'adx-enabled' in params and params['adx-enabled']:
long_conditions.append(dataframe['adx'] > params['adx-value'])
if 'rsi-enabled' in params and params['rsi-enabled']:
long_conditions.append(dataframe['rsi'] < params['rsi-value'])
# TRIGGERS
if 'trigger' in params:
if params['trigger'] == 'boll':
long_conditions.append(dataframe['close'] < dataframe['bb_lowerband'])
if params['trigger'] == 'macd_cross_signal':
long_conditions.append(qtpylib.crossed_above(
dataframe['macd'], dataframe['macdsignal']
))
if params['trigger'] == 'sar_reversal':
long_conditions.append(qtpylib.crossed_above(
dataframe['close'], dataframe['sar']
))
# Check that volume is not 0
long_conditions.append(dataframe['volume'] > 0)
if long_conditions:
dataframe.loc[
reduce(lambda x, y: x & y, long_conditions),
'buy'] = 1
return dataframe
return populate_buy_trend
@staticmethod
def sell_indicator_space() -> List[Dimension]:
"""
Define your Hyperopt space for searching sell strategy parameters.
"""
return [
Integer(75, 100, name='sell-mfi-value'),
Integer(50, 100, name='sell-fastd-value'),
Integer(50, 100, name='sell-adx-value'),
Integer(60, 100, name='sell-rsi-value'),
Categorical([True, False], name='sell-mfi-enabled'),
Categorical([True, False], name='sell-fastd-enabled'),
Categorical([True, False], name='sell-adx-enabled'),
Categorical([True, False], name='sell-rsi-enabled'),
Categorical(['sell-boll',
'sell-macd_cross_signal',
'sell-sar_reversal'],
name='sell-trigger')
]
@staticmethod
def sell_strategy_generator(params: Dict[str, Any]) -> Callable:
"""
Define the sell strategy parameters to be used by hyperopt
"""
def populate_sell_trend(dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Sell strategy Hyperopt will build and use
"""
# print(params)
exit_long_conditions = []
# GUARDS AND TRENDS
if 'sell-mfi-enabled' in params and params['sell-mfi-enabled']:
exit_long_conditions.append(dataframe['mfi'] > params['sell-mfi-value'])
if 'sell-fastd-enabled' in params and params['sell-fastd-enabled']:
exit_long_conditions.append(dataframe['fastd'] > params['sell-fastd-value'])
if 'sell-adx-enabled' in params and params['sell-adx-enabled']:
exit_long_conditions.append(dataframe['adx'] < params['sell-adx-value'])
if 'sell-rsi-enabled' in params and params['sell-rsi-enabled']:
exit_long_conditions.append(dataframe['rsi'] > params['sell-rsi-value'])
# TRIGGERS
if 'sell-trigger' in params:
if params['sell-trigger'] == 'sell-boll':
exit_long_conditions.append(dataframe['close'] > dataframe['bb_upperband'])
if params['sell-trigger'] == 'sell-macd_cross_signal':
exit_long_conditions.append(qtpylib.crossed_above(
dataframe['macdsignal'],
dataframe['macd']
))
if params['sell-trigger'] == 'sell-sar_reversal':
exit_long_conditions.append(qtpylib.crossed_above(
dataframe['sar'],
dataframe['close']
))
# Check that volume is not 0
exit_long_conditions.append(dataframe['volume'] > 0)
if exit_long_conditions:
dataframe.loc[
reduce(lambda x, y: x & y, exit_long_conditions),
'sell'] = 1
return dataframe
return populate_sell_trend
@staticmethod
def generate_roi_table(params: Dict) -> Dict[int, float]:
"""
Generate the ROI table that will be used by Hyperopt
This implementation generates the default legacy Freqtrade ROI tables.
Change it if you need different number of steps in the generated
ROI tables or other structure of the ROI tables.
Please keep it aligned with parameters in the 'roi' optimization
hyperspace defined by the roi_space method.
"""
roi_table = {}
roi_table[0] = params['roi_p1'] + params['roi_p2'] + params['roi_p3']
roi_table[params['roi_t3']] = params['roi_p1'] + params['roi_p2']
roi_table[params['roi_t3'] + params['roi_t2']] = params['roi_p1']
roi_table[params['roi_t3'] + params['roi_t2'] + params['roi_t1']] = 0
return roi_table
@staticmethod
def roi_space() -> List[Dimension]:
"""
Values to search for each ROI steps
Override it if you need some different ranges for the parameters in the
'roi' optimization hyperspace.
Please keep it aligned with the implementation of the
generate_roi_table method.
"""
return [
Integer(10, 120, name='roi_t1'),
Integer(10, 60, name='roi_t2'),
Integer(10, 40, name='roi_t3'),
SKDecimal(0.01, 0.04, decimals=3, name='roi_p1'),
SKDecimal(0.01, 0.07, decimals=3, name='roi_p2'),
SKDecimal(0.01, 0.20, decimals=3, name='roi_p3'),
]
@staticmethod
def stoploss_space() -> List[Dimension]:
"""
Stoploss Value to search
Override it if you need some different range for the parameter in the
'stoploss' optimization hyperspace.
"""
return [
SKDecimal(-0.35, -0.02, decimals=3, name='stoploss'),
]
@staticmethod
def trailing_space() -> List[Dimension]:
"""
Create a trailing stoploss space.
You may override it in your custom Hyperopt class.
"""
return [
# It was decided to always set trailing_stop is to True if the 'trailing' hyperspace
# is used. Otherwise hyperopt will vary other parameters that won't have effect if
# trailing_stop is set False.
# This parameter is included into the hyperspace dimensions rather than assigning
# it explicitly in the code in order to have it printed in the results along with
# other 'trailing' hyperspace parameters.
Categorical([True], name='trailing_stop'),
SKDecimal(0.01, 0.35, decimals=3, name='trailing_stop_positive'),
# 'trailing_stop_positive_offset' should be greater than 'trailing_stop_positive',
# so this intermediate parameter is used as the value of the difference between
# them. The value of the 'trailing_stop_positive_offset' is constructed in the
# generate_trailing_params() method.
# This is similar to the hyperspace dimensions used for constructing the ROI tables.
SKDecimal(0.001, 0.1, decimals=3, name='trailing_stop_positive_offset_p1'),
Categorical([True, False], name='trailing_only_offset_is_reached'),
]

View File

@ -1,8 +0,0 @@
if params.get('mfi-enabled'):
conditions.append(dataframe['mfi'] < params['mfi-value'])
if params.get('fastd-enabled'):
conditions.append(dataframe['fastd'] < params['fastd-value'])
if params.get('adx-enabled'):
conditions.append(dataframe['adx'] > params['adx-value'])
if params.get('rsi-enabled'):
conditions.append(dataframe['rsi'] < params['rsi-value'])

View File

@ -1,2 +0,0 @@
if params.get('rsi-enabled'):
conditions.append(dataframe['rsi'] < params['rsi-value'])

View File

@ -1,9 +0,0 @@
Integer(10, 25, name='mfi-value'),
Integer(15, 45, name='fastd-value'),
Integer(20, 50, name='adx-value'),
Integer(20, 40, name='rsi-value'),
Categorical([True, False], name='mfi-enabled'),
Categorical([True, False], name='fastd-enabled'),
Categorical([True, False], name='adx-enabled'),
Categorical([True, False], name='rsi-enabled'),
Categorical(['bb_lower', 'macd_cross_signal', 'sar_reversal'], name='trigger')

View File

@ -1,3 +0,0 @@
Integer(20, 40, name='rsi-value'),
Categorical([True, False], name='rsi-enabled'),
Categorical(['bb_lower', 'macd_cross_signal', 'sar_reversal'], name='trigger')

View File

@ -1,8 +0,0 @@
if params.get('sell-mfi-enabled'):
conditions.append(dataframe['mfi'] > params['sell-mfi-value'])
if params.get('sell-fastd-enabled'):
conditions.append(dataframe['fastd'] > params['sell-fastd-value'])
if params.get('sell-adx-enabled'):
conditions.append(dataframe['adx'] < params['sell-adx-value'])
if params.get('sell-rsi-enabled'):
conditions.append(dataframe['rsi'] > params['sell-rsi-value'])

View File

@ -1,2 +0,0 @@
if params.get('sell-rsi-enabled'):
conditions.append(dataframe['rsi'] > params['sell-rsi-value'])

View File

@ -1,11 +0,0 @@
Integer(75, 100, name='sell-mfi-value'),
Integer(50, 100, name='sell-fastd-value'),
Integer(50, 100, name='sell-adx-value'),
Integer(60, 100, name='sell-rsi-value'),
Categorical([True, False], name='sell-mfi-enabled'),
Categorical([True, False], name='sell-fastd-enabled'),
Categorical([True, False], name='sell-adx-enabled'),
Categorical([True, False], name='sell-rsi-enabled'),
Categorical(['sell-bb_upper',
'sell-macd_cross_signal',
'sell-sar_reversal'], name='sell-trigger')

View File

@ -1,5 +0,0 @@
Integer(60, 100, name='sell-rsi-value'),
Categorical([True, False], name='sell-rsi-enabled'),
Categorical(['sell-bb_upper',
'sell-macd_cross_signal',
'sell-sar_reversal'], name='sell-trigger')

View File

@ -10,10 +10,10 @@ import pytest
from freqtrade.commands import (start_convert_data, start_create_userdir, start_download_data, from freqtrade.commands import (start_convert_data, start_create_userdir, start_download_data,
start_hyperopt_list, start_hyperopt_show, start_install_ui, start_hyperopt_list, start_hyperopt_show, start_install_ui,
start_list_data, start_list_exchanges, start_list_hyperopts, start_list_data, start_list_exchanges, start_list_markets,
start_list_markets, start_list_strategies, start_list_timeframes, start_list_strategies, start_list_timeframes, start_new_strategy,
start_new_hyperopt, start_new_strategy, start_show_trades, start_show_trades, start_test_pairlist, start_trading,
start_test_pairlist, start_trading, start_webserver) start_webserver)
from freqtrade.commands.deploy_commands import (clean_ui_subdir, download_and_install_ui, from freqtrade.commands.deploy_commands import (clean_ui_subdir, download_and_install_ui,
get_ui_download_url, read_ui_version) get_ui_download_url, read_ui_version)
from freqtrade.configuration import setup_utils_configuration from freqtrade.configuration import setup_utils_configuration
@ -517,37 +517,6 @@ def test_start_new_strategy_no_arg(mocker, caplog):
start_new_strategy(get_args(args)) start_new_strategy(get_args(args))
def test_start_new_hyperopt(mocker, caplog):
wt_mock = mocker.patch.object(Path, "write_text", MagicMock())
mocker.patch.object(Path, "exists", MagicMock(return_value=False))
args = [
"new-hyperopt",
"--hyperopt",
"CoolNewhyperopt"
]
start_new_hyperopt(get_args(args))
assert wt_mock.call_count == 1
assert "CoolNewhyperopt" in wt_mock.call_args_list[0][0][0]
assert log_has_re("Writing hyperopt to .*", caplog)
mocker.patch('freqtrade.commands.deploy_commands.setup_utils_configuration')
mocker.patch.object(Path, "exists", MagicMock(return_value=True))
with pytest.raises(OperationalException,
match=r".* already exists. Please choose another Hyperopt Name\."):
start_new_hyperopt(get_args(args))
def test_start_new_hyperopt_no_arg(mocker):
args = [
"new-hyperopt",
]
with pytest.raises(OperationalException,
match="`new-hyperopt` requires --hyperopt to be set."):
start_new_hyperopt(get_args(args))
def test_start_install_ui(mocker): def test_start_install_ui(mocker):
clean_mock = mocker.patch('freqtrade.commands.deploy_commands.clean_ui_subdir') clean_mock = mocker.patch('freqtrade.commands.deploy_commands.clean_ui_subdir')
get_url_mock = mocker.patch('freqtrade.commands.deploy_commands.get_ui_download_url', get_url_mock = mocker.patch('freqtrade.commands.deploy_commands.get_ui_download_url',
@ -822,37 +791,20 @@ def test_start_list_strategies(mocker, caplog, capsys):
assert "legacy_strategy_v1.py" in captured.out assert "legacy_strategy_v1.py" in captured.out
assert "StrategyTestV2" in captured.out assert "StrategyTestV2" in captured.out
# Test color output
def test_start_list_hyperopts(mocker, caplog, capsys):
args = [ args = [
"list-hyperopts", "list-strategies",
"--hyperopt-path", "--strategy-path",
str(Path(__file__).parent.parent / "optimize" / "hyperopts"), str(Path(__file__).parent.parent / "strategy" / "strats"),
"-1"
] ]
pargs = get_args(args) pargs = get_args(args)
# pargs['config'] = None # pargs['config'] = None
start_list_hyperopts(pargs) start_list_strategies(pargs)
captured = capsys.readouterr() captured = capsys.readouterr()
assert "TestHyperoptLegacy" not in captured.out assert "TestStrategyLegacyV1" in captured.out
assert "legacy_hyperopt.py" not in captured.out assert "legacy_strategy_v1.py" in captured.out
assert "HyperoptTestSepFile" in captured.out assert "StrategyTestV2" in captured.out
assert "test_hyperopt.py" not in captured.out assert "LOAD FAILED" in captured.out
# Test regular output
args = [
"list-hyperopts",
"--hyperopt-path",
str(Path(__file__).parent.parent / "optimize" / "hyperopts"),
]
pargs = get_args(args)
# pargs['config'] = None
start_list_hyperopts(pargs)
captured = capsys.readouterr()
assert "TestHyperoptLegacy" not in captured.out
assert "legacy_hyperopt.py" not in captured.out
assert "HyperoptTestSepFile" in captured.out
def test_start_test_pairlist(mocker, caplog, tickers, default_conf, capsys): def test_start_test_pairlist(mocker, caplog, tickers, default_conf, capsys):

View File

@ -54,7 +54,9 @@ EXCHANGES = {
def exchange_conf(): def exchange_conf():
config = get_default_conf((Path(__file__).parent / "testdata").resolve()) config = get_default_conf((Path(__file__).parent / "testdata").resolve())
config['exchange']['pair_whitelist'] = [] config['exchange']['pair_whitelist'] = []
# config['dry_run'] = False config['exchange']['key'] = ''
config['exchange']['secret'] = ''
config['dry_run'] = False
return config return config

View File

@ -205,7 +205,7 @@ def test_exchange_resolver(default_conf, mocker, caplog):
def test_validate_order_time_in_force(default_conf, mocker, caplog): def test_validate_order_time_in_force(default_conf, mocker, caplog):
caplog.set_level(logging.INFO) caplog.set_level(logging.INFO)
# explicitly test bittrex, exchanges implementing other policies need seperate tests # explicitly test bittrex, exchanges implementing other policies need separate tests
ex = get_patched_exchange(mocker, default_conf, id="bittrex") ex = get_patched_exchange(mocker, default_conf, id="bittrex")
tif = { tif = {
"buy": "gtc", "buy": "gtc",
@ -2515,7 +2515,7 @@ def test_fetch_order(default_conf, mocker, exchange_name, caplog):
@pytest.mark.parametrize("exchange_name", EXCHANGES) @pytest.mark.parametrize("exchange_name", EXCHANGES)
def test_fetch_stoploss_order(default_conf, mocker, exchange_name): def test_fetch_stoploss_order(default_conf, mocker, exchange_name):
# Don't test FTX here - that needs a seperate test # Don't test FTX here - that needs a separate test
if exchange_name == 'ftx': if exchange_name == 'ftx':
return return
default_conf['dry_run'] = True default_conf['dry_run'] = True

View File

@ -16,7 +16,7 @@ def hyperopt_conf(default_conf):
hyperconf.update({ hyperconf.update({
'datadir': Path(default_conf['datadir']), 'datadir': Path(default_conf['datadir']),
'runmode': RunMode.HYPEROPT, 'runmode': RunMode.HYPEROPT,
'hyperopt': 'HyperoptTestSepFile', 'strategy': 'HyperoptableStrategy',
'hyperopt_loss': 'ShortTradeDurHyperOptLoss', 'hyperopt_loss': 'ShortTradeDurHyperOptLoss',
'hyperopt_path': str(Path(__file__).parent / 'hyperopts'), 'hyperopt_path': str(Path(__file__).parent / 'hyperopts'),
'epochs': 1, 'epochs': 1,

View File

@ -1,207 +0,0 @@
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
from functools import reduce
from typing import Any, Callable, Dict, List
import talib.abstract as ta
from pandas import DataFrame
from skopt.space import Categorical, Dimension, Integer
import freqtrade.vendor.qtpylib.indicators as qtpylib
from freqtrade.optimize.hyperopt_interface import IHyperOpt
class HyperoptTestSepFile(IHyperOpt):
"""
Default hyperopt provided by the Freqtrade bot.
You can override it with your own Hyperopt
"""
@staticmethod
def populate_indicators(dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Add several indicators needed for buy and sell strategies defined below.
"""
# ADX
dataframe['adx'] = ta.ADX(dataframe)
# MACD
macd = ta.MACD(dataframe)
dataframe['macd'] = macd['macd']
dataframe['macdsignal'] = macd['macdsignal']
# MFI
dataframe['mfi'] = ta.MFI(dataframe)
# RSI
dataframe['rsi'] = ta.RSI(dataframe)
# Stochastic Fast
stoch_fast = ta.STOCHF(dataframe)
dataframe['fastd'] = stoch_fast['fastd']
# Minus-DI
dataframe['minus_di'] = ta.MINUS_DI(dataframe)
# Bollinger bands
bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
dataframe['bb_lowerband'] = bollinger['lower']
dataframe['bb_upperband'] = bollinger['upper']
# SAR
dataframe['sar'] = ta.SAR(dataframe)
return dataframe
@staticmethod
def buy_strategy_generator(params: Dict[str, Any]) -> Callable:
"""
Define the buy strategy parameters to be used by Hyperopt.
"""
def populate_buy_trend(dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Buy strategy Hyperopt will build and use.
"""
conditions = []
# GUARDS AND TRENDS
if 'mfi-enabled' in params and params['mfi-enabled']:
conditions.append(dataframe['mfi'] < params['mfi-value'])
if 'fastd-enabled' in params and params['fastd-enabled']:
conditions.append(dataframe['fastd'] < params['fastd-value'])
if 'adx-enabled' in params and params['adx-enabled']:
conditions.append(dataframe['adx'] > params['adx-value'])
if 'rsi-enabled' in params and params['rsi-enabled']:
conditions.append(dataframe['rsi'] < params['rsi-value'])
# TRIGGERS
if 'trigger' in params:
if params['trigger'] == 'boll':
conditions.append(dataframe['close'] < dataframe['bb_lowerband'])
if params['trigger'] == 'macd_cross_signal':
conditions.append(qtpylib.crossed_above(
dataframe['macd'],
dataframe['macdsignal']
))
if params['trigger'] == 'sar_reversal':
conditions.append(qtpylib.crossed_above(
dataframe['close'],
dataframe['sar']
))
if conditions:
dataframe.loc[
reduce(lambda x, y: x & y, conditions),
'buy'] = 1
return dataframe
return populate_buy_trend
@staticmethod
def indicator_space() -> List[Dimension]:
"""
Define your Hyperopt space for searching buy strategy parameters.
"""
return [
Integer(10, 25, name='mfi-value'),
Integer(15, 45, name='fastd-value'),
Integer(20, 50, name='adx-value'),
Integer(20, 40, name='rsi-value'),
Categorical([True, False], name='mfi-enabled'),
Categorical([True, False], name='fastd-enabled'),
Categorical([True, False], name='adx-enabled'),
Categorical([True, False], name='rsi-enabled'),
Categorical(['boll', 'macd_cross_signal', 'sar_reversal'], name='trigger')
]
@staticmethod
def sell_strategy_generator(params: Dict[str, Any]) -> Callable:
"""
Define the sell strategy parameters to be used by Hyperopt.
"""
def populate_sell_trend(dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Sell strategy Hyperopt will build and use.
"""
conditions = []
# GUARDS AND TRENDS
if 'sell-mfi-enabled' in params and params['sell-mfi-enabled']:
conditions.append(dataframe['mfi'] > params['sell-mfi-value'])
if 'sell-fastd-enabled' in params and params['sell-fastd-enabled']:
conditions.append(dataframe['fastd'] > params['sell-fastd-value'])
if 'sell-adx-enabled' in params and params['sell-adx-enabled']:
conditions.append(dataframe['adx'] < params['sell-adx-value'])
if 'sell-rsi-enabled' in params and params['sell-rsi-enabled']:
conditions.append(dataframe['rsi'] > params['sell-rsi-value'])
# TRIGGERS
if 'sell-trigger' in params:
if params['sell-trigger'] == 'sell-boll':
conditions.append(dataframe['close'] > dataframe['bb_upperband'])
if params['sell-trigger'] == 'sell-macd_cross_signal':
conditions.append(qtpylib.crossed_above(
dataframe['macdsignal'],
dataframe['macd']
))
if params['sell-trigger'] == 'sell-sar_reversal':
conditions.append(qtpylib.crossed_above(
dataframe['sar'],
dataframe['close']
))
if conditions:
dataframe.loc[
reduce(lambda x, y: x & y, conditions),
'sell'] = 1
return dataframe
return populate_sell_trend
@staticmethod
def sell_indicator_space() -> List[Dimension]:
"""
Define your Hyperopt space for searching sell strategy parameters.
"""
return [
Integer(75, 100, name='sell-mfi-value'),
Integer(50, 100, name='sell-fastd-value'),
Integer(50, 100, name='sell-adx-value'),
Integer(60, 100, name='sell-rsi-value'),
Categorical([True, False], name='sell-mfi-enabled'),
Categorical([True, False], name='sell-fastd-enabled'),
Categorical([True, False], name='sell-adx-enabled'),
Categorical([True, False], name='sell-rsi-enabled'),
Categorical(['sell-boll',
'sell-macd_cross_signal',
'sell-sar_reversal'],
name='sell-trigger')
]
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Based on TA indicators. Should be a copy of same method from strategy.
Must align to populate_indicators in this file.
Only used when --spaces does not include buy space.
"""
dataframe.loc[
(
(dataframe['close'] < dataframe['bb_lowerband']) &
(dataframe['mfi'] < 16) &
(dataframe['adx'] > 25) &
(dataframe['rsi'] < 21)
),
'buy'] = 1
return dataframe
def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Based on TA indicators. Should be a copy of same method from strategy.
Must align to populate_indicators in this file.
Only used when --spaces does not include sell space.
"""
dataframe.loc[
(
(qtpylib.crossed_above(
dataframe['macdsignal'], dataframe['macd']
)) &
(dataframe['fastd'] > 54)
),
'sell'] = 1
return dataframe

View File

@ -17,13 +17,10 @@ from freqtrade.optimize.hyperopt_auto import HyperOptAuto
from freqtrade.optimize.hyperopt_tools import HyperoptTools from freqtrade.optimize.hyperopt_tools import HyperoptTools
from freqtrade.optimize.optimize_reports import generate_strategy_stats from freqtrade.optimize.optimize_reports import generate_strategy_stats
from freqtrade.optimize.space import SKDecimal from freqtrade.optimize.space import SKDecimal
from freqtrade.resolvers.hyperopt_resolver import HyperOptResolver
from freqtrade.strategy.hyper import IntParameter from freqtrade.strategy.hyper import IntParameter
from tests.conftest import (get_args, log_has, log_has_re, patch_exchange, from tests.conftest import (get_args, log_has, log_has_re, patch_exchange,
patched_configuration_load_config_file) patched_configuration_load_config_file)
from .hyperopts.hyperopt_test_sep_file import HyperoptTestSepFile
def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, caplog) -> None: def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, caplog) -> None:
patched_configuration_load_config_file(mocker, default_conf) patched_configuration_load_config_file(mocker, default_conf)
@ -31,7 +28,7 @@ def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, ca
args = [ args = [
'hyperopt', 'hyperopt',
'--config', 'config.json', '--config', 'config.json',
'--hyperopt', 'HyperoptTestSepFile', '--strategy', 'HyperoptableStrategy',
] ]
config = setup_optimize_configuration(get_args(args), RunMode.HYPEROPT) config = setup_optimize_configuration(get_args(args), RunMode.HYPEROPT)
@ -63,7 +60,7 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo
args = [ args = [
'hyperopt', 'hyperopt',
'--config', 'config.json', '--config', 'config.json',
'--hyperopt', 'HyperoptTestSepFile', '--strategy', 'HyperoptableStrategy',
'--datadir', '/foo/bar', '--datadir', '/foo/bar',
'--timeframe', '1m', '--timeframe', '1m',
'--timerange', ':100', '--timerange', ':100',
@ -115,7 +112,7 @@ def test_setup_hyperopt_configuration_stake_amount(mocker, default_conf) -> None
args = [ args = [
'hyperopt', 'hyperopt',
'--config', 'config.json', '--config', 'config.json',
'--hyperopt', 'HyperoptTestSepFile', '--strategy', 'HyperoptableStrategy',
'--stake-amount', '1', '--stake-amount', '1',
'--starting-balance', '2' '--starting-balance', '2'
] ]
@ -133,47 +130,6 @@ def test_setup_hyperopt_configuration_stake_amount(mocker, default_conf) -> None
setup_optimize_configuration(get_args(args), RunMode.HYPEROPT) setup_optimize_configuration(get_args(args), RunMode.HYPEROPT)
def test_hyperoptresolver(mocker, default_conf, caplog) -> None:
patched_configuration_load_config_file(mocker, default_conf)
hyperopt = HyperoptTestSepFile
delattr(hyperopt, 'populate_indicators')
delattr(hyperopt, 'populate_buy_trend')
delattr(hyperopt, 'populate_sell_trend')
mocker.patch(
'freqtrade.resolvers.hyperopt_resolver.HyperOptResolver.load_object',
MagicMock(return_value=hyperopt(default_conf))
)
default_conf.update({'hyperopt': 'HyperoptTestSepFile'})
x = HyperOptResolver.load_hyperopt(default_conf)
assert not hasattr(x, 'populate_indicators')
assert not hasattr(x, 'populate_buy_trend')
assert not hasattr(x, 'populate_sell_trend')
assert log_has("Hyperopt class does not provide populate_indicators() method. "
"Using populate_indicators from the strategy.", caplog)
assert log_has("Hyperopt class does not provide populate_sell_trend() method. "
"Using populate_sell_trend from the strategy.", caplog)
assert log_has("Hyperopt class does not provide populate_buy_trend() method. "
"Using populate_buy_trend from the strategy.", caplog)
assert hasattr(x, "ticker_interval") # DEPRECATED
assert hasattr(x, "timeframe")
def test_hyperoptresolver_wrongname(default_conf) -> None:
default_conf.update({'hyperopt': "NonExistingHyperoptClass"})
with pytest.raises(OperationalException, match=r'Impossible to load Hyperopt.*'):
HyperOptResolver.load_hyperopt(default_conf)
def test_hyperoptresolver_noname(default_conf):
default_conf['hyperopt'] = ''
with pytest.raises(OperationalException,
match="No Hyperopt set. Please use `--hyperopt` to specify "
"the Hyperopt class to use."):
HyperOptResolver.load_hyperopt(default_conf)
def test_start_not_installed(mocker, default_conf, import_fails) -> None: def test_start_not_installed(mocker, default_conf, import_fails) -> None:
start_mock = MagicMock() start_mock = MagicMock()
patched_configuration_load_config_file(mocker, default_conf) patched_configuration_load_config_file(mocker, default_conf)
@ -184,9 +140,7 @@ def test_start_not_installed(mocker, default_conf, import_fails) -> None:
args = [ args = [
'hyperopt', 'hyperopt',
'--config', 'config.json', '--config', 'config.json',
'--hyperopt', 'HyperoptTestSepFile', '--strategy', 'HyperoptableStrategy',
'--hyperopt-path',
str(Path(__file__).parent / "hyperopts"),
'--epochs', '5', '--epochs', '5',
'--hyperopt-loss', 'SharpeHyperOptLossDaily', '--hyperopt-loss', 'SharpeHyperOptLossDaily',
] ]
@ -196,7 +150,7 @@ def test_start_not_installed(mocker, default_conf, import_fails) -> None:
start_hyperopt(pargs) start_hyperopt(pargs)
def test_start(mocker, hyperopt_conf, caplog) -> None: def test_start_no_hyperopt_allowed(mocker, hyperopt_conf, caplog) -> None:
start_mock = MagicMock() start_mock = MagicMock()
patched_configuration_load_config_file(mocker, hyperopt_conf) patched_configuration_load_config_file(mocker, hyperopt_conf)
mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.start', start_mock) mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.start', start_mock)
@ -210,11 +164,9 @@ def test_start(mocker, hyperopt_conf, caplog) -> None:
'--epochs', '5' '--epochs', '5'
] ]
pargs = get_args(args) pargs = get_args(args)
with pytest.raises(OperationalException, match=r"Using separate Hyperopt files has been.*"):
start_hyperopt(pargs) start_hyperopt(pargs)
assert log_has('Starting freqtrade in Hyperopt mode', caplog)
assert start_mock.call_count == 1
def test_start_no_data(mocker, hyperopt_conf) -> None: def test_start_no_data(mocker, hyperopt_conf) -> None:
patched_configuration_load_config_file(mocker, hyperopt_conf) patched_configuration_load_config_file(mocker, hyperopt_conf)
@ -225,11 +177,11 @@ def test_start_no_data(mocker, hyperopt_conf) -> None:
) )
patch_exchange(mocker) patch_exchange(mocker)
# TODO: migrate to strategy-based hyperopt
args = [ args = [
'hyperopt', 'hyperopt',
'--config', 'config.json', '--config', 'config.json',
'--hyperopt', 'HyperoptTestSepFile', '--strategy', 'HyperoptableStrategy',
'--hyperopt-loss', 'SharpeHyperOptLossDaily', '--hyperopt-loss', 'SharpeHyperOptLossDaily',
'--epochs', '5' '--epochs', '5'
] ]
@ -247,7 +199,7 @@ def test_start_filelock(mocker, hyperopt_conf, caplog) -> None:
args = [ args = [
'hyperopt', 'hyperopt',
'--config', 'config.json', '--config', 'config.json',
'--hyperopt', 'HyperoptTestSepFile', '--strategy', 'HyperoptableStrategy',
'--hyperopt-loss', 'SharpeHyperOptLossDaily', '--hyperopt-loss', 'SharpeHyperOptLossDaily',
'--epochs', '5' '--epochs', '5'
] ]
@ -427,66 +379,14 @@ def test_hyperopt_format_results(hyperopt):
def test_populate_indicators(hyperopt, testdatadir) -> None: def test_populate_indicators(hyperopt, testdatadir) -> None:
data = load_data(testdatadir, '1m', ['UNITTEST/BTC'], fill_up_missing=True) data = load_data(testdatadir, '1m', ['UNITTEST/BTC'], fill_up_missing=True)
dataframes = hyperopt.backtesting.strategy.advise_all_indicators(data) dataframes = hyperopt.backtesting.strategy.advise_all_indicators(data)
dataframe = hyperopt.custom_hyperopt.populate_indicators(dataframes['UNITTEST/BTC'], dataframe = dataframes['UNITTEST/BTC']
{'pair': 'UNITTEST/BTC'})
# Check if some indicators are generated. We will not test all of them # Check if some indicators are generated. We will not test all of them
assert 'adx' in dataframe assert 'adx' in dataframe
assert 'mfi' in dataframe assert 'macd' in dataframe
assert 'rsi' in dataframe assert 'rsi' in dataframe
def test_buy_strategy_generator(hyperopt, testdatadir) -> None:
data = load_data(testdatadir, '1m', ['UNITTEST/BTC'], fill_up_missing=True)
dataframes = hyperopt.backtesting.strategy.advise_all_indicators(data)
dataframe = hyperopt.custom_hyperopt.populate_indicators(dataframes['UNITTEST/BTC'],
{'pair': 'UNITTEST/BTC'})
populate_buy_trend = hyperopt.custom_hyperopt.buy_strategy_generator(
{
'adx-value': 20,
'fastd-value': 20,
'mfi-value': 20,
'rsi-value': 20,
'adx-enabled': True,
'fastd-enabled': True,
'mfi-enabled': True,
'rsi-enabled': True,
'trigger': 'bb_lower'
}
)
result = populate_buy_trend(dataframe, {'pair': 'UNITTEST/BTC'})
# Check if some indicators are generated. We will not test all of them
assert 'buy' in result
assert 1 in result['buy']
def test_sell_strategy_generator(hyperopt, testdatadir) -> None:
data = load_data(testdatadir, '1m', ['UNITTEST/BTC'], fill_up_missing=True)
dataframes = hyperopt.backtesting.strategy.advise_all_indicators(data)
dataframe = hyperopt.custom_hyperopt.populate_indicators(dataframes['UNITTEST/BTC'],
{'pair': 'UNITTEST/BTC'})
populate_sell_trend = hyperopt.custom_hyperopt.sell_strategy_generator(
{
'sell-adx-value': 20,
'sell-fastd-value': 75,
'sell-mfi-value': 80,
'sell-rsi-value': 20,
'sell-adx-enabled': True,
'sell-fastd-enabled': True,
'sell-mfi-enabled': True,
'sell-rsi-enabled': True,
'sell-trigger': 'sell-bb_upper'
}
)
result = populate_sell_trend(dataframe, {'pair': 'UNITTEST/BTC'})
# Check if some indicators are generated. We will not test all of them
print(result)
assert 'sell' in result
assert 1 in result['sell']
def test_generate_optimizer(mocker, hyperopt_conf) -> None: def test_generate_optimizer(mocker, hyperopt_conf) -> None:
hyperopt_conf.update({'spaces': 'all', hyperopt_conf.update({'spaces': 'all',
'hyperopt_min_trades': 1, 'hyperopt_min_trades': 1,
@ -527,24 +427,12 @@ def test_generate_optimizer(mocker, hyperopt_conf) -> None:
mocker.patch('freqtrade.optimize.hyperopt.load', return_value={'XRP/BTC': None}) mocker.patch('freqtrade.optimize.hyperopt.load', return_value={'XRP/BTC': None})
optimizer_param = { optimizer_param = {
'adx-value': 0, 'buy_plusdi': 0.02,
'fastd-value': 35, 'buy_rsi': 35,
'mfi-value': 0, 'sell_minusdi': 0.02,
'rsi-value': 0, 'sell_rsi': 75,
'adx-enabled': False, 'protection_cooldown_lookback': 20,
'fastd-enabled': True, 'protection_enabled': True,
'mfi-enabled': False,
'rsi-enabled': False,
'trigger': 'macd_cross_signal',
'sell-adx-value': 0,
'sell-fastd-value': 75,
'sell-mfi-value': 0,
'sell-rsi-value': 0,
'sell-adx-enabled': False,
'sell-fastd-enabled': True,
'sell-mfi-enabled': False,
'sell-rsi-enabled': False,
'sell-trigger': 'macd_cross_signal',
'roi_t1': 60.0, 'roi_t1': 60.0,
'roi_t2': 30.0, 'roi_t2': 30.0,
'roi_t3': 20.0, 'roi_t3': 20.0,
@ -564,29 +452,19 @@ def test_generate_optimizer(mocker, hyperopt_conf) -> None:
'0.00003100 BTC ( 0.00%). ' '0.00003100 BTC ( 0.00%). '
'Avg duration 0:50:00 min.' 'Avg duration 0:50:00 min.'
), ),
'params_details': {'buy': {'adx-enabled': False, 'params_details': {'buy': {'buy_plusdi': 0.02,
'adx-value': 0, 'buy_rsi': 35,
'fastd-enabled': True, },
'fastd-value': 35,
'mfi-enabled': False,
'mfi-value': 0,
'rsi-enabled': False,
'rsi-value': 0,
'trigger': 'macd_cross_signal'},
'roi': {"0": 0.12000000000000001, 'roi': {"0": 0.12000000000000001,
"20.0": 0.02, "20.0": 0.02,
"50.0": 0.01, "50.0": 0.01,
"110.0": 0}, "110.0": 0},
'protection': {}, 'protection': {'protection_cooldown_lookback': 20,
'sell': {'sell-adx-enabled': False, 'protection_enabled': True,
'sell-adx-value': 0, },
'sell-fastd-enabled': True, 'sell': {'sell_minusdi': 0.02,
'sell-fastd-value': 75, 'sell_rsi': 75,
'sell-mfi-enabled': False, },
'sell-mfi-value': 0,
'sell-rsi-enabled': False,
'sell-rsi-value': 0,
'sell-trigger': 'macd_cross_signal'},
'stoploss': {'stoploss': -0.4}, 'stoploss': {'stoploss': -0.4},
'trailing': {'trailing_only_offset_is_reached': False, 'trailing': {'trailing_only_offset_is_reached': False,
'trailing_stop': True, 'trailing_stop': True,
@ -808,11 +686,6 @@ def test_simplified_interface_roi_stoploss(mocker, hyperopt_conf, capsys) -> Non
hyperopt.backtesting.strategy.advise_all_indicators = MagicMock() hyperopt.backtesting.strategy.advise_all_indicators = MagicMock()
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={}) hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
del hyperopt.custom_hyperopt.__class__.buy_strategy_generator
del hyperopt.custom_hyperopt.__class__.sell_strategy_generator
del hyperopt.custom_hyperopt.__class__.indicator_space
del hyperopt.custom_hyperopt.__class__.sell_indicator_space
hyperopt.start() hyperopt.start()
parallel.assert_called_once() parallel.assert_called_once()
@ -843,16 +716,14 @@ def test_simplified_interface_all_failed(mocker, hyperopt_conf) -> None:
hyperopt_conf.update({'spaces': 'all', }) hyperopt_conf.update({'spaces': 'all', })
mocker.patch('freqtrade.optimize.hyperopt_auto.HyperOptAuto._generate_indicator_space',
return_value=[])
hyperopt = Hyperopt(hyperopt_conf) hyperopt = Hyperopt(hyperopt_conf)
hyperopt.backtesting.strategy.advise_all_indicators = MagicMock() hyperopt.backtesting.strategy.advise_all_indicators = MagicMock()
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={}) hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
del hyperopt.custom_hyperopt.__class__.buy_strategy_generator with pytest.raises(OperationalException, match=r"The 'protection' space is included into *"):
del hyperopt.custom_hyperopt.__class__.sell_strategy_generator
del hyperopt.custom_hyperopt.__class__.indicator_space
del hyperopt.custom_hyperopt.__class__.sell_indicator_space
with pytest.raises(OperationalException, match=r"The 'buy' space is included into *"):
hyperopt.start() hyperopt.start()
@ -889,11 +760,6 @@ def test_simplified_interface_buy(mocker, hyperopt_conf, capsys) -> None:
hyperopt.backtesting.strategy.advise_all_indicators = MagicMock() hyperopt.backtesting.strategy.advise_all_indicators = MagicMock()
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={}) hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
# TODO: sell_strategy_generator() is actually not called because
# run_optimizer_parallel() is mocked
del hyperopt.custom_hyperopt.__class__.sell_strategy_generator
del hyperopt.custom_hyperopt.__class__.sell_indicator_space
hyperopt.start() hyperopt.start()
parallel.assert_called_once() parallel.assert_called_once()
@ -943,11 +809,6 @@ def test_simplified_interface_sell(mocker, hyperopt_conf, capsys) -> None:
hyperopt.backtesting.strategy.advise_all_indicators = MagicMock() hyperopt.backtesting.strategy.advise_all_indicators = MagicMock()
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={}) hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
# TODO: buy_strategy_generator() is actually not called because
# run_optimizer_parallel() is mocked
del hyperopt.custom_hyperopt.__class__.buy_strategy_generator
del hyperopt.custom_hyperopt.__class__.indicator_space
hyperopt.start() hyperopt.start()
parallel.assert_called_once() parallel.assert_called_once()
@ -964,13 +825,12 @@ def test_simplified_interface_sell(mocker, hyperopt_conf, capsys) -> None:
assert hasattr(hyperopt, "position_stacking") assert hasattr(hyperopt, "position_stacking")
@pytest.mark.parametrize("method,space", [ @pytest.mark.parametrize("space", [
('buy_strategy_generator', 'buy'), ('buy'),
('indicator_space', 'buy'), ('sell'),
('sell_strategy_generator', 'sell'), ('protection'),
('sell_indicator_space', 'sell'),
]) ])
def test_simplified_interface_failed(mocker, hyperopt_conf, method, space) -> None: def test_simplified_interface_failed(mocker, hyperopt_conf, space) -> None:
mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock()) mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
mocker.patch('freqtrade.optimize.hyperopt.file_dump_json') mocker.patch('freqtrade.optimize.hyperopt.file_dump_json')
mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data', mocker.patch('freqtrade.optimize.backtesting.Backtesting.load_bt_data',
@ -979,6 +839,8 @@ def test_simplified_interface_failed(mocker, hyperopt_conf, method, space) -> No
'freqtrade.optimize.hyperopt.get_timerange', 'freqtrade.optimize.hyperopt.get_timerange',
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13))) MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
) )
mocker.patch('freqtrade.optimize.hyperopt_auto.HyperOptAuto._generate_indicator_space',
return_value=[])
patch_exchange(mocker) patch_exchange(mocker)
@ -988,8 +850,6 @@ def test_simplified_interface_failed(mocker, hyperopt_conf, method, space) -> No
hyperopt.backtesting.strategy.advise_all_indicators = MagicMock() hyperopt.backtesting.strategy.advise_all_indicators = MagicMock()
hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={}) hyperopt.custom_hyperopt.generate_roi_table = MagicMock(return_value={})
delattr(hyperopt.custom_hyperopt.__class__, method)
with pytest.raises(OperationalException, match=f"The '{space}' space is included into *"): with pytest.raises(OperationalException, match=f"The '{space}' space is included into *"):
hyperopt.start() hyperopt.start()
@ -999,7 +859,6 @@ def test_in_strategy_auto_hyperopt(mocker, hyperopt_conf, tmpdir, fee) -> None:
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
(Path(tmpdir) / 'hyperopt_results').mkdir(parents=True) (Path(tmpdir) / 'hyperopt_results').mkdir(parents=True)
# No hyperopt needed # No hyperopt needed
del hyperopt_conf['hyperopt']
hyperopt_conf.update({ hyperopt_conf.update({
'strategy': 'HyperoptableStrategy', 'strategy': 'HyperoptableStrategy',
'user_data_dir': Path(tmpdir), 'user_data_dir': Path(tmpdir),

View File

@ -68,7 +68,7 @@ def test_PairLocks(use_db):
# Global lock # Global lock
PairLocks.lock_pair('*', lock_time) PairLocks.lock_pair('*', lock_time)
assert PairLocks.is_global_lock(lock_time + timedelta(minutes=-50)) assert PairLocks.is_global_lock(lock_time + timedelta(minutes=-50))
# Global lock also locks every pair seperately # Global lock also locks every pair separately
assert PairLocks.is_pair_locked(pair, lock_time + timedelta(minutes=-50)) assert PairLocks.is_pair_locked(pair, lock_time + timedelta(minutes=-50))
assert PairLocks.is_pair_locked('XRP/USDT', lock_time + timedelta(minutes=-50)) assert PairLocks.is_pair_locked('XRP/USDT', lock_time + timedelta(minutes=-50))

View File

@ -74,16 +74,12 @@ def test_copy_sample_files(mocker, default_conf, caplog) -> None:
copymock = mocker.patch('shutil.copy', MagicMock()) copymock = mocker.patch('shutil.copy', MagicMock())
copy_sample_files(Path('/tmp/bar')) copy_sample_files(Path('/tmp/bar'))
assert copymock.call_count == 5 assert copymock.call_count == 3
assert copymock.call_args_list[0][0][1] == str( assert copymock.call_args_list[0][0][1] == str(
Path('/tmp/bar') / 'strategies/sample_strategy.py') Path('/tmp/bar') / 'strategies/sample_strategy.py')
assert copymock.call_args_list[1][0][1] == str( assert copymock.call_args_list[1][0][1] == str(
Path('/tmp/bar') / 'hyperopts/sample_hyperopt_advanced.py')
assert copymock.call_args_list[2][0][1] == str(
Path('/tmp/bar') / 'hyperopts/sample_hyperopt_loss.py') Path('/tmp/bar') / 'hyperopts/sample_hyperopt_loss.py')
assert copymock.call_args_list[3][0][1] == str( assert copymock.call_args_list[2][0][1] == str(
Path('/tmp/bar') / 'hyperopts/sample_hyperopt.py')
assert copymock.call_args_list[4][0][1] == str(
Path('/tmp/bar') / 'notebooks/strategy_analysis_example.ipynb') Path('/tmp/bar') / 'notebooks/strategy_analysis_example.ipynb')