Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hyperopt results improvement request #7342

Open
sanoj2021 opened this issue Sep 4, 2022 · 23 comments
Open

Hyperopt results improvement request #7342

sanoj2021 opened this issue Sep 4, 2022 · 23 comments
Labels
Hyperopt Hyperopt related issues and pull requests

Comments

@sanoj2021
Copy link

Describe your environment

(if applicable)

  • Operating system: Linux Manjaro
  • Python Version: 3.10.0 (python -V)
  • CCXT version: 1.92.60 (pip freeze | grep ccxt)
  • Freqtrade Version: 2022.8.dev-a6d78a861 (freqtrade -V or docker-compose run --rm freqtrade -V for Freqtrade running in docker)

Describe the enhancement

Hi,
Maybe someone considers the following a reasonable contribution...
It would be nice to get a feedback from hyperopting about the "places" of the good (and best) results in the parameter spaces. The goal of this is to make better estimates about the robustness of the found optimal parameter configuration in terms of "how robust is the solustion to small variations in the optimal parameters".
One can think of e.g. some form of 2D graph of the loss in a varying 2d-parameter space. Or, even inclusion of the particular parameters of each epoch in the live log or a log file would suffice.

In case I missed the according feature please let me know. Thanks

@xmatthias xmatthias added the Hyperopt Hyperopt related issues and pull requests label Sep 4, 2022
@xmatthias
Copy link
Member

a varying 2d-parameter space

Hyperopt is not using 2d spaces, but n-dimensional spaces (unless you use only 2 parameters).

As such - i'm not aware how it should be possible visualize a n-dimensional space (take 6 or 7 as an example - but obviously it'll have to work for everything).

It'd be possible up to 3 spaces - as that'd then be a 3d image - but I've rarely (if ever) seen strategies that only use 3 parameters.

Sure it would e nice - but i don't think it's possible to visualize in a human-readable format.


i'll leave this open for some time so you can maybe provide us with some links or examples on what you'd imagine (please also keep higher dimensional data in mind, so images of 2d/3d spaces will not suffice) - but for now, i doubt we'll be keeping this open as enhancement, as it's an enhancement that'd stay open forever - as there's no technical solution.

@sanoj2021
Copy link
Author

sanoj2021 commented Sep 4, 2022

Hi, Thank you in any regards
ok I try to explain it differently. I am aware of the n-dimensionality of the hyper parameter space.
My suggestion is to let the user choose 2 (or 3 if you like the colored 3D plots) of the many parameters which he wants for the 3D plot. This should work in a separate command after hyperopting (or passing the 2 desired parameters to the hyperopt command).
I could even imagine that it could work just plotting every parameter combination as in many cases I have only <10 parameters. The fact that this would produce like some 10 to 100 plots is no problem in my eyes, would just give a big output file.
Please rethink keeping this open as an enhancement as you did not considered just printing all the tried parameter combinations to a log file, which would be alright for me aswell.

(with varying 2d parameter space i meant to let the user choose 2...)

@xmatthias
Copy link
Member

i'm not sure this would provide actual benefits to the user (other than wasting their time looking at questionable information).

especially with many parameters, you'll not be able to manually digest "is this good".
If you were - why hyperopt in the first place?

if you assume for example "rsi was < 14 - so this is a non-robust result" - then your parameter range was wrong - as you didn't give hyperopt this additional information - which you had and are now using to determine that the result is "not robust".

@sanoj2021
Copy link
Author

sanoj2021 commented Sep 4, 2022 via email

@italodamato
Copy link
Contributor

I have a rough implementation in my repo. I don't have time to clean it up and create a PR at the moment but it'd be great if you or someone else could implement it. It's been INCREDIBLY useful so far.

This is what it looks like
Screenshot 2022-09-05 at 17 5 39

@sanoj2021
Copy link
Author

Wonderful, looks perfectly well and like what i was searching for. I doubt that I have the skills to do this, because I never did a PR, but will have a look definetly.

@xmatthias
Copy link
Member

For anyone considering this - we'll not be adding a dependency on matplotlib.

we use plotly as plotting library - we won't need another one (which also doesn't provide any interactivity on top of it).

@italodamato
Copy link
Contributor

I don't think it's necessary as I'm using from skopt.plots import plot_evaluations, plot_objective @xmatthias

@sanoj2021
Copy link
Author

Could you please serve a brief explanation on how this cn be used, or does it plot right away when using freqtrade hyperopt?

@xmatthias
Copy link
Member

I don't think it's necessary as I'm using from skopt.plots import plot_evaluations, plot_objective @xmatthias

the plots look too much like matplotlib to be coincidence.

Go on and try to uninstall matplotlib - skopt has an optional dependency to matplotlib - so i'm pretty sure that it'll break the moment matplotlib is no longer installed.

@italodamato
Copy link
Contributor

Could you please serve a brief explanation on how this cn be used, or does it plot right away when using freqtrade hyperopt?

It plots once per epoch when hyperopting based on the points it tried so far.

the plots look too much like matplotlib to be coincidence.

yes it uses matplotlib under the hood but it doesn't require an additional direct dependency.

@xmatthias
Copy link
Member

yes it uses matplotlib under the hood but it doesn't require an additional direct dependency.

that's not true - it'll require matplotlib to be installed.
If we assume it's not a depenency we install - it'll not be functional (we don't install optional dependencies of packages by default).
That means you'll need to have a ton of checks in place to ensure this code is never called if the dependency is not installed.

@sanoj2021
Copy link
Author

sanoj2021 commented Sep 6, 2022

Should be possible to transcript it to use plotly, only question remains who wants to put work in this and has the know how

@italodamato
Copy link
Contributor

Just confirming the matplotlib needs to be installed. I've tried to run on a fresh installation and it doesn't run without it. ;)

@sanoj2021
Copy link
Author

sanoj2021 commented Sep 23, 2022

Getting a IndexError: tuple index out of range when running freqtrade hyperopt ... in a fresh copy of your repo from here with matplotlib installed in a new conda env.

As your link refers to a tree which is not directly copyable by 'git clone ...' I just used the zip extraction. Is this a wrong approach?

Any suggestions?

Traceback (most recent call last):
  File ".../freqtrade-1/freqtrade/main.py", line 37, in main
    return_code = args['func'](args)
  File ".../freqtrade-1/freqtrade/commands/optimize_commands.py", line 107, in start_hyperopt
    hyperopt.start()
  File ".../freqtrade-1/freqtrade/optimize/hyperopt.py", line 572, in start
    self.plot_optimizer(res, path='user_data/scripts')
  File ".../freqtrade-1/freqtrade/optimize/hyperopt.py", line 627, in plot_optimizer
    ax = plot_evaluations(res)
  File ".../anaconda3/envs/freqtrade-1-conda/lib/python3.10/site-packages/skopt/plots.py", line 855, in plot_evaluations
    return _format_scatter_plot_axes(ax, space, ylabel="Number of samples",
  File ".../anaconda3/envs/freqtrade-1-conda/lib/python3.10/site-packages/skopt/plots.py", line 415, in _format_scatter_plot_axes
    [l.set_rotation(45) for l in ax_.get_xticklabels()]
  File ".../anaconda3/envs/freqtrade-1-conda/lib/python3.10/site-packages/matplotlib/axes/_base.py", line 73, in wrapper
    return get_method(self)(*args, **kwargs)
  File ".../anaconda3/envs/freqtrade-1-conda/lib/python3.10/site-packages/matplotlib/axis.py", line 1388, in get_ticklabels
    return self.get_majorticklabels()
  File ".../anaconda3/envs/freqtrade-1-conda/lib/python3.10/site-packages/matplotlib/axis.py", line 1345, in get_majorticklabels
    self._update_ticks()
  File ".../anaconda3/envs/freqtrade-1-conda/lib/python3.10/site-packages/matplotlib/axis.py", line 1191, in _update_ticks
    major_labels = self.major.formatter.format_ticks(major_locs)
  File ".../anaconda3/envs/freqtrade-1-conda/lib/python3.10/site-packages/matplotlib/ticker.py", line 233, in format_ticks
    return [self(value, i) for i, value in enumerate(values)]
  File ".../anaconda3/envs/freqtrade-1-conda/lib/python3.10/site-packages/matplotlib/ticker.py", line 233, in <listcomp>
    return [self(value, i) for i, value in enumerate(values)]
  File ".../anaconda3/envs/freqtrade-1-conda/lib/python3.10/site-packages/matplotlib/ticker.py", line 340, in __call__
    return self.func(x, pos)
  File ".../anaconda3/envs/freqtrade-1-conda/lib/python3.10/site-packages/skopt/plots.py", line 1337, in _cat_format
    return str(dimension.categories[int(x)])
  IndexError: tuple index out of range

@italodamato
Copy link
Contributor

@sanoj2021 this is the branch that I use, I'm not sure what you did exactly plot-hyperopt-stats, you should be able to clone the repo and checkout in that branch

@sanoj2021
Copy link
Author

Ok, here's what i did:

git clone https://github.com/italodamato/freqtrade-1.git
cd freqtrade-1
git checkout cdca65c090f7ef7350b0013907866710ee1301a4
git switch -c my-develop-branch
conda env create -n freqtrade-conda-1 -f environment.yml
conda activate freqtrade-conda-1
freqtrade create-userdir --userdir user_data
freqtrade new-config --config config.json

After setting everything in the config.json and copying my strategy file and candle data into the user-data/strategies folder:

freqtrade hyperopt --hyperopt-loss SortinoHyperOptLoss --spaces buy sell --strategy MyStrategy -e 1000 --timerange 20220920- -j -2 --print-all

The same strategy file runs without problems on the official freqtrade version.
Did I miss something?
Is the git switch practice correct or rather needless?
Any advice is highly appreciated, thanks!

@sanoj2021
Copy link
Author

This is my log from where the actual hyperopting should start:

2022-09-23 21:12:12,157 - freqtrade.optimize.hyperopt - INFO - Using estimator ET.
2022-09-23 21:12:12,169 - freqtrade.optimize.hyperopt - INFO - Effective number of parallel workers used: 7
a
c
.../freqtrade-1/freqtrade/optimize/optimize_reports.py:174: FutureWarning: iteritems is deprecated and will be removed in a future version. Use .items instead.
  for tag, count in results[tag_type].value_counts().iteritems():
.../freqtrade-1/freqtrade/optimize/optimize_reports.py:200: FutureWarning: iteritems is deprecated and will be removed in a future version. Use .items instead.
  for reason, count in results['exit_reason'].value_counts().iteritems():
.../freqtrade-1/freqtrade/optimize/optimize_reports.py:174: FutureWarning: iteritems is deprecated and will be removed in a future version. Use .items instead.
  for tag, count in results[tag_type].value_counts().iteritems():
.../freqtrade-1/freqtrade/optimize/optimize_reports.py:200: FutureWarning: iteritems is deprecated and will be removed in a future version. Use .items instead.
  for reason, count in results['exit_reason'].value_counts().iteritems():
.../freqtrade-1/freqtrade/optimize/optimize_reports.py:174: FutureWarning: iteritems is deprecated and will be removed in a future version. Use .items instead.
  for tag, count in results[tag_type].value_counts().iteritems():
.../freqtrade-1/freqtrade/optimize/optimize_reports.py:200: FutureWarning: iteritems is deprecated and will be removed in a future version. Use .items instead.
  for reason, count in results['exit_reason'].value_counts().iteritems():
.../freqtrade-1/freqtrade/optimize/optimize_reports.py:174: FutureWarning: iteritems is deprecated and will be removed in a future version. Use .items instead.
  for tag, count in results[tag_type].value_counts().iteritems():
.../freqtrade-1/freqtrade/optimize/optimize_reports.py:200: FutureWarning: iteritems is deprecated and will be removed in a future version. Use .items instead.
  for reason, count in results['exit_reason'].value_counts().iteritems():
.../freqtrade-1/freqtrade/data/btanalysis.py:344: FutureWarning: In a future version, `df.iloc[:, i] = newvals` will attempt to set the values inplace instead of always setting a new array. To retain the old behavior, use either `df[df.columns[i]] = newvals` or, if columns are non-unique, `df.isetitem(i, newvals)`
  df.loc[:, 'close_date'] = pd.to_datetime(df['close_date'], utc=True)
.../freqtrade-1/freqtrade/data/btanalysis.py:345: FutureWarning: In a future version, `df.iloc[:, i] = newvals` will attempt to set the values inplace instead of always setting a new array. To retain the old behavior, use either `df[df.columns[i]] = newvals` or, if columns are non-unique, `df.isetitem(i, newvals)`
  df.loc[:, 'open_date'] = pd.to_datetime(df['open_date'], utc=True)
.../freqtrade-1/freqtrade/optimize/optimize_reports.py:174: FutureWarning: iteritems is deprecated and will be removed in a future version. Use .items instead.
  for tag, count in results[tag_type].value_counts().iteritems():
.../freqtrade-1/freqtrade/optimize/optimize_reports.py:200: FutureWarning: iteritems is deprecated and will be removed in a future version. Use .items instead.
  for reason, count in results['exit_reason'].value_counts().iteritems():
.../freqtrade-1/freqtrade/data/btanalysis.py:344: FutureWarning: In a future version, `df.iloc[:, i] = newvals` will attempt to set the values inplace instead of always setting a new array. To retain the old behavior, use either `df[df.columns[i]] = newvals` or, if columns are non-unique, `df.isetitem(i, newvals)`
  df.loc[:, 'close_date'] = pd.to_datetime(df['close_date'], utc=True)
.../freqtrade-1/freqtrade/data/btanalysis.py:345: FutureWarning: In a future version, `df.iloc[:, i] = newvals` will attempt to set the values inplace instead of always setting a new array. To retain the old behavior, use either `df[df.columns[i]] = newvals` or, if columns are non-unique, `df.isetitem(i, newvals)`
  df.loc[:, 'open_date'] = pd.to_datetime(df['open_date'], utc=True)
.../freqtrade-1/freqtrade/optimize/optimize_reports.py:362: FutureWarning: iteritems is deprecated and will be removed in a future version. Use .items instead.
  daily_profit_list = [(str(idx.date()), val) for idx, val in daily_profit.iteritems()]
.../freqtrade-1/freqtrade/optimize/optimize_reports.py:174: FutureWarning: iteritems is deprecated and will be removed in a future version. Use .items instead.
  for tag, count in results[tag_type].value_counts().iteritems():
.../freqtrade-1/freqtrade/optimize/optimize_reports.py:200: FutureWarning: iteritems is deprecated and will be removed in a future version. Use .items instead.
  for reason, count in results['exit_reason'].value_counts().iteritems():
.../freqtrade-1/freqtrade/optimize/optimize_reports.py:362: FutureWarning: iteritems is deprecated and will be removed in a future version. Use .items instead.
  daily_profit_list = [(str(idx.date()), val) for idx, val in daily_profit.iteritems()]
.../freqtrade-1/freqtrade/data/btanalysis.py:344: FutureWarning: In a future version, `df.iloc[:, i] = newvals` will attempt to set the values inplace instead of always setting a new array. To retain the old behavior, use either `df[df.columns[i]] = newvals` or, if columns are non-unique, `df.isetitem(i, newvals)`
  df.loc[:, 'close_date'] = pd.to_datetime(df['close_date'], utc=True)
.../freqtrade-1/freqtrade/data/btanalysis.py:345: FutureWarning: In a future version, `df.iloc[:, i] = newvals` will attempt to set the values inplace instead of always setting a new array. To retain the old behavior, use either `df[df.columns[i]] = newvals` or, if columns are non-unique, `df.isetitem(i, newvals)`
  df.loc[:, 'open_date'] = pd.to_datetime(df['open_date'], utc=True)
.../freqtrade-1/freqtrade/optimize/optimize_reports.py:174: FutureWarning: iteritems is deprecated and will be removed in a future version. Use .items instead.
  for tag, count in results[tag_type].value_counts().iteritems():
.../freqtrade-1/freqtrade/optimize/optimize_reports.py:200: FutureWarning: iteritems is deprecated and will be removed in a future version. Use .items instead.
  for reason, count in results['exit_reason'].value_counts().iteritems():
.../freqtrade-1/freqtrade/optimize/optimize_reports.py:362: FutureWarning: iteritems is deprecated and will be removed in a future version. Use .items instead.
  daily_profit_list = [(str(idx.date()), val) for idx, val in daily_profit.iteritems()]

2022-09-23 21:12:59,511 - freqtrade - ERROR - Fatal exception!
Traceback (most recent call last):
  File ".../freqtrade-1/freqtrade/main.py", line 37, in main
    return_code = args['func'](args)
  File ".../freqtrade-1/freqtrade/commands/optimize_commands.py", line 107, in start_hyperopt
    hyperopt.start()
  File ".../freqtrade-1/freqtrade/optimize/hyperopt.py", line 567, in start
    self.plot_optimizer(res, path='user_data/scripts')
  File ".../freqtrade-1/freqtrade/optimize/hyperopt.py", line 622, in plot_optimizer
    ax = plot_evaluations(res)
  File ".../anaconda3/envs/freqtrade-conda-1/lib/python3.10/site-packages/skopt/plots.py", line 855, in plot_evaluations
    return _format_scatter_plot_axes(ax, space, ylabel="Number of samples",
  File ".../anaconda3/envs/freqtrade-conda-1/lib/python3.10/site-packages/skopt/plots.py", line 415, in _format_scatter_plot_axes
    [l.set_rotation(45) for l in ax_.get_xticklabels()]
  File ".../anaconda3/envs/freqtrade-conda-1/lib/python3.10/site-packages/matplotlib/axes/_base.py", line 73, in wrapper
    return get_method(self)(*args, **kwargs)
  File ".../anaconda3/envs/freqtrade-conda-1/lib/python3.10/site-packages/matplotlib/axis.py", line 1388, in get_ticklabels
    return self.get_majorticklabels()
  File ".../anaconda3/envs/freqtrade-conda-1/lib/python3.10/site-packages/matplotlib/axis.py", line 1345, in get_majorticklabels
    self._update_ticks()
  File ".../anaconda3/envs/freqtrade-conda-1/lib/python3.10/site-packages/matplotlib/axis.py", line 1191, in _update_ticks
    major_labels = self.major.formatter.format_ticks(major_locs)
  File ".../anaconda3/envs/freqtrade-conda-1/lib/python3.10/site-packages/matplotlib/ticker.py", line 233, in format_ticks
    return [self(value, i) for i, value in enumerate(values)]
  File ".../anaconda3/envs/freqtrade-conda-1/lib/python3.10/site-packages/matplotlib/ticker.py", line 233, in <listcomp>
    return [self(value, i) for i, value in enumerate(values)]
  File ".../anaconda3/envs/freqtrade-conda-1/lib/python3.10/site-packages/matplotlib/ticker.py", line 340, in __call__
    return self.func(x, pos)
  File ".../anaconda3/envs/freqtrade-conda-1/lib/python3.10/site-packages/skopt/plots.py", line 1337, in _cat_format
    return str(dimension.categories[int(x)])
IndexError: tuple index out of range

@italodamato
Copy link
Contributor

I think the key info I need is which parameter types you are trying to optimize and their ranges. Could you provide the section of your strategy in which they are defined? Also, try to set just a couple of them as optimize=True to see if maybe that's causing the issue.

@nartes
Copy link

nartes commented Sep 24, 2022

@sanoj2021 how much parameters does your strategy have?

@sanoj2021
Copy link
Author

@nartes The strategy uses 10 parameters for each pair and I tested it with 2 pairs: 2 CategoricalParameter, 6 DecimalParameter and 4 IntParameter.
The IntParameter have 10 value range each.
The DecimalParameter some have 25 values, some 7.
The CategoricalParameter have 4.
@italodamato I will try activating just some few parameters and let you know what happens. Here is the part of my strategy of the parameter definition for one pair:

    loss_methods = ["soft_l1", "huber", "cauchy", "arctan"]

    f_param_lower = 0.1
    f_param_upper = 0.7
    f_param_decimals = 1

    buy_interval_lower = 2
    buy_interval_upper = 13

    buy_peak_low_lower = -5
    buy_peak_low_upper = 0
    buy_peak_high_lower = 0
    buy_peak_high_upper = 5

    buy_threshold_lower = -5
    buy_threshold_upper = 5

    # 1st pair
    p1_buy_loss = CategoricalParameter(loss_methods, default="cauchy", space='buy', optimize=True, load=True)
    p1_buy_f_param = DecimalParameter(low=f_param_lower, high=f_param_upper, decimals=f_param_decimals, default=0.4, space="buy", optimize=True, load=True)
    p1_buy_interval = IntParameter(low=buy_interval_lower, high=buy_interval_upper, default=12, space='buy', optimize=True, load=True)
    p1_buy_peak_low = DecimalParameter(low=buy_peak_low_lower, high=buy_peak_low_upper, decimals=1, default=-1, space="buy", optimize=True, load=True)
    p1_buy_peak_high = DecimalParameter(low=buy_peak_high_lower, high=buy_peak_high_upper, decimals=1, default=1, space="buy", optimize=True, load=True)
    p1_buy_threshold = IntParameter(low=buy_threshold_lower, high=buy_threshold_upper, default=0, space='buy', optimize=True, load=True)
    p1_sell_loss = CategoricalParameter(loss_methods, default="cauchy", space='sell', optimize=True, load=True)
    p1_sell_f_param = DecimalParameter(low=f_param_lower, high=f_param_upper, decimals=f_param_decimals, default=0.2, space="sell", optimize=True, load=True)
    p1_sell_interval = IntParameter(low=sell_interval_lower, high=sell_interval_upper, default=12, space='sell', optimize=True, load=True)
    p1_sell_peak_low = DecimalParameter(low=sell_peak_low_lower, high=sell_peak_low_upper, decimals=1, default=-1, space="sell", optimize=True, load=True)
    p1_sell_peak_high = DecimalParameter(low=sell_peak_high_lower, high=sell_peak_high_upper, decimals=1, default=1, space="sell", optimize=True, load=True)
    p1_sell_threshold = IntParameter(low=sell_threshold_lower, high=sell_threshold_upper, default=0, space='sell', optimize=True, load=True)

@nartes
Copy link

nartes commented Sep 24, 2022

@sanoj2021 from statistical standpoint, it is like you randomly sample parameters, obtain backtest results versus input parameters. Then just try to correlate numeric parameters versus profit, or if its categorical one - then A/B testing.

It also makes sense to examine manually some random deals, just to see what pattern the strategy has detected, and what was the decision.

I've been recently working on support line indicator.

Here are some examples:

A sample of detected support line with some intermediate visuals.

圖片

A table with 10 samples containing parameters versus performance metrics.

圖片

@sanoj2021
Copy link
Author

sanoj2021 commented Sep 24, 2022

@nartes You're right, I do random sampling, but isn't that the whole point while hyperopting? Generally I have fairly good understanding of how hyperopting and backtesting or bayesian optimization works.

Then just try to correlate numeric parameters versus profit

That's what I was searching for in the first place and found in the plotting option of @italodamato 's custom branch. My current problem is solely related to getting this to work. (I may also just did something wrong with the installation of the custom branch. Someone kindly likes to have a look on my installation procedure a few posts above?)

It also makes sense to examine manually some random deals

This seems quite interesting to me, (how) can I add manual random examination in hyperopting?

But most importantly I would like to know how you did get to the table with parameters vs performance parameters? ... as this would also perfectly well give me all I need. Do you possibly share your steps?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Hyperopt Hyperopt related issues and pull requests
Projects
None yet
Development

No branches or pull requests

4 participants