Skip to content

Instantly share code, notes, and snippets.

@hroff-1902
Created September 25, 2019 17:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save hroff-1902/201e8090de74e6fec035c345913aadee to your computer and use it in GitHub Desktop.
Save hroff-1902/201e8090de74e6fec035c345913aadee to your computer and use it in GitHub Desktop.
Advanced Hyperopt technique
"""
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