Merge 5b953f03e7
into 0893431fde
This commit is contained in:
commit
4ca932338b
@ -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 [{
|
||||||
|
@ -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
73
hyperopt-config.json
Normal 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
49
hyperopt-mongodb-note.md
Normal 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
615
hyperopt-mongodb.py
Normal 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()
|
@ -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)]
|
||||||
|
|
||||||
|
@ -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'
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user