This commit is contained in:
MoonGem 2018-03-24 02:07:29 +00:00 committed by GitHub
commit 4ca932338b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 1094 additions and 79 deletions

View File

@ -62,6 +62,9 @@ class Bittrex(Exchange):
def buy(self, pair: str, rate: float, amount: float) -> str: def buy(self, pair: str, rate: float, amount: float) -> str:
data = _API.buy_limit(pair.replace('_', '-'), amount, rate) data = _API.buy_limit(pair.replace('_', '-'), amount, rate)
if not data['success']: if not data['success']:
if 'APIKEY_INVALID' in str(data['message']):
print('Api Key...')
else:
Bittrex._validate_response(data) Bittrex._validate_response(data)
raise OperationalException('{message} params=({pair}, {rate}, {amount})'.format( raise OperationalException('{message} params=({pair}, {rate}, {amount})'.format(
message=data['message'], message=data['message'],
@ -73,6 +76,9 @@ class Bittrex(Exchange):
def sell(self, pair: str, rate: float, amount: float) -> str: def sell(self, pair: str, rate: float, amount: float) -> str:
data = _API.sell_limit(pair.replace('_', '-'), amount, rate) data = _API.sell_limit(pair.replace('_', '-'), amount, rate)
if not data['success']: if not data['success']:
if 'APIKEY_INVALID' in str(data['message']):
print('Api Key...')
else:
Bittrex._validate_response(data) Bittrex._validate_response(data)
raise OperationalException('{message} params=({pair}, {rate}, {amount})'.format( raise OperationalException('{message} params=({pair}, {rate}, {amount})'.format(
message=data['message'], message=data['message'],
@ -84,6 +90,9 @@ class Bittrex(Exchange):
def get_balance(self, currency: str) -> float: def get_balance(self, currency: str) -> float:
data = _API.get_balance(currency) data = _API.get_balance(currency)
if not data['success']: if not data['success']:
if 'APIKEY_INVALID' in str(data['message']):
print('Api Key...')
else:
Bittrex._validate_response(data) Bittrex._validate_response(data)
raise OperationalException('{message} params=({currency})'.format( raise OperationalException('{message} params=({currency})'.format(
message=data['message'], message=data['message'],
@ -93,6 +102,9 @@ class Bittrex(Exchange):
def get_balances(self): def get_balances(self):
data = _API.get_balances() data = _API.get_balances()
if not data['success']: if not data['success']:
if 'APIKEY_INVALID' in str(data['message']):
print('Api Key...')
else:
Bittrex._validate_response(data) Bittrex._validate_response(data)
raise OperationalException('{message}'.format(message=data['message'])) raise OperationalException('{message}'.format(message=data['message']))
return data['result'] return data['result']
@ -101,6 +113,9 @@ class Bittrex(Exchange):
if refresh or pair not in self.cached_ticker.keys(): if refresh or pair not in self.cached_ticker.keys():
data = _API.get_ticker(pair.replace('_', '-')) data = _API.get_ticker(pair.replace('_', '-'))
if not data['success']: if not data['success']:
if 'APIKEY_INVALID' in str(data['message']):
print('Api Key...')
else:
Bittrex._validate_response(data) Bittrex._validate_response(data)
raise OperationalException('{message} params=({pair})'.format( raise OperationalException('{message} params=({pair})'.format(
message=data['message'], message=data['message'],
@ -147,6 +162,9 @@ class Bittrex(Exchange):
'in response params=({})'.format(prop, pair)) 'in response params=({})'.format(prop, pair))
if not data['success']: if not data['success']:
if 'APIKEY_INVALID' in str(data['message']):
print('Api Key...')
else:
Bittrex._validate_response(data) Bittrex._validate_response(data)
raise OperationalException('{message} params=({pair})'.format( raise OperationalException('{message} params=({pair})'.format(
message=data['message'], message=data['message'],
@ -176,6 +194,9 @@ class Bittrex(Exchange):
def cancel_order(self, order_id: str) -> None: def cancel_order(self, order_id: str) -> None:
data = _API.cancel(order_id) data = _API.cancel(order_id)
if not data['success']: if not data['success']:
if 'APIKEY_INVALID' in str(data['message']):
print('Api Key...')
else:
Bittrex._validate_response(data) Bittrex._validate_response(data)
raise OperationalException('{message} params=({order_id})'.format( raise OperationalException('{message} params=({order_id})'.format(
message=data['message'], message=data['message'],
@ -187,6 +208,9 @@ class Bittrex(Exchange):
def get_markets(self) -> List[str]: def get_markets(self) -> List[str]:
data = _API.get_markets() data = _API.get_markets()
if not data['success']: if not data['success']:
if 'APIKEY_INVALID' in str(data['message']):
print('Api Key...')
else:
Bittrex._validate_response(data) Bittrex._validate_response(data)
raise OperationalException(data['message']) raise OperationalException(data['message'])
return [m['MarketName'].replace('-', '_') for m in data['result']] return [m['MarketName'].replace('-', '_') for m in data['result']]
@ -194,6 +218,9 @@ class Bittrex(Exchange):
def get_market_summaries(self) -> List[Dict]: def get_market_summaries(self) -> List[Dict]:
data = _API.get_market_summaries() data = _API.get_market_summaries()
if not data['success']: if not data['success']:
if 'APIKEY_INVALID' in str(data['message']):
print('Api Key...')
else:
Bittrex._validate_response(data) Bittrex._validate_response(data)
raise OperationalException(data['message']) raise OperationalException(data['message'])
return data['result'] return data['result']
@ -201,6 +228,9 @@ class Bittrex(Exchange):
def get_wallet_health(self) -> List[Dict]: def get_wallet_health(self) -> List[Dict]:
data = _API_V2.get_wallet_health() data = _API_V2.get_wallet_health()
if not data['success']: if not data['success']:
if 'APIKEY_INVALID' in str(data['message']):
print('Api Key...')
else:
Bittrex._validate_response(data) Bittrex._validate_response(data)
raise OperationalException(data['message']) raise OperationalException(data['message'])
return [{ return [{

View File

@ -113,7 +113,7 @@ class Backtesting(object):
open_date=buy_row.date, open_date=buy_row.date,
stake_amount=stake_amount, stake_amount=stake_amount,
amount=stake_amount / buy_row.open, amount=stake_amount / buy_row.open,
fee=exchange.get_fee() fee=0.0025
) )
# calculate win/lose forwards from buy point # calculate win/lose forwards from buy point

73
hyperopt-config.json Normal file
View File

@ -0,0 +1,73 @@
{
"max_open_trades": 15,
"stake_currency": "BTC",
"stake_amount": 0.00075,
"fiat_display_currency": "USD",
"dry_run": false,
"unfilledtimeout": 600,
"ticker_interval": 5,
"bid_strategy": {
"ask_last_balance": 0.0
},
"exchange": {
"name": "bittrex",
"key": "XXXXXXXXXXXX",
"secret": "XXXXXXXX",
"pair_whitelist": [
"BTC_ETH",
"BTC_XRP",
"BTC_BCC",
"BTC_LTC",
"BTC_ADA",
"BTC_XMR",
"BTC_DASH",
"BTC_TRX",
"BTC_ETC",
"BTC_ZEC",
"BTC_WAVES",
"BTC_STEEM",
"BTC_STRAT",
"BTC_DCR",
"BTC_REP",
"BTC_SNT",
"BTC_KMD",
"BTC_ARK",
"BTC_ARDR",
"BTC_MONA",
"BTC_DGB",
"BTC_PIVX",
"BTC_SYS",
"BTC_FCT",
"BTC_BAT",
"BTC_GNT",
"BTC_XZC",
"BTC_EMC",
"BTC_NXT",
"BTC_SALT",
"BTC_PAY",
"BTC_PART",
"BTC_GBYTE",
"BTC_BNT",
"BTC_POWR",
"BTC_NXS",
"BTC_SRN"
],
"pair_blacklist": [
"BTC_DOGE"
]
},
"experimental": {
"use_sell_signal": false,
"sell_profit_only": false
},
"telegram": {
"enabled": true,
"token": "XXXXXXXXXXXXXX",
"chat_id": "XXXXXXXXXXX"
},
"initial_state": "running",
"internals": {
"process_throttle_secs": 5
}
}

49
hyperopt-mongodb-note.md Normal file
View File

@ -0,0 +1,49 @@
Basically, to sum things up:
The locations of your hyperopt directories may vary. Some reside in .local.
Make these changes per this PR in these files:
https://github.com/hyperopt/hyperopt/pull/287/files
/usr/local/lib/python3.6/dist-packages/hyperopt/fmin.py
removetrial['state'] == base.JOB_STATE_RUNNING
addtrial['state'] = base.JOB_STATE_RUNNING
Make these changes per this PR into the hyperopt files:
https://github.com/hyperopt/hyperopt/pull/288/files
remove:import six.moves.cPickle as pickle
add: import dill as pickle in the following files:
/usr/local/lib/python3.6/dist-packages/hyperopt/fmin.py
/usr/local/lib/python3.6/dist-packages/hyperopt/main.py
/usr/local/lib/python3.6/dist-packages/hyperopt/mongoexp.py
/usr/local/lib/python3.6/dist-packages/hyperopt/utils.py
Install dill:
pip3.6 install dill
replace freqtrade/optimize/hyperopt.py with the hyperopt-mongodb.py in the freqtrade root.
Management tool for mongodb:
https://robomongo.org/download
Then, run the scipts. 5000 epochs can take a few hours even on a high-end machine. You can use the mongodb took above to check the progress on the local machine.
Lastly, read the test_strategy.py file located in user_data/strategy/test_strategy.py
Once you've done that, run scripts. You can restart the worker and the freqtrader hyperopt process but I do not advise it. Monitor the output with mongodb.

615
hyperopt-mongodb.py Normal file
View File

@ -0,0 +1,615 @@
# pragma pylint: disable=too-many-instance-attributes, pointless-string-statement
"""
This module contains the hyperopt logic
"""
import json
import logging
import os
import pickle
import signal
import sys
from argparse import Namespace
from functools import reduce
from math import exp
from operator import itemgetter
from typing import Dict, Any, Callable
import numpy
import talib.abstract as ta
from hyperopt import STATUS_FAIL, STATUS_OK, Trials, fmin, hp, space_eval, tpe
from hyperopt.mongoexp import MongoTrials
from pandas import DataFrame
import freqtrade.vendor.qtpylib.indicators as qtpylib
from freqtrade.arguments import Arguments
from freqtrade.configuration import Configuration
from freqtrade.logger import Logger
from freqtrade.optimize import load_data
from freqtrade.optimize.backtesting import Backtesting
from user_data.hyperopt_conf import hyperopt_optimize_conf
class Testz():
db_name = 'freqtrade_hyperopt'
MongoTrials(
arg='mongo://127.0.0.1:1234/{}/jobs'.format(db_name),
exp_key='exp1'
)
class Hyperopt(Backtesting):
"""
Hyperopt class, this class contains all the logic to run a hyperopt simulation
To run a backtest:
hyperopt = Hyperopt(config)
hyperopt.start()
"""
def __init__(self, config: Dict[str, Any]) -> None:
super().__init__(config)
# Rename the logging to display Hyperopt file instead of Backtesting
self.logging = Logger(name=__name__, level=config['loglevel'])
self.logger = self.logging.get_logger()
# set TARGET_TRADES to suit your number concurrent trades so its realistic
# to the number of days
self.target_trades = 600
self.total_tries = config.get('epochs', 0)
self.current_tries = 0
self.current_best_loss = 100
# max average trade duration in minutes
# if eval ends with higher value, we consider it a failed eval
self.max_accepted_trade_duration = 300
# this is expexted avg profit * expected trade count
# for example 3.5%, 1100 trades, self.expected_max_profit = 3.85
# check that the reported Σ% values do not exceed this!
self.expected_max_profit = 3.0
# Configuration and data used by hyperopt
self.processed = None
# Hyperopt Trials
self.trials_file = os.path.join('user_data', 'hyperopt_trials.pickle')
self.trials = Testz()
@staticmethod
def populate_indicators(dataframe: DataFrame) -> DataFrame:
"""
Adds several different TA indicators to the given DataFrame
"""
dataframe['adx'] = ta.ADX(dataframe)
dataframe['ao'] = qtpylib.awesome_oscillator(dataframe)
dataframe['cci'] = ta.CCI(dataframe)
macd = ta.MACD(dataframe)
dataframe['macd'] = macd['macd']
dataframe['macdsignal'] = macd['macdsignal']
dataframe['macdhist'] = macd['macdhist']
dataframe['mfi'] = ta.MFI(dataframe)
dataframe['minus_dm'] = ta.MINUS_DM(dataframe)
dataframe['minus_di'] = ta.MINUS_DI(dataframe)
dataframe['plus_dm'] = ta.PLUS_DM(dataframe)
dataframe['plus_di'] = ta.PLUS_DI(dataframe)
dataframe['roc'] = ta.ROC(dataframe)
dataframe['rsi'] = ta.RSI(dataframe)
# Inverse Fisher transform on RSI, values [-1.0, 1.0] (https://goo.gl/2JGGoy)
rsi = 0.1 * (dataframe['rsi'] - 50)
dataframe['fisher_rsi'] = (numpy.exp(2 * rsi) - 1) / (numpy.exp(2 * rsi) + 1)
# Inverse Fisher transform on RSI normalized, value [0.0, 100.0] (https://goo.gl/2JGGoy)
dataframe['fisher_rsi_norma'] = 50 * (dataframe['fisher_rsi'] + 1)
# Stoch
stoch = ta.STOCH(dataframe)
dataframe['slowd'] = stoch['slowd']
dataframe['slowk'] = stoch['slowk']
# Stoch fast
stoch_fast = ta.STOCHF(dataframe)
dataframe['fastd'] = stoch_fast['fastd']
dataframe['fastk'] = stoch_fast['fastk']
# Stoch RSI
stoch_rsi = ta.STOCHRSI(dataframe)
dataframe['fastd_rsi'] = stoch_rsi['fastd']
dataframe['fastk_rsi'] = stoch_rsi['fastk']
# Bollinger bands
bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
dataframe['bb_lowerband'] = bollinger['lower']
dataframe['bb_middleband'] = bollinger['mid']
dataframe['bb_upperband'] = bollinger['upper']
# EMA - Exponential Moving Average
dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3)
dataframe['ema5'] = ta.EMA(dataframe, timeperiod=5)
dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10)
dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50)
dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100)
# SAR Parabolic
dataframe['sar'] = ta.SAR(dataframe)
# SMA - Simple Moving Average
dataframe['sma'] = ta.SMA(dataframe, timeperiod=40)
# TEMA - Triple Exponential Moving Average
dataframe['tema'] = ta.TEMA(dataframe, timeperiod=9)
# Hilbert Transform Indicator - SineWave
hilbert = ta.HT_SINE(dataframe)
dataframe['htsine'] = hilbert['sine']
dataframe['htleadsine'] = hilbert['leadsine']
# Pattern Recognition - Bullish candlestick patterns
# ------------------------------------
"""
# Hammer: values [0, 100]
dataframe['CDLHAMMER'] = ta.CDLHAMMER(dataframe)
# Inverted Hammer: values [0, 100]
dataframe['CDLINVERTEDHAMMER'] = ta.CDLINVERTEDHAMMER(dataframe)
# Dragonfly Doji: values [0, 100]
dataframe['CDLDRAGONFLYDOJI'] = ta.CDLDRAGONFLYDOJI(dataframe)
# Piercing Line: values [0, 100]
dataframe['CDLPIERCING'] = ta.CDLPIERCING(dataframe) # values [0, 100]
# Morningstar: values [0, 100]
dataframe['CDLMORNINGSTAR'] = ta.CDLMORNINGSTAR(dataframe) # values [0, 100]
# Three White Soldiers: values [0, 100]
dataframe['CDL3WHITESOLDIERS'] = ta.CDL3WHITESOLDIERS(dataframe) # values [0, 100]
"""
# Pattern Recognition - Bearish candlestick patterns
# ------------------------------------
"""
# Hanging Man: values [0, 100]
dataframe['CDLHANGINGMAN'] = ta.CDLHANGINGMAN(dataframe)
# Shooting Star: values [0, 100]
dataframe['CDLSHOOTINGSTAR'] = ta.CDLSHOOTINGSTAR(dataframe)
# Gravestone Doji: values [0, 100]
dataframe['CDLGRAVESTONEDOJI'] = ta.CDLGRAVESTONEDOJI(dataframe)
# Dark Cloud Cover: values [0, 100]
dataframe['CDLDARKCLOUDCOVER'] = ta.CDLDARKCLOUDCOVER(dataframe)
# Evening Doji Star: values [0, 100]
dataframe['CDLEVENINGDOJISTAR'] = ta.CDLEVENINGDOJISTAR(dataframe)
# Evening Star: values [0, 100]
dataframe['CDLEVENINGSTAR'] = ta.CDLEVENINGSTAR(dataframe)
"""
# Pattern Recognition - Bullish/Bearish candlestick patterns
# ------------------------------------
"""
# Three Line Strike: values [0, -100, 100]
dataframe['CDL3LINESTRIKE'] = ta.CDL3LINESTRIKE(dataframe)
# Spinning Top: values [0, -100, 100]
dataframe['CDLSPINNINGTOP'] = ta.CDLSPINNINGTOP(dataframe) # values [0, -100, 100]
# Engulfing: values [0, -100, 100]
dataframe['CDLENGULFING'] = ta.CDLENGULFING(dataframe) # values [0, -100, 100]
# Harami: values [0, -100, 100]
dataframe['CDLHARAMI'] = ta.CDLHARAMI(dataframe) # values [0, -100, 100]
# Three Outside Up/Down: values [0, -100, 100]
dataframe['CDL3OUTSIDE'] = ta.CDL3OUTSIDE(dataframe) # values [0, -100, 100]
# Three Inside Up/Down: values [0, -100, 100]
dataframe['CDL3INSIDE'] = ta.CDL3INSIDE(dataframe) # values [0, -100, 100]
"""
# Chart type
# ------------------------------------
# Heikinashi stategy
heikinashi = qtpylib.heikinashi(dataframe)
dataframe['ha_open'] = heikinashi['open']
dataframe['ha_close'] = heikinashi['close']
dataframe['ha_high'] = heikinashi['high']
dataframe['ha_low'] = heikinashi['low']
return dataframe
def save_trials(self) -> None:
"""
Save hyperopt trials to file
"""
self.logger.info('Saving Trials to \'%s\'', self.trials_file)
pickle.dump(self.trials, open(self.trials_file, 'wb'))
def read_trials(self) -> Trials:
"""
Read hyperopt trials file
"""
self.logger.info('Reading Trials from \'%s\'', self.trials_file)
trials = pickle.load(open(self.trials_file, 'rb'))
os.remove(self.trials_file)
return trials
def log_trials_result(self) -> None:
"""
Display Best hyperopt result
"""
vals = json.dumps(self.trials.best_trial['misc']['vals'], indent=4)
results = self.trials.best_trial['result']['result']
self.logger.info('Best result:\n%s\nwith values:\n%s', results, vals)
def log_results(self, results) -> None:
"""
Log results if it is better than any previous evaluation
"""
if results['loss'] < self.current_best_loss:
self.current_best_loss = results['loss']
log_msg = '{:5d}/{}: {}. Loss {:.5f}'.format(
results['current_tries'],
results['total_tries'],
results['result'],
results['loss']
)
self.logger.info(log_msg)
else:
print('.', end='')
sys.stdout.flush()
def calculate_loss(self, total_profit: float, trade_count: int, trade_duration: float) -> float:
"""
Objective function, returns smaller number for more optimal results
"""
trade_loss = 1 - 0.25 * exp(-(trade_count - self.target_trades) ** 2 / 10 ** 5.8)
profit_loss = max(0, 1 - total_profit / self.expected_max_profit)
duration_loss = 0.4 * min(trade_duration / self.max_accepted_trade_duration, 1)
return trade_loss + profit_loss + duration_loss
@staticmethod
def generate_roi_table(params: Dict) -> Dict[int, float]:
"""
Generate the ROI table thqt will be used by Hyperopt
"""
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() -> Dict[str, Any]:
"""
Values to search for each ROI steps
"""
return {
'roi_t1': hp.quniform('roi_t1', 10, 120, 20),
'roi_t2': hp.quniform('roi_t2', 10, 60, 15),
'roi_t3': hp.quniform('roi_t3', 10, 40, 10),
'roi_p1': hp.quniform('roi_p1', 0.01, 0.04, 0.01),
'roi_p2': hp.quniform('roi_p2', 0.01, 0.07, 0.01),
'roi_p3': hp.quniform('roi_p3', 0.01, 0.20, 0.01),
}
@staticmethod
def stoploss_space() -> Dict[str, Any]:
"""
Stoploss Value to search
"""
return {
'stoploss': hp.quniform('stoploss', -0.5, -0.02, 0.02),
}
@staticmethod
def indicator_space() -> Dict[str, Any]:
"""
Define your Hyperopt space for searching strategy parameters
"""
return {
'macd_below_zero': hp.choice('macd_below_zero', [
{'enabled': False},
{'enabled': True}
]),
'mfi': hp.choice('mfi', [
{'enabled': False},
{'enabled': True, 'value': hp.quniform('mfi-value', 10, 25, 5)}
]),
'fastd': hp.choice('fastd', [
{'enabled': False},
{'enabled': True, 'value': hp.quniform('fastd-value', 15, 45, 5)}
]),
'adx': hp.choice('adx', [
{'enabled': False},
{'enabled': True, 'value': hp.quniform('adx-value', 20, 50, 5)}
]),
'rsi': hp.choice('rsi', [
{'enabled': False},
{'enabled': True, 'value': hp.quniform('rsi-value', 20, 40, 5)}
]),
'uptrend_long_ema': hp.choice('uptrend_long_ema', [
{'enabled': False},
{'enabled': True}
]),
'uptrend_short_ema': hp.choice('uptrend_short_ema', [
{'enabled': False},
{'enabled': True}
]),
'over_sar': hp.choice('over_sar', [
{'enabled': False},
{'enabled': True}
]),
'green_candle': hp.choice('green_candle', [
{'enabled': False},
{'enabled': True}
]),
'uptrend_sma': hp.choice('uptrend_sma', [
{'enabled': False},
{'enabled': True}
]),
'trigger': hp.choice('trigger', [
{'type': 'lower_bb'},
{'type': 'lower_bb_tema'},
{'type': 'faststoch10'},
{'type': 'ao_cross_zero'},
{'type': 'ema3_cross_ema10'},
{'type': 'macd_cross_signal'},
{'type': 'sar_reversal'},
{'type': 'ht_sine'},
{'type': 'heiken_reversal_bull'},
{'type': 'di_cross'},
]),
}
def has_space(self, space: str) -> bool:
"""
Tell if a space value is contained in the configuration
"""
if space in self.config['spaces'] or 'all' in self.config['spaces']:
return True
return False
def hyperopt_space(self) -> Dict[str, Any]:
"""
Return the space to use during Hyperopt
"""
spaces = {}
if self.has_space('buy'):
spaces = {**spaces, **Hyperopt.indicator_space()}
if self.has_space('roi'):
spaces = {**spaces, **Hyperopt.roi_space()}
if self.has_space('stoploss'):
spaces = {**spaces, **Hyperopt.stoploss_space()}
return spaces
@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) -> DataFrame:
"""
Buy strategy Hyperopt will build and use
"""
conditions = []
# GUARDS AND TRENDS
if 'uptrend_long_ema' in params and params['uptrend_long_ema']['enabled']:
conditions.append(dataframe['ema50'] > dataframe['ema100'])
if 'macd_below_zero' in params and params['macd_below_zero']['enabled']:
conditions.append(dataframe['macd'] < 0)
if 'uptrend_short_ema' in params and params['uptrend_short_ema']['enabled']:
conditions.append(dataframe['ema5'] > dataframe['ema10'])
if 'mfi' in params and params['mfi']['enabled']:
conditions.append(dataframe['mfi'] < params['mfi']['value'])
if 'fastd' in params and params['fastd']['enabled']:
conditions.append(dataframe['fastd'] < params['fastd']['value'])
if 'adx' in params and params['adx']['enabled']:
conditions.append(dataframe['adx'] > params['adx']['value'])
if 'rsi' in params and params['rsi']['enabled']:
conditions.append(dataframe['rsi'] < params['rsi']['value'])
if 'over_sar' in params and params['over_sar']['enabled']:
conditions.append(dataframe['close'] > dataframe['sar'])
if 'green_candle' in params and params['green_candle']['enabled']:
conditions.append(dataframe['close'] > dataframe['open'])
if 'uptrend_sma' in params and params['uptrend_sma']['enabled']:
prevsma = dataframe['sma'].shift(1)
conditions.append(dataframe['sma'] > prevsma)
# TRIGGERS
triggers = {
'lower_bb': (
dataframe['close'] < dataframe['bb_lowerband']
),
'lower_bb_tema': (
dataframe['tema'] < dataframe['bb_lowerband']
),
'faststoch10': (qtpylib.crossed_above(
dataframe['fastd'], 10.0
)),
'ao_cross_zero': (qtpylib.crossed_above(
dataframe['ao'], 0.0
)),
'ema3_cross_ema10': (qtpylib.crossed_above(
dataframe['ema3'], dataframe['ema10']
)),
'macd_cross_signal': (qtpylib.crossed_above(
dataframe['macd'], dataframe['macdsignal']
)),
'sar_reversal': (qtpylib.crossed_above(
dataframe['close'], dataframe['sar']
)),
'ht_sine': (qtpylib.crossed_above(
dataframe['htleadsine'], dataframe['htsine']
)),
'heiken_reversal_bull': (
(qtpylib.crossed_above(dataframe['ha_close'], dataframe['ha_open'])) &
(dataframe['ha_low'] == dataframe['ha_open'])
),
'di_cross': (qtpylib.crossed_above(
dataframe['plus_di'], dataframe['minus_di']
)),
}
conditions.append(triggers.get(params['trigger']['type']))
dataframe.loc[
reduce(lambda x, y: x & y, conditions),
'buy'] = 1
return dataframe
return populate_buy_trend
def generate_optimizer(self, params: Dict) -> Dict:
if self.has_space('roi'):
self.analyze.strategy.minimal_roi = self.generate_roi_table(params)
if self.has_space('buy'):
self.populate_buy_trend = self.buy_strategy_generator(params)
if self.has_space('stoploss'):
self.analyze.strategy.stoploss = params['stoploss']
results = self.backtest(
{
'stake_amount': self.config['stake_amount'],
'processed': self.processed,
'realistic': self.config.get('realistic_simulation', False),
}
)
result_explanation = self.format_results(results)
total_profit = results.profit_percent.sum()
trade_count = len(results.index)
trade_duration = results.duration.mean()
if trade_count == 0 or trade_duration > self.max_accepted_trade_duration:
print('.', end='')
return {
'status': STATUS_FAIL,
'loss': float('inf')
}
loss = self.calculate_loss(total_profit, trade_count, trade_duration)
self.current_tries += 1
self.log_results(
{
'loss': loss,
'current_tries': self.current_tries,
'total_tries': self.total_tries,
'result': result_explanation,
}
)
return {
'loss': loss,
'status': STATUS_OK,
'result': result_explanation,
}
@staticmethod
def format_results(results: DataFrame) -> str:
"""
Return the format result in a string
"""
return ('{:6d} trades. Avg profit {: 5.2f}%. '
'Total profit {: 11.8f} BTC ({:.4f}Σ%). Avg duration {:5.1f} mins.').format(
len(results.index),
results.profit_percent.mean() * 100.0,
results.profit_BTC.sum(),
results.profit_percent.sum(),
results.duration.mean(),
)
def start(self) -> None:
timerange = Arguments.parse_timerange(self.config.get('timerange'))
data = load_data(
datadir=self.config.get('datadir'),
pairs=self.config['exchange']['pair_whitelist'],
ticker_interval=self.ticker_interval,
timerange=timerange
)
if self.has_space('buy'):
self.analyze.populate_indicators = Hyperopt.populate_indicators
self.processed = self.tickerdata_to_dataframe(data)
if self.config.get('mongodb'):
self.logger.info('Using mongodb ...')
self.logger.info(
'Start scripts/start-mongodb.sh and start-hyperopt-worker.sh manually!'
)
else:
self.logger.info('Preparing Trials..')
signal.signal(signal.SIGINT, self.signal_handler)
# read trials file if we have one
if os.path.exists(self.trials_file) and os.path.getsize(self.trials_file) > 0:
self.trials = self.read_trials()
self.current_tries = len(self.trials.results)
self.total_tries += self.current_tries
self.logger.info(
'Continuing with trials. Current: %d, Total: %d',
self.current_tries,
self.total_tries
)
try:
best_parameters = fmin(
fn=self.generate_optimizer,
space=self.hyperopt_space(),
algo=tpe.suggest,
max_evals=self.total_tries,
trials=MongoTrials(arg='mongo://127.0.0.1:1234/freqtrade_hyperopt/jobs', exp_key='exp1')
)
# change the Logging format
self.logging.set_format('\n%(message)s')
results = sorted(self.trials.results, key=itemgetter('loss'))
best_result = results[0]['result']
# Improve best parameter logging display
except ValueError:
best_parameters = {}
best_result = 'Sorry, Hyperopt was not able to find good parameters. Please ' \
'try with more epochs (param: -e).'
if best_parameters:
best_parameters = space_eval(
self.hyperopt_space(),
best_parameters
)
self.logger.info('Best parameters:\n%s', json.dumps(best_parameters, indent=4))
if 'roi_t1' in best_parameters:
self.logger.info('ROI table:\n%s', self.generate_roi_table(best_parameters))
self.logger.info('Best Result:\n%s', best_result)
# Store trials result to file to resume next time
self.save_trials()
def signal_handler(self, sig, frame) -> None:
"""
Hyperopt SIGINT handler
"""
self.logger.info(
'Hyperopt received %s',
signal.Signals(sig).name
)
self.save_trials()
self.log_trials_result()
sys.exit(0)
def start(args: Namespace) -> None:
"""
Start Backtesting script
:param args: Cli args from Arguments()
:return: None
"""
# Remove noisy log messages
logging.getLogger('hyperopt.mongoexp').setLevel(logging.WARNING)
logging.getLogger('hyperopt.tpe').setLevel(logging.WARNING)
# Initialize logger
logger = Logger(name=__name__).get_logger()
logger.info('Starting freqtrade in Hyperopt mode')
# Initialize configuration
# Monkey patch the configuration with hyperopt_conf.py
configuration = Configuration(args)
optimize_config = hyperopt_optimize_conf()
config = configuration._load_common_config(optimize_config)
config = configuration._load_backtesting_config(config)
config = configuration._load_hyperopt_config(config)
config['exchange']['key'] = ''
config['exchange']['secret'] = ''
# Initialize backtesting object
hyperopt = Hyperopt(config)
hyperopt.start()

View File

@ -19,6 +19,7 @@ command = [
'--mongo=127.0.0.1:1234/{}'.format(DB_NAME), '--mongo=127.0.0.1:1234/{}'.format(DB_NAME),
'--poll-interval=0.1', '--poll-interval=0.1',
'--workdir={}'.format(WORK_DIR), '--workdir={}'.format(WORK_DIR),
'--reserve-timeout=900',
] ]
processes = [subprocess.Popen(command) for i in range(PROC_COUNT)] processes = [subprocess.Popen(command) for i in range(PROC_COUNT)]

View File

@ -11,31 +11,52 @@ def hyperopt_optimize_conf() -> dict:
:return: :return:
""" """
return { return {
'max_open_trades': 3, 'max_open_trades': 15,
'stake_currency': 'BTC', 'stake_currency': 'BTC',
'stake_amount': 0.01, 'stake_amount': 0.00075,
"minimal_roi": { 'ticker_interval': 5,
'40': 0.0,
'30': 0.01,
'20': 0.02,
'0': 0.04,
},
'stoploss': -0.10,
"bid_strategy": { "bid_strategy": {
"ask_last_balance": 0.0 "ask_last_balance": 0.0
}, },
"exchange": { "exchange": {
"pair_whitelist": [ "pair_whitelist": [
"BTC_ETH", 'BTC_ETH',
"BTC_LTC", 'BTC_XRP',
"BTC_ETC", 'BTC_BCC',
"BTC_DASH", 'BTC_LTC',
"BTC_ZEC", 'BTC_ADA',
"BTC_XLM", 'BTC_XMR',
"BTC_NXT", 'BTC_DASH',
"BTC_POWR", 'BTC_TRX',
"BTC_ADA", 'BTC_ETC',
"BTC_XMR" 'BTC_ZEC',
'BTC_WAVES',
'BTC_STEEM',
'BTC_STRAT',
'BTC_DCR',
'BTC_REP',
'BTC_SNT',
'BTC_KMD',
'BTC_ARK',
'BTC_ARDR',
'BTC_MONA',
'BTC_DGB',
'BTC_PIVX',
'BTC_SYS',
'BTC_FCT',
'BTC_BAT',
'BTC_GNT',
'BTC_XZC',
'BTC_EMC',
'BTC_NXT',
'BTC_SALT',
'BTC_PAY',
'BTC_PART',
'BTC_GBYTE',
'BTC_BNT',
'BTC_POWR',
'BTC_NXS',
'BTC_SRN'
] ]
} }
} }

View File

@ -1,6 +1,9 @@
# --- Do not remove these libs --- # --- Do not remove these libs ---
from freqtrade.strategy.interface import IStrategy from freqtrade.strategy.interface import IStrategy
from typing import Dict, List
from hyperopt import hp
from functools import reduce
from pandas import DataFrame from pandas import DataFrame
# -------------------------------- # --------------------------------
@ -10,12 +13,40 @@ import freqtrade.vendor.qtpylib.indicators as qtpylib
import numpy # noqa import numpy # noqa
# Update this variable if you change the class name # Make a backup of default_strategy then move this file over your default strategy in freqtrade/strategy and to run:
class_name = 'TestStrategy' # 'python3.6 freqtrade/main.py -c config.json hyperopt -s all --realistic-simulation -i 5'
# Make sure to take note of the if statements at the bottom of the file, and the pattern of > < and >= <= and the triggers configuration.
# Then, take note of the results: There is one trigger in the results, map the trigger as an example:
# 'Trigger: 7'
# The 7th trigger in this file's if statement such as:
# 'sar_reversal': (qtpylib.crossed_above(
# dataframe['close'], dataframe['sar']
#
# Drop the trigger into 'def populate_buy_trend(self, dataframe: DataFrame) -> DataFrame:' into your default_strategy backup as:
# dataframe['close'], dataframe['sar'] &
# Then for your other values such as: 'Mfi-Value: 15.00'
# The if statement looks like: conditions.append(dataframe['mfi'] < params['mfi']['value']
# Drop this in as:
# (dataframe['mfi'] < 15.00) &
# The last condition in 'def populate_buy_trend(self, dataframe: DataFrame) -> DataFrame:' will have no &
# This class is a sample. Feel free to customize it. ### As an example the sell/buy trend looks like this:
class TestStrategy(IStrategy): # dataframe.loc[
# (
# (dataframe['adx'] > 70) &
# (dataframe['tema'] < dataframe['tema'].shift(1))
# ),
# 'sell'] = 1
#
# Once done, move your backup strategy back over this one and backtest.
class_name = 'DefaultStrategy'
class DefaultStrategy(IStrategy):
""" """
This is a test strategy to inspire you. This is a test strategy to inspire you.
More information in https://github.com/gcarq/freqtrade/blob/develop/docs/bot-optimization.md More information in https://github.com/gcarq/freqtrade/blob/develop/docs/bot-optimization.md
@ -62,7 +93,7 @@ class TestStrategy(IStrategy):
# ADX # ADX
dataframe['adx'] = ta.ADX(dataframe) dataframe['adx'] = ta.ADX(dataframe)
"""
# Awesome oscillator # Awesome oscillator
dataframe['ao'] = qtpylib.awesome_oscillator(dataframe) dataframe['ao'] = qtpylib.awesome_oscillator(dataframe)
@ -114,18 +145,28 @@ class TestStrategy(IStrategy):
stoch_rsi = ta.STOCHRSI(dataframe) stoch_rsi = ta.STOCHRSI(dataframe)
dataframe['fastd_rsi'] = stoch_rsi['fastd'] dataframe['fastd_rsi'] = stoch_rsi['fastd']
dataframe['fastk_rsi'] = stoch_rsi['fastk'] dataframe['fastk_rsi'] = stoch_rsi['fastk']
"""
# Overlap Studies # Overlap Studies
# ------------------------------------ # ------------------------------------
# Previous Bollinger bands
# Because ta.BBANDS implementation is broken with small numbers, it actually
# returns middle band for all the three bands. Switch to qtpylib.bollinger_bands
# and use middle band instead.
# Is broken
"""
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['ema3'] = ta.EMA(dataframe, timeperiod=3)
dataframe['ema5'] = ta.EMA(dataframe, timeperiod=5) dataframe['ema5'] = ta.EMA(dataframe, timeperiod=5)
@ -138,7 +179,6 @@ class TestStrategy(IStrategy):
# SMA - Simple Moving Average # SMA - Simple Moving Average
dataframe['sma'] = ta.SMA(dataframe, timeperiod=40) dataframe['sma'] = ta.SMA(dataframe, timeperiod=40)
"""
# TEMA - Triple Exponential Moving Average # TEMA - Triple Exponential Moving Average
dataframe['tema'] = ta.TEMA(dataframe, timeperiod=9) dataframe['tema'] = ta.TEMA(dataframe, timeperiod=9)
@ -152,7 +192,7 @@ class TestStrategy(IStrategy):
# Pattern Recognition - Bullish candlestick patterns # Pattern Recognition - Bullish candlestick patterns
# ------------------------------------ # ------------------------------------
"""
# Hammer: values [0, 100] # Hammer: values [0, 100]
dataframe['CDLHAMMER'] = ta.CDLHAMMER(dataframe) dataframe['CDLHAMMER'] = ta.CDLHAMMER(dataframe)
# Inverted Hammer: values [0, 100] # Inverted Hammer: values [0, 100]
@ -165,11 +205,11 @@ class TestStrategy(IStrategy):
dataframe['CDLMORNINGSTAR'] = ta.CDLMORNINGSTAR(dataframe) # values [0, 100] dataframe['CDLMORNINGSTAR'] = ta.CDLMORNINGSTAR(dataframe) # values [0, 100]
# Three White Soldiers: values [0, 100] # Three White Soldiers: values [0, 100]
dataframe['CDL3WHITESOLDIERS'] = ta.CDL3WHITESOLDIERS(dataframe) # values [0, 100] dataframe['CDL3WHITESOLDIERS'] = ta.CDL3WHITESOLDIERS(dataframe) # values [0, 100]
"""
# Pattern Recognition - Bearish candlestick patterns # Pattern Recognition - Bearish candlestick patterns
# ------------------------------------ # ------------------------------------
"""
# Hanging Man: values [0, 100] # Hanging Man: values [0, 100]
dataframe['CDLHANGINGMAN'] = ta.CDLHANGINGMAN(dataframe) dataframe['CDLHANGINGMAN'] = ta.CDLHANGINGMAN(dataframe)
# Shooting Star: values [0, 100] # Shooting Star: values [0, 100]
@ -182,11 +222,10 @@ class TestStrategy(IStrategy):
dataframe['CDLEVENINGDOJISTAR'] = ta.CDLEVENINGDOJISTAR(dataframe) dataframe['CDLEVENINGDOJISTAR'] = ta.CDLEVENINGDOJISTAR(dataframe)
# Evening Star: values [0, 100] # Evening Star: values [0, 100]
dataframe['CDLEVENINGSTAR'] = ta.CDLEVENINGSTAR(dataframe) dataframe['CDLEVENINGSTAR'] = ta.CDLEVENINGSTAR(dataframe)
"""
# Pattern Recognition - Bullish/Bearish candlestick patterns # Pattern Recognition - Bullish/Bearish candlestick patterns
# ------------------------------------ # ------------------------------------
"""
# Three Line Strike: values [0, -100, 100] # Three Line Strike: values [0, -100, 100]
dataframe['CDL3LINESTRIKE'] = ta.CDL3LINESTRIKE(dataframe) dataframe['CDL3LINESTRIKE'] = ta.CDL3LINESTRIKE(dataframe)
# Spinning Top: values [0, -100, 100] # Spinning Top: values [0, -100, 100]
@ -199,18 +238,18 @@ class TestStrategy(IStrategy):
dataframe['CDL3OUTSIDE'] = ta.CDL3OUTSIDE(dataframe) # values [0, -100, 100] dataframe['CDL3OUTSIDE'] = ta.CDL3OUTSIDE(dataframe) # values [0, -100, 100]
# Three Inside Up/Down: values [0, -100, 100] # Three Inside Up/Down: values [0, -100, 100]
dataframe['CDL3INSIDE'] = ta.CDL3INSIDE(dataframe) # values [0, -100, 100] dataframe['CDL3INSIDE'] = ta.CDL3INSIDE(dataframe) # values [0, -100, 100]
"""
# 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
@ -220,12 +259,65 @@ class TestStrategy(IStrategy):
:param dataframe: DataFrame :param dataframe: DataFrame
:return: DataFrame with buy column :return: DataFrame with buy column
""" """
dataframe.loc[ if 'uptrend_long_ema' in params and params['uptrend_long_ema']['enabled']:
( conditions.append(dataframe['ema50'] > dataframe['ema100'])
(dataframe['adx'] > 30) & if 'macd_below_zero' in params and params['macd_below_zero']['enabled']:
(dataframe['tema'] <= dataframe['bb_middleband']) & conditions.append(dataframe['macd'] < 0)
(dataframe['tema'] > dataframe['tema'].shift(1)) if 'uptrend_short_ema' in params and params['uptrend_short_ema']['enabled']:
conditions.append(dataframe['ema5'] > dataframe['ema10'])
if 'mfi' in params and params['mfi']['enabled']:
conditions.append(dataframe['mfi'] < params['mfi']['value'])
if 'fastd' in params and params['fastd']['enabled']:
conditions.append(dataframe['fastd'] < params['fastd']['value'])
if 'adx' in params and params['adx']['enabled']:
conditions.append(dataframe['adx'] > params['adx']['value'])
if 'rsi' in params and params['rsi']['enabled']:
conditions.append(dataframe['rsi'] < params['rsi']['value'])
if 'over_sar' in params and params['over_sar']['enabled']:
conditions.append(dataframe['close'] > dataframe['sar'])
if 'green_candle' in params and params['green_candle']['enabled']:
conditions.append(dataframe['close'] > dataframe['open'])
if 'uptrend_sma' in params and params['uptrend_sma']['enabled']:
prevsma = dataframe['sma'].shift(1)
conditions.append(dataframe['sma'] > prevsma)
# TRIGGERS
triggers = {
'lower_bb': (
dataframe['close'] < dataframe['bb_lowerband']
), ),
'lower_bb_tema': (
dataframe['tema'] < dataframe['bb_lowerband']
),
'faststoch10': (qtpylib.crossed_above(
dataframe['fastd'], 10.0
)),
'ao_cross_zero': (qtpylib.crossed_above(
dataframe['ao'], 0.0
)),
'ema3_cross_ema10': (qtpylib.crossed_above(
dataframe['ema3'], dataframe['ema10']
)),
'macd_cross_signal': (qtpylib.crossed_above(
dataframe['macd'], dataframe['macdsignal']
)),
'sar_reversal': (qtpylib.crossed_above(
dataframe['close'], dataframe['sar']
)),
'ht_sine': (qtpylib.crossed_above(
dataframe['htleadsine'], dataframe['htsine']
)),
'heiken_reversal_bull': (
(qtpylib.crossed_above(dataframe['ha_close'], dataframe['ha_open'])) &
(dataframe['ha_low'] == dataframe['ha_open'])
),
'di_cross': (qtpylib.crossed_above(
dataframe['plus_di'], dataframe['minus_di']
)),
}
conditions.append(triggers.get(params['trigger']['type']))
dataframe.loc[
reduce(lambda x, y: x & y, conditions),
'buy'] = 1 'buy'] = 1
return dataframe return dataframe
@ -239,8 +331,142 @@ class TestStrategy(IStrategy):
dataframe.loc[ dataframe.loc[
( (
(dataframe['adx'] > 70) & (dataframe['adx'] > 70) &
(dataframe['tema'] > dataframe['bb_middleband']) &
(dataframe['tema'] < dataframe['tema'].shift(1)) (dataframe['tema'] < dataframe['tema'].shift(1))
), ),
'sell'] = 1 'sell'] = 1
return dataframe return dataframe
def hyperopt_space(self) -> List[Dict]:
"""
Define your Hyperopt space for the strategy
:return: Dict
"""
space = {
'macd_below_zero': hp.choice('macd_below_zero', [
{'enabled': False},
{'enabled': True}
]),
'mfi': hp.choice('mfi', [
{'enabled': False},
{'enabled': True, 'value': hp.quniform('mfi-value', 5, 25, 1)}
]),
'fastd': hp.choice('fastd', [
{'enabled': False},
{'enabled': True, 'value': hp.quniform('fastd-value', 10, 50, 1)}
]),
'adx': hp.choice('adx', [
{'enabled': False},
{'enabled': True, 'value': hp.quniform('adx-value', 15, 50, 1)}
]),
'rsi': hp.choice('rsi', [
{'enabled': False},
{'enabled': True, 'value': hp.quniform('rsi-value', 20, 40, 1)}
]),
'uptrend_long_ema': hp.choice('uptrend_long_ema', [
{'enabled': False},
{'enabled': True}
]),
'uptrend_short_ema': hp.choice('uptrend_short_ema', [
{'enabled': False},
{'enabled': True}
]),
'over_sar': hp.choice('over_sar', [
{'enabled': False},
{'enabled': True}
]),
'green_candle': hp.choice('green_candle', [
{'enabled': False},
{'enabled': True}
]),
'uptrend_sma': hp.choice('uptrend_sma', [
{'enabled': False},
{'enabled': True}
]),
'trigger': hp.choice('trigger', [
{'type': 'lower_bb'},
{'type': 'lower_bb_tema'},
{'type': 'faststoch10'},
{'type': 'ao_cross_zero'},
{'type': 'ema3_cross_ema10'},
{'type': 'macd_cross_signal'},
{'type': 'sar_reversal'},
{'type': 'ht_sine'},
{'type': 'heiken_reversal_bull'},
{'type': 'di_cross'},
]),
'stoploss': hp.uniform('stoploss', -0.5, -0.02),
}
return space
def buy_strategy_generator(self, params) -> None:
"""
Define the buy strategy parameters to be used by hyperopt
"""
def populate_buy_trend(dataframe: DataFrame) -> DataFrame:
conditions = []
# GUARDS AND TRENDS
if 'uptrend_long_ema' in params and params['uptrend_long_ema']['enabled']:
conditions.append(dataframe['ema50'] > dataframe['ema100'])
if 'macd_below_zero' in params and params['macd_below_zero']['enabled']:
conditions.append(dataframe['macd'] < 0)
if 'uptrend_short_ema' in params and params['uptrend_short_ema']['enabled']:
conditions.append(dataframe['ema5'] > dataframe['ema10'])
if 'mfi' in params and params['mfi']['enabled']:
conditions.append(dataframe['mfi'] < params['mfi']['value'])
if 'fastd' in params and params['fastd']['enabled']:
conditions.append(dataframe['fastd'] < params['fastd']['value'])
if 'adx' in params and params['adx']['enabled']:
conditions.append(dataframe['adx'] > params['adx']['value'])
if 'rsi' in params and params['rsi']['enabled']:
conditions.append(dataframe['rsi'] < params['rsi']['value'])
if 'over_sar' in params and params['over_sar']['enabled']:
conditions.append(dataframe['close'] > dataframe['sar'])
if 'green_candle' in params and params['green_candle']['enabled']:
conditions.append(dataframe['close'] > dataframe['open'])
if 'uptrend_sma' in params and params['uptrend_sma']['enabled']:
prevsma = dataframe['sma'].shift(1)
conditions.append(dataframe['sma'] > prevsma)
# TRIGGERS
triggers = {
'lower_bb': (
dataframe['close'] < dataframe['bb_lowerband']
),
'lower_bb_tema': (
dataframe['tema'] < dataframe['bb_lowerband']
),
'faststoch10': (qtpylib.crossed_above(
dataframe['fastd'], 10.0
)),
'ao_cross_zero': (qtpylib.crossed_above(
dataframe['ao'], 0.0
)),
'ema3_cross_ema10': (qtpylib.crossed_above(
dataframe['ema3'], dataframe['ema10']
)),
'macd_cross_signal': (qtpylib.crossed_above(
dataframe['macd'], dataframe['macdsignal']
)),
'sar_reversal': (qtpylib.crossed_above(
dataframe['close'], dataframe['sar']
)),
'ht_sine': (qtpylib.crossed_above(
dataframe['htleadsine'], dataframe['htsine']
)),
'heiken_reversal_bull': (
(qtpylib.crossed_above(dataframe['ha_close'], dataframe['ha_open'])) &
(dataframe['ha_low'] == dataframe['ha_open'])
),
'di_cross': (qtpylib.crossed_above(
dataframe['plus_di'], dataframe['minus_di']
)),
}
conditions.append(triggers.get(params['trigger']['type']))
dataframe.loc[
reduce(lambda x, y: x & y, conditions),
'buy'] = 1
return dataframe
return populate_buy_trend