Merge branch 'develop' into support_multiple_ticker
This commit is contained in:
commit
c9e1fd3fc4
@ -74,6 +74,8 @@ def populate_indicators(dataframe: DataFrame) -> DataFrame:
|
|||||||
# Plus Directional Indicator / Movement
|
# Plus Directional Indicator / Movement
|
||||||
dataframe['plus_dm'] = ta.PLUS_DM(dataframe)
|
dataframe['plus_dm'] = ta.PLUS_DM(dataframe)
|
||||||
dataframe['plus_di'] = ta.PLUS_DI(dataframe)
|
dataframe['plus_di'] = ta.PLUS_DI(dataframe)
|
||||||
|
dataframe['minus_di'] = ta.MINUS_DI(dataframe)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# ROC
|
# ROC
|
||||||
dataframe['roc'] = ta.ROC(dataframe)
|
dataframe['roc'] = ta.ROC(dataframe)
|
||||||
@ -114,13 +116,14 @@ def populate_indicators(dataframe: DataFrame) -> DataFrame:
|
|||||||
dataframe['blower'] = ta.BBANDS(dataframe, nbdevup=2, nbdevdn=2)['lowerband']
|
dataframe['blower'] = ta.BBANDS(dataframe, nbdevup=2, nbdevdn=2)['lowerband']
|
||||||
"""
|
"""
|
||||||
# Bollinger bands
|
# Bollinger bands
|
||||||
|
"""
|
||||||
bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
|
bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
|
||||||
dataframe['bb_lowerband'] = bollinger['lower']
|
dataframe['bb_lowerband'] = bollinger['lower']
|
||||||
dataframe['bb_middleband'] = bollinger['mid']
|
dataframe['bb_middleband'] = bollinger['mid']
|
||||||
dataframe['bb_upperband'] = bollinger['upper']
|
dataframe['bb_upperband'] = bollinger['upper']
|
||||||
"""
|
|
||||||
|
|
||||||
# EMA - Exponential Moving Average
|
# EMA - Exponential Moving Average
|
||||||
|
dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3)
|
||||||
dataframe['ema5'] = ta.EMA(dataframe, timeperiod=5)
|
dataframe['ema5'] = ta.EMA(dataframe, timeperiod=5)
|
||||||
dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10)
|
dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10)
|
||||||
dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50)
|
dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50)
|
||||||
@ -210,14 +213,12 @@ def populate_indicators(dataframe: DataFrame) -> DataFrame:
|
|||||||
|
|
||||||
# Chart type
|
# Chart type
|
||||||
# ------------------------------------
|
# ------------------------------------
|
||||||
"""
|
|
||||||
# Heikinashi stategy
|
# Heikinashi stategy
|
||||||
heikinashi = qtpylib.heikinashi(dataframe)
|
heikinashi = qtpylib.heikinashi(dataframe)
|
||||||
dataframe['ha_open'] = heikinashi['open']
|
dataframe['ha_open'] = heikinashi['open']
|
||||||
dataframe['ha_close'] = heikinashi['close']
|
dataframe['ha_close'] = heikinashi['close']
|
||||||
dataframe['ha_high'] = heikinashi['high']
|
dataframe['ha_high'] = heikinashi['high']
|
||||||
dataframe['ha_low'] = heikinashi['low']
|
dataframe['ha_low'] = heikinashi['low']
|
||||||
"""
|
|
||||||
|
|
||||||
return dataframe
|
return dataframe
|
||||||
|
|
||||||
|
@ -30,18 +30,19 @@ logging.getLogger('hyperopt.tpe').setLevel(logging.WARNING)
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# set TARGET_TRADES to suit your number concurrent trades so its realistic to 20days of data
|
# set TARGET_TRADES to suit your number concurrent trades so its realistic to 20days of data
|
||||||
TARGET_TRADES = 1100
|
TARGET_TRADES = 600
|
||||||
TOTAL_TRIES = 0
|
TOTAL_TRIES = 0
|
||||||
_CURRENT_TRIES = 0
|
_CURRENT_TRIES = 0
|
||||||
CURRENT_BEST_LOSS = 100
|
CURRENT_BEST_LOSS = 100
|
||||||
|
|
||||||
# max average trade duration in minutes
|
# max average trade duration in minutes
|
||||||
# if eval ends with higher value, we consider it a failed eval
|
# if eval ends with higher value, we consider it a failed eval
|
||||||
MAX_ACCEPTED_TRADE_DURATION = 240
|
MAX_ACCEPTED_TRADE_DURATION = 300
|
||||||
|
|
||||||
# this is expexted avg profit * expected trade count
|
# this is expexted avg profit * expected trade count
|
||||||
# for example 3.5%, 1100 trades, EXPECTED_MAX_PROFIT = 3.85
|
# for example 3.5%, 1100 trades, EXPECTED_MAX_PROFIT = 3.85
|
||||||
EXPECTED_MAX_PROFIT = 3.85
|
# check that the reported Σ% values do not exceed this!
|
||||||
|
EXPECTED_MAX_PROFIT = 3.0
|
||||||
|
|
||||||
# Configuration and data used by hyperopt
|
# Configuration and data used by hyperopt
|
||||||
PROCESSED = None # optimize.preprocess(optimize.load_data())
|
PROCESSED = None # optimize.preprocess(optimize.load_data())
|
||||||
@ -57,6 +58,10 @@ main._CONF = OPTIMIZE_CONFIG
|
|||||||
|
|
||||||
|
|
||||||
SPACE = {
|
SPACE = {
|
||||||
|
'macd_below_zero': hp.choice('macd_below_zero', [
|
||||||
|
{'enabled': False},
|
||||||
|
{'enabled': True}
|
||||||
|
]),
|
||||||
'mfi': hp.choice('mfi', [
|
'mfi': hp.choice('mfi', [
|
||||||
{'enabled': False},
|
{'enabled': False},
|
||||||
{'enabled': True, 'value': hp.quniform('mfi-value', 5, 25, 1)}
|
{'enabled': True, 'value': hp.quniform('mfi-value', 5, 25, 1)}
|
||||||
@ -95,13 +100,15 @@ SPACE = {
|
|||||||
]),
|
]),
|
||||||
'trigger': hp.choice('trigger', [
|
'trigger': hp.choice('trigger', [
|
||||||
{'type': 'lower_bb'},
|
{'type': 'lower_bb'},
|
||||||
|
{'type': 'lower_bb_tema'},
|
||||||
{'type': 'faststoch10'},
|
{'type': 'faststoch10'},
|
||||||
{'type': 'ao_cross_zero'},
|
{'type': 'ao_cross_zero'},
|
||||||
{'type': 'ema5_cross_ema10'},
|
{'type': 'ema3_cross_ema10'},
|
||||||
{'type': 'macd_cross_signal'},
|
{'type': 'macd_cross_signal'},
|
||||||
{'type': 'sar_reversal'},
|
{'type': 'sar_reversal'},
|
||||||
{'type': 'stochf_cross'},
|
|
||||||
{'type': 'ht_sine'},
|
{'type': 'ht_sine'},
|
||||||
|
{'type': 'heiken_reversal_bull'},
|
||||||
|
{'type': 'di_cross'},
|
||||||
]),
|
]),
|
||||||
'stoploss': hp.uniform('stoploss', -0.5, -0.02),
|
'stoploss': hp.uniform('stoploss', -0.5, -0.02),
|
||||||
}
|
}
|
||||||
@ -133,10 +140,11 @@ def log_results(results):
|
|||||||
|
|
||||||
if results['loss'] < CURRENT_BEST_LOSS:
|
if results['loss'] < CURRENT_BEST_LOSS:
|
||||||
CURRENT_BEST_LOSS = results['loss']
|
CURRENT_BEST_LOSS = results['loss']
|
||||||
logger.info('{:5d}/{}: {}'.format(
|
logger.info('{:5d}/{}: {}. Loss {:.5f}'.format(
|
||||||
results['current_tries'],
|
results['current_tries'],
|
||||||
results['total_tries'],
|
results['total_tries'],
|
||||||
results['result']))
|
results['result'],
|
||||||
|
results['loss']))
|
||||||
else:
|
else:
|
||||||
print('.', end='')
|
print('.', end='')
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
@ -144,9 +152,9 @@ def log_results(results):
|
|||||||
|
|
||||||
def calculate_loss(total_profit: float, trade_count: int, trade_duration: float):
|
def calculate_loss(total_profit: float, trade_count: int, trade_duration: float):
|
||||||
""" objective function, returns smaller number for more optimal results """
|
""" objective function, returns smaller number for more optimal results """
|
||||||
trade_loss = 1 - 0.35 * exp(-(trade_count - TARGET_TRADES) ** 2 / 10 ** 5.2)
|
trade_loss = 1 - 0.25 * exp(-(trade_count - TARGET_TRADES) ** 2 / 10 ** 5.8)
|
||||||
profit_loss = max(0, 1 - total_profit / EXPECTED_MAX_PROFIT)
|
profit_loss = max(0, 1 - total_profit / EXPECTED_MAX_PROFIT)
|
||||||
duration_loss = min(trade_duration / MAX_ACCEPTED_TRADE_DURATION, 1)
|
duration_loss = 0.7 + 0.3 * min(trade_duration / MAX_ACCEPTED_TRADE_DURATION, 1)
|
||||||
return trade_loss + profit_loss + duration_loss
|
return trade_loss + profit_loss + duration_loss
|
||||||
|
|
||||||
|
|
||||||
@ -190,12 +198,13 @@ def optimizer(params):
|
|||||||
|
|
||||||
def format_results(results: DataFrame):
|
def format_results(results: DataFrame):
|
||||||
return ('{:6d} trades. Avg profit {: 5.2f}%. '
|
return ('{:6d} trades. Avg profit {: 5.2f}%. '
|
||||||
'Total profit {: 11.8f} BTC. Avg duration {:5.1f} mins.').format(
|
'Total profit {: 11.8f} BTC ({:.4f}Σ%). Avg duration {:5.1f} mins.').format(
|
||||||
len(results.index),
|
len(results.index),
|
||||||
results.profit_percent.mean() * 100.0,
|
results.profit_percent.mean() * 100.0,
|
||||||
results.profit_BTC.sum(),
|
results.profit_BTC.sum(),
|
||||||
|
results.profit_percent.sum(),
|
||||||
results.duration.mean() * 5,
|
results.duration.mean() * 5,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def buy_strategy_generator(params):
|
def buy_strategy_generator(params):
|
||||||
@ -204,6 +213,8 @@ def buy_strategy_generator(params):
|
|||||||
# GUARDS AND TRENDS
|
# GUARDS AND TRENDS
|
||||||
if params['uptrend_long_ema']['enabled']:
|
if params['uptrend_long_ema']['enabled']:
|
||||||
conditions.append(dataframe['ema50'] > dataframe['ema100'])
|
conditions.append(dataframe['ema50'] > dataframe['ema100'])
|
||||||
|
if params['macd_below_zero']['enabled']:
|
||||||
|
conditions.append(dataframe['macd'] < 0)
|
||||||
if params['uptrend_short_ema']['enabled']:
|
if params['uptrend_short_ema']['enabled']:
|
||||||
conditions.append(dataframe['ema5'] > dataframe['ema10'])
|
conditions.append(dataframe['ema5'] > dataframe['ema10'])
|
||||||
if params['mfi']['enabled']:
|
if params['mfi']['enabled']:
|
||||||
@ -224,14 +235,17 @@ def buy_strategy_generator(params):
|
|||||||
|
|
||||||
# TRIGGERS
|
# TRIGGERS
|
||||||
triggers = {
|
triggers = {
|
||||||
'lower_bb': dataframe['tema'] <= dataframe['blower'],
|
'lower_bb': (dataframe['close'] < dataframe['bb_lowerband']),
|
||||||
|
'lower_bb_tema': (dataframe['tema'] < dataframe['bb_lowerband']),
|
||||||
'faststoch10': (crossed_above(dataframe['fastd'], 10.0)),
|
'faststoch10': (crossed_above(dataframe['fastd'], 10.0)),
|
||||||
'ao_cross_zero': (crossed_above(dataframe['ao'], 0.0)),
|
'ao_cross_zero': (crossed_above(dataframe['ao'], 0.0)),
|
||||||
'ema5_cross_ema10': (crossed_above(dataframe['ema5'], dataframe['ema10'])),
|
'ema3_cross_ema10': (crossed_above(dataframe['ema3'], dataframe['ema10'])),
|
||||||
'macd_cross_signal': (crossed_above(dataframe['macd'], dataframe['macdsignal'])),
|
'macd_cross_signal': (crossed_above(dataframe['macd'], dataframe['macdsignal'])),
|
||||||
'sar_reversal': (crossed_above(dataframe['close'], dataframe['sar'])),
|
'sar_reversal': (crossed_above(dataframe['close'], dataframe['sar'])),
|
||||||
'stochf_cross': (crossed_above(dataframe['fastk'], dataframe['fastd'])),
|
|
||||||
'ht_sine': (crossed_above(dataframe['htleadsine'], dataframe['htsine'])),
|
'ht_sine': (crossed_above(dataframe['htleadsine'], dataframe['htsine'])),
|
||||||
|
'heiken_reversal_bull': (crossed_above(dataframe['ha_close'], dataframe['ha_open'])) &
|
||||||
|
(dataframe['ha_low'] == dataframe['ha_open']),
|
||||||
|
'di_cross': (crossed_above(dataframe['plus_di'], dataframe['minus_di'])),
|
||||||
}
|
}
|
||||||
conditions.append(triggers.get(params['trigger']['type']))
|
conditions.append(triggers.get(params['trigger']['type']))
|
||||||
|
|
||||||
|
@ -107,6 +107,7 @@ def test_no_log_if_loss_does_not_improve(mocker):
|
|||||||
|
|
||||||
def test_fmin_best_results(mocker, caplog):
|
def test_fmin_best_results(mocker, caplog):
|
||||||
fmin_result = {
|
fmin_result = {
|
||||||
|
"macd_below_zero": 0,
|
||||||
"adx": 1,
|
"adx": 1,
|
||||||
"adx-value": 15.0,
|
"adx-value": 15.0,
|
||||||
"fastd": 1,
|
"fastd": 1,
|
||||||
@ -136,7 +137,7 @@ def test_fmin_best_results(mocker, caplog):
|
|||||||
'"adx": {\n "enabled": true,\n "value": 15.0\n },',
|
'"adx": {\n "enabled": true,\n "value": 15.0\n },',
|
||||||
'"green_candle": {\n "enabled": true\n },',
|
'"green_candle": {\n "enabled": true\n },',
|
||||||
'"mfi": {\n "enabled": false\n },',
|
'"mfi": {\n "enabled": false\n },',
|
||||||
'"trigger": {\n "type": "ao_cross_zero"\n },',
|
'"trigger": {\n "type": "faststoch10"\n },',
|
||||||
'"stoploss": -0.1',
|
'"stoploss": -0.1',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user