Created
September 25, 2019 17:39
-
-
Save hroff-1902/201e8090de74e6fec035c345913aadee to your computer and use it in GitHub Desktop.
Advanced Hyperopt technique
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Place this file into into `user_data/hyperopts`. | |
Run hyperopt: | |
$ freqtrade -c your/config.json --strategy-path user_data/hyperopts/ -s CombinedBinHAndClucSupertrendOpts6Mixin hyperopt --customhyperopt CombinedBinHAndClucSupertrendOpts6 --hyperopt-loss OnlyProfitHyperOptLoss --min-trades 3 --print-all --random-state 333 -e 1000 | |
$ freqtrade -c your/config.json --strategy-path user_data/hyperopts/ -s CombinedBinHAndClucSupertrendOpts6Mixin hyperopt --customhyperopt CombinedBinHAndClucSupertrendOpts6 --hyperopt-loss OnlyProfitHyperOptLoss --min-trades 3 --print-all --random-state 333 -e 1000 --space roi stoploss | |
The `--min-trades 3 --print-all --random-state 333 -e 1000 --space roi stoploss` part is optional here, just for illustration. | |
Run backtesting: | |
$ freqtrade -c your/config.json --strategy-path user_data/hyperopts/ -s CombinedBinHAndClucSupertrendOpts6Mixin backtesting | |
""" | |
from typing import Any, Callable, Dict, List | |
import numpy as np # noqa F401 | |
import talib.abstract as ta | |
from pandas import DataFrame, DatetimeIndex | |
from skopt.space import Integer, Real, Categorical, Dimension | |
from technical.util import resample_to_interval | |
import freqtrade.vendor.qtpylib.indicators as qtpylib | |
from freqtrade.optimize.hyperopt_interface import IHyperOpt | |
from freqtrade.strategy.interface import IStrategy | |
def bollinger_bands(stock_price, window_size, num_of_std): | |
rolling_mean = stock_price.rolling(window=window_size).mean() | |
rolling_std = stock_price.rolling(window=window_size).std() | |
lower_band = rolling_mean - (rolling_std * num_of_std) | |
return np.nan_to_num(rolling_mean), np.nan_to_num(lower_band) | |
def fill_column(df, column): | |
df = df.set_index(DatetimeIndex(df['date'])) | |
df[column] = df[column].fillna(method='ffill') | |
df['date'] = df.index | |
return df | |
def supertrend(dataframe, multiplier=3, period=10): | |
df = dataframe.copy() | |
df['TR'] = ta.TRANGE(df) | |
df['ATR'] = df['TR'].ewm(alpha=1 / period).mean() | |
# atr = 'ATR_' + str(period) | |
st = 'ST_' + str(period) + '_' + str(multiplier) | |
stx = 'STX_' + str(period) + '_' + str(multiplier) | |
# Compute basic upper and lower bands | |
df['basic_ub'] = (df['high'] + df['low']) / 2 + multiplier * df['ATR'] | |
df['basic_lb'] = (df['high'] + df['low']) / 2 - multiplier * df['ATR'] | |
# Compute final upper and lower bands | |
df['final_ub'] = 0.00 | |
df['final_lb'] = 0.00 | |
for i in range(period, len(df)): | |
df['final_ub'].iat[i] = df['basic_ub'].iat[i] if df['basic_ub'].iat[i] < df['final_ub'].iat[i - 1] or \ | |
df['close'].iat[i - 1] > df['final_ub'].iat[i - 1] else \ | |
df['final_ub'].iat[i - 1] | |
df['final_lb'].iat[i] = df['basic_lb'].iat[i] if df['basic_lb'].iat[i] > df['final_lb'].iat[i - 1] or \ | |
df['close'].iat[i - 1] < df['final_lb'].iat[i - 1] else \ | |
df['final_lb'].iat[i - 1] | |
# Set the Supertrend value | |
df[st] = 0.00 | |
for i in range(period, len(df)): | |
df[st].iat[i] = df['final_ub'].iat[i] if df[st].iat[i - 1] == df['final_ub'].iat[i - 1] and df['close'].iat[ | |
i] <= df['final_ub'].iat[i] else \ | |
df['final_lb'].iat[i] if df[st].iat[i - 1] == df['final_ub'].iat[i - 1] and df['close'].iat[i] > \ | |
df['final_ub'].iat[i] else \ | |
df['final_lb'].iat[i] if df[st].iat[i - 1] == df['final_lb'].iat[i - 1] and df['close'].iat[i] >= \ | |
df['final_lb'].iat[i] else \ | |
df['final_ub'].iat[i] if df[st].iat[i - 1] == df['final_lb'].iat[i - 1] and df['close'].iat[i] < \ | |
df['final_lb'].iat[i] else 0.00 | |
# Mark the trend direction up/down | |
df[stx] = np.where((df[st] > 0.00), np.where((df['close'] < df[st]), -100, 100), 0) | |
# Remove basic and final bands from the columns | |
df.drop(['basic_ub', 'basic_lb', 'final_ub', 'final_lb'], inplace=True, axis=1) | |
df.fillna(0, inplace=True) | |
return DataFrame(index=df.index, data={ | |
'date': df['date'], | |
'ST': df[st], | |
'STX': df[stx] | |
}) | |
def historical_data(pair: str, timeframe: str, dataframe: DataFrame = None) -> DataFrame: | |
return resample_to_interval(dataframe, timeframe) | |
def supertrend_on_tf(dataframe, pair, timeframe) -> DataFrame: | |
df = historical_data(pair=pair, timeframe=timeframe, dataframe=dataframe) | |
df = supertrend(df) | |
# add date index | |
dataframe = dataframe.set_index(DatetimeIndex(dataframe['date'])) | |
# resample ST | |
dataframe['ST'] = df['ST'] | |
dataframe = fill_column(dataframe, 'ST') | |
# resample STX | |
dataframe['STX'] = fill_column(df, 'STX')['STX'] | |
dataframe = fill_column(dataframe, 'STX') | |
# remove date index | |
dataframe = dataframe.reset_index(drop=True) | |
dataframe = dataframe.reindex() | |
return dataframe | |
################################################################# | |
_buy_params = { | |
'BBDELTA_MULT': 0.014079875915048848, | |
'BBDELTA_TAIL_MULT': 0.8452308728330871, | |
'BBLOWER_MULT': 0.058748785289872124, | |
'BOLLINGER_BAND_STDS_BINH': 4, | |
'BOLLINGER_BAND_STDS_CLUC': 9, | |
'BOLLINGER_BAND_WINDOW_BINH': 26, | |
'BOLLINGER_BAND_WINDOW_CLUC': 56, | |
'CLOSE_DELTA_MULT': 0.010464523522448359, | |
'SUPERTREND_TF': '5m', | |
'SUPERTREND_VALUE_BUY': -100, | |
'VOLUME_MULT': 32 | |
} | |
_sell_params = { | |
'BB_MIDDLEBAND_MULT': 1.4972771731556151, 'SUPERTREND_VALUE_SELL': 100, | |
'SUPERTREND_TF': '5m', | |
} | |
_minimal_roi = { | |
"0": 0.22769, "40": 0.03356, "100": 0.01301, "146": 0 | |
} | |
_stoploss = -0.15042 | |
################################################################# | |
class CombinedBinHAndClucSupertrendOpts6(IHyperOpt): | |
use_sell_signal = True | |
sell_profit_only = True | |
ignore_roi_if_buy_signal = False | |
minimal_roi = _minimal_roi | |
stoploss = _stoploss | |
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: | |
""" | |
Indicators, common for buy and sell parts. | |
""" | |
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 | |
""" | |
return _populate_buy_trend(dataframe, metadata, params) | |
return populate_buy_trend | |
@staticmethod | |
def indicator_space() -> List[Dimension]: | |
""" | |
Define your Hyperopt space for searching strategy parameters | |
""" | |
return [ | |
Categorical(['1m', '5m', '15m', '30m', '1h', '2h', '4h'], name='SUPERTREND_TF'), | |
Categorical([-100, 100], name='SUPERTREND_VALUE_BUY'), | |
Integer(1, 10, name='BOLLINGER_BAND_STDS_BINH'), | |
Integer(10, 60, name='BOLLINGER_BAND_WINDOW_BINH'), | |
Integer(1, 10, name='BOLLINGER_BAND_STDS_CLUC'), | |
Integer(5, 60, name='BOLLINGER_BAND_WINDOW_CLUC'), | |
Real(0.001, 0.02, name='CLOSE_DELTA_MULT'), | |
Real(0.001, 0.02, name='BBDELTA_MULT'), | |
Real(0.01, 1, name='BBDELTA_TAIL_MULT'), | |
Real(0.001, 0.1, name='BBLOWER_MULT'), | |
Integer(1, 50, name='VOLUME_MULT'), | |
] | |
@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 | |
""" | |
return _populate_sell_trend(dataframe, metadata, params) | |
return populate_sell_trend | |
@staticmethod | |
def sell_indicator_space() -> List[Dimension]: | |
""" | |
Define your Hyperopt space for searching sell strategy parameters | |
""" | |
return [ | |
Categorical([-100, 100], name='SUPERTREND_VALUE_SELL'), | |
Real(0.8, 1.8, name='BB_MIDDLEBAND_MULT'), | |
] | |
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: | |
""" | |
Based on TA indicators. Should be a copy of from strategy | |
must align to populate_indicators in this file | |
Only used when --spaces does not include buy | |
""" | |
return _populate_buy_trend(dataframe, metadata, _buy_params) | |
def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: | |
""" | |
Based on TA indicators. Should be a copy of from strategy | |
must align to populate_indicators in this file | |
Only used when --spaces does not include sell | |
""" | |
return _populate_sell_trend(dataframe, metadata, _sell_params) | |
def _populate_buy_trend(dataframe: DataFrame, metadata: dict, params = {}) -> DataFrame: | |
""" | |
Buy strategy Hyperopt will build and use | |
""" | |
# strategy BinHV45 | |
mid, lower = bollinger_bands( | |
dataframe['close'], | |
window_size=params['BOLLINGER_BAND_WINDOW_BINH'], | |
num_of_std=params['BOLLINGER_BAND_STDS_BINH'] | |
) | |
dataframe['lower'] = lower | |
dataframe['bbdelta'] = (mid - dataframe['lower']).abs() | |
dataframe['closedelta'] = (dataframe['close'] - dataframe['close'].shift()).abs() | |
dataframe['tail'] = (dataframe['close'] - dataframe['low']).abs() | |
# strategy ClucMay72018 | |
bollinger = qtpylib.bollinger_bands( | |
qtpylib.typical_price(dataframe), | |
window=params['BOLLINGER_BAND_WINDOW_CLUC'], | |
stds=params['BOLLINGER_BAND_STDS_CLUC'] | |
) | |
dataframe['bb_lowerband'] = bollinger['lower'] | |
dataframe['bb_middleband'] = bollinger['mid'] | |
dataframe['ema_slow'] = ta.EMA(dataframe, timeperiod=50) | |
dataframe['volume_mean_slow'] = dataframe['volume'].rolling(window=30).mean() | |
# supertrend | |
dataframe = supertrend_on_tf(dataframe, "", params['SUPERTREND_TF']) | |
dataframe.loc[ | |
( # trend filter | |
dataframe['STX'] == params['SUPERTREND_VALUE_BUY'] | |
) & | |
( # strategy BinHV45 | |
dataframe['lower'].shift().gt(0) & | |
dataframe['bbdelta'].gt( | |
dataframe['close'] * params['BBDELTA_MULT'] | |
) & | |
dataframe['closedelta'].gt( | |
dataframe['close'] * params['CLOSE_DELTA_MULT'] | |
) & | |
dataframe['tail'].lt( | |
dataframe['bbdelta'] * params['BBDELTA_TAIL_MULT'] | |
) & | |
dataframe['close'].lt(dataframe['lower'].shift()) & | |
dataframe['close'].le(dataframe['close'].shift()) | |
) | | |
( # strategy ClucMay72018 | |
(dataframe['close'] < dataframe['ema_slow']) & | |
( | |
dataframe['close'] < | |
( | |
params['BBLOWER_MULT'] * | |
dataframe['bb_lowerband'] | |
) | |
) & | |
( | |
dataframe['volume'] < | |
( | |
dataframe['volume_mean_slow'].shift(1) * | |
params['VOLUME_MULT'] | |
) | |
) | |
), | |
'buy' | |
] = 1 | |
return dataframe | |
def _populate_sell_trend(dataframe: DataFrame, metadata: dict, params = {}) -> DataFrame: | |
""" | |
Sell strategy Hyperopt will build and use | |
""" | |
bb_middleband_mult = params['BB_MIDDLEBAND_MULT'] | |
supertrend_value_sell = params['SUPERTREND_VALUE_SELL'] | |
# supertrend | |
dataframe = supertrend_on_tf(dataframe, "", params['SUPERTREND_TF']) | |
dataframe.loc[ | |
( | |
(dataframe['STX'] == supertrend_value_sell) | | |
(dataframe['close'] > (dataframe['bb_middleband'] * bb_middleband_mult)) | |
), | |
'sell' | |
] = 1 | |
return dataframe | |
""" | |
Best result: | |
76/100: 30 trades. Avg profit 0.50%. Total profit 149.84044521 USDT ( 14.97Σ%). Avg duration 993.7 mins. Objective: 0.95010 | |
Buy hyperspace params: | |
{ 'BBDELTA_MULT': 0.014079875915048848, | |
'BBDELTA_TAIL_MULT': 0.8452308728330871, | |
'BBLOWER_MULT': 0.058748785289872124, | |
'BOLLINGER_BAND_STDS_BINH': 4, | |
'BOLLINGER_BAND_STDS_CLUC': 9, | |
'BOLLINGER_BAND_WINDOW_BINH': 26, | |
'BOLLINGER_BAND_WINDOW_CLUC': 56, | |
'CLOSE_DELTA_MULT': 0.010464523522448359, | |
'SUPERTREND_TF': '5m', | |
'SUPERTREND_VALUE_BUY': -100, | |
'VOLUME_MULT': 32} | |
Sell hyperspace params: | |
{'BB_MIDDLEBAND_MULT': 1.4972771731556151, 'SUPERTREND_VALUE_SELL': 100} | |
ROI table: | |
{0: 0.22769, 40: 0.03356, 100: 0.01301, 146: 0} | |
Stoploss: -0.15042 | |
------------------------------------------------------ | |
Best result: | |
342/1000: 24 trades. Avg profit 1.61%. Total profit 386.90672532 USDT ( 38.65Σ%). Avg duration 755.8 mins. Objective: 0.87116 | |
Buy hyperspace params: | |
{ 'BBDELTA_MULT': 0.015715369078588278, | |
'BBDELTA_TAIL_MULT': 0.8374334258980787, | |
'BBLOWER_MULT': 0.02179397400723257, | |
'BOLLINGER_BAND_STDS_BINH': 1, | |
'BOLLINGER_BAND_STDS_CLUC': 5, | |
'BOLLINGER_BAND_WINDOW_BINH': 28, | |
'BOLLINGER_BAND_WINDOW_CLUC': 32, | |
'CLOSE_DELTA_MULT': 0.005235863351974562, | |
'SUPERTREND_TF': '5m', | |
'SUPERTREND_VALUE_BUY': -100, | |
'VOLUME_MULT': 45} | |
Sell hyperspace params: | |
{'BB_MIDDLEBAND_MULT': 1.2149599928001056, 'SUPERTREND_VALUE_SELL': 100} | |
ROI table: | |
{0: 0.13025, 19: 0.04938, 68: 0.02238, 171: 0} | |
Stoploss: -0.19789 | |
------------------------------------------------------------ | |
""" | |
class CombinedBinHAndClucSupertrendOpts6Mixin(CombinedBinHAndClucSupertrendOpts6, IStrategy): | |
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: | |
return super().populate_indicators(dataframe, metadata) | |
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: | |
return super().populate_buy_trend(dataframe, metadata) | |
def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: | |
return super().populate_sell_trend(dataframe, metadata) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment