Merge e6d3a440cd
into 0980e7e82d
This commit is contained in:
commit
1688fba2f3
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,7 +6,6 @@ config*.json
|
|||||||
.hyperopt
|
.hyperopt
|
||||||
logfile.txt
|
logfile.txt
|
||||||
hyperopt_trials.pickle
|
hyperopt_trials.pickle
|
||||||
user_data/
|
|
||||||
freqtrade-plot.html
|
freqtrade-plot.html
|
||||||
|
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
|
@ -5,6 +5,9 @@
|
|||||||
"fiat_display_currency": "USD",
|
"fiat_display_currency": "USD",
|
||||||
"ticker_interval" : "5m",
|
"ticker_interval" : "5m",
|
||||||
"dry_run": false,
|
"dry_run": false,
|
||||||
|
"trailing_stop": {
|
||||||
|
"positive" : 0.005
|
||||||
|
},
|
||||||
"unfilledtimeout": 600,
|
"unfilledtimeout": 600,
|
||||||
"bid_strategy": {
|
"bid_strategy": {
|
||||||
"ask_last_balance": 0.0
|
"ask_last_balance": 0.0
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
"fiat_display_currency": "USD",
|
"fiat_display_currency": "USD",
|
||||||
"dry_run": false,
|
"dry_run": false,
|
||||||
"ticker_interval": "5m",
|
"ticker_interval": "5m",
|
||||||
|
"trailing_stop": true,
|
||||||
"minimal_roi": {
|
"minimal_roi": {
|
||||||
"40": 0.0,
|
"40": 0.0,
|
||||||
"30": 0.01,
|
"30": 0.01,
|
||||||
|
@ -14,7 +14,6 @@ from freqtrade.exchange import get_ticker_history
|
|||||||
from freqtrade.persistence import Trade
|
from freqtrade.persistence import Trade
|
||||||
from freqtrade.strategy.resolver import StrategyResolver
|
from freqtrade.strategy.resolver import StrategyResolver
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@ -31,6 +30,7 @@ class Analyze(object):
|
|||||||
Analyze class contains everything the bot need to determine if the situation is good for
|
Analyze class contains everything the bot need to determine if the situation is good for
|
||||||
buying or selling.
|
buying or selling.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, config: dict) -> None:
|
def __init__(self, config: dict) -> None:
|
||||||
"""
|
"""
|
||||||
Init Analyze
|
Init Analyze
|
||||||
@ -195,10 +195,41 @@ class Analyze(object):
|
|||||||
:return True if bot should sell at current rate
|
:return True if bot should sell at current rate
|
||||||
"""
|
"""
|
||||||
current_profit = trade.calc_profit_percent(current_rate)
|
current_profit = trade.calc_profit_percent(current_rate)
|
||||||
if self.strategy.stoploss is not None and current_profit < self.strategy.stoploss:
|
|
||||||
|
if trade.stop_loss is None:
|
||||||
|
# initially adjust the stop loss to the base value
|
||||||
|
trade.adjust_stop_loss(trade.open_rate, self.strategy.stoploss)
|
||||||
|
|
||||||
|
# evaluate if the stoploss was hit
|
||||||
|
if self.strategy.stoploss is not None and trade.stop_loss >= current_rate:
|
||||||
|
|
||||||
|
if 'trailing_stop' in self.config and self.config['trailing_stop']:
|
||||||
|
logger.warning(
|
||||||
|
"HIT STOP: current price at {:.6f}, stop loss is {:.6f}, "
|
||||||
|
"initial stop loss was at {:.6f}, trade opened at {:.6f}".format(
|
||||||
|
current_rate, trade.stop_loss, trade.initial_stop_loss, trade.open_rate))
|
||||||
|
logger.debug("trailing stop saved us: {:.6f}"
|
||||||
|
.format(trade.stop_loss - trade.initial_stop_loss))
|
||||||
|
|
||||||
logger.debug('Stop loss hit.')
|
logger.debug('Stop loss hit.')
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
# update the stop loss afterwards, after all by definition it's supposed to be hanging
|
||||||
|
if 'trailing_stop' in self.config and self.config['trailing_stop']:
|
||||||
|
|
||||||
|
# check if we have a special stop loss for positive condition
|
||||||
|
# and if profit is positive
|
||||||
|
stop_loss_value = self.strategy.stoploss
|
||||||
|
if isinstance(self.config['trailing_stop'], dict) and \
|
||||||
|
'positive' in self.config['trailing_stop'] and \
|
||||||
|
current_profit > 0:
|
||||||
|
|
||||||
|
logger.debug("using positive stop loss mode: {} since we have profit {}".format(
|
||||||
|
self.config['trailing_stop']['positive'], current_profit))
|
||||||
|
stop_loss_value = self.config['trailing_stop']['positive']
|
||||||
|
|
||||||
|
trade.adjust_stop_loss(current_rate, stop_loss_value)
|
||||||
|
|
||||||
# Check if time matches and current rate is above threshold
|
# Check if time matches and current rate is above threshold
|
||||||
time_diff = (current_time.timestamp() - trade.open_date.timestamp()) / 60
|
time_diff = (current_time.timestamp() - trade.open_date.timestamp()) / 60
|
||||||
for duration, threshold in self.strategy.minimal_roi.items():
|
for duration, threshold in self.strategy.minimal_roi.items():
|
||||||
|
@ -148,6 +148,12 @@ class Trade(_DECL_BASE):
|
|||||||
open_date = Column(DateTime, nullable=False, default=datetime.utcnow)
|
open_date = Column(DateTime, nullable=False, default=datetime.utcnow)
|
||||||
close_date = Column(DateTime)
|
close_date = Column(DateTime)
|
||||||
open_order_id = Column(String)
|
open_order_id = Column(String)
|
||||||
|
# absolute value of the stop loss
|
||||||
|
stop_loss = Column(Float, nullable=True, default=0.0)
|
||||||
|
# absolute value of the initial stop loss
|
||||||
|
initial_stop_loss = Column(Float, nullable=True, default=0.0)
|
||||||
|
# absolute value of the highest reached price
|
||||||
|
max_rate = Column(Float, nullable=True, default=0.0)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return 'Trade(id={}, pair={}, amount={:.8f}, open_rate={:.8f}, open_since={})'.format(
|
return 'Trade(id={}, pair={}, amount={:.8f}, open_rate={:.8f}, open_since={})'.format(
|
||||||
@ -158,6 +164,50 @@ class Trade(_DECL_BASE):
|
|||||||
arrow.get(self.open_date).humanize() if self.is_open else 'closed'
|
arrow.get(self.open_date).humanize() if self.is_open else 'closed'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def adjust_stop_loss(self, current_price, stoploss):
|
||||||
|
"""
|
||||||
|
|
||||||
|
this adjusts the stop loss to it's most recently observed
|
||||||
|
setting
|
||||||
|
:param current_price:
|
||||||
|
:param stoploss:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
new_loss = Decimal(current_price * (1 - abs(stoploss)))
|
||||||
|
|
||||||
|
# keeping track of the highest observed rate for this trade
|
||||||
|
if self.max_rate is None:
|
||||||
|
self.max_rate = current_price
|
||||||
|
else:
|
||||||
|
if current_price > self.max_rate:
|
||||||
|
self.max_rate = current_price
|
||||||
|
|
||||||
|
# no stop loss assigned yet
|
||||||
|
if self.stop_loss is None or self.stop_loss == 0:
|
||||||
|
logger.debug("assigning new stop loss")
|
||||||
|
self.stop_loss = new_loss
|
||||||
|
self.initial_stop_loss = new_loss
|
||||||
|
|
||||||
|
# evaluate if the stop loss needs to be updated
|
||||||
|
else:
|
||||||
|
if new_loss > self.stop_loss: # stop losses only walk up, never down!
|
||||||
|
self.stop_loss = new_loss
|
||||||
|
logger.debug("adjusted stop loss")
|
||||||
|
else:
|
||||||
|
logger.debug("keeping current stop loss")
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
"{} - current price {:.8f}, bought at {:.8f} and calculated "
|
||||||
|
"stop loss is at: {:.8f} initial stop at {:.8f}. trailing stop loss saved us: {:.8f} "
|
||||||
|
"and max observed rate was {:.8f}".format(
|
||||||
|
self.pair, current_price, self.open_rate,
|
||||||
|
self.initial_stop_loss,
|
||||||
|
self.stop_loss, float(self.stop_loss) - float(self.initial_stop_loss),
|
||||||
|
self.max_rate
|
||||||
|
|
||||||
|
))
|
||||||
|
|
||||||
def update(self, order: Dict) -> None:
|
def update(self, order: Dict) -> None:
|
||||||
"""
|
"""
|
||||||
Updates this entity with amount and actual open/close rates.
|
Updates this entity with amount and actual open/close rates.
|
||||||
|
@ -98,10 +98,11 @@ class RPC(object):
|
|||||||
trade.id,
|
trade.id,
|
||||||
trade.pair,
|
trade.pair,
|
||||||
shorten_date(arrow.get(trade.open_date).humanize(only_distance=True)),
|
shorten_date(arrow.get(trade.open_date).humanize(only_distance=True)),
|
||||||
'{:.2f}%'.format(100 * trade.calc_profit_percent(current_rate))
|
'{:.2f}%'.format(100 * trade.calc_profit_percent(current_rate)),
|
||||||
|
'{:.6f}'.format(trade.amount * current_rate)
|
||||||
])
|
])
|
||||||
|
|
||||||
columns = ['ID', 'Pair', 'Since', 'Profit']
|
columns = ['ID', 'Pair', 'Since', 'Profit', 'Value']
|
||||||
df_statuses = DataFrame.from_records(trades_list, columns=columns)
|
df_statuses = DataFrame.from_records(trades_list, columns=columns)
|
||||||
df_statuses = df_statuses.set_index(columns[0])
|
df_statuses = df_statuses.set_index(columns[0])
|
||||||
# The style used throughout is to return a tuple
|
# The style used throughout is to return a tuple
|
||||||
|
@ -84,6 +84,7 @@ def load_data_test(what):
|
|||||||
|
|
||||||
def simple_backtest(config, contour, num_results, mocker) -> None:
|
def simple_backtest(config, contour, num_results, mocker) -> None:
|
||||||
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
|
||||||
|
|
||||||
backtesting = Backtesting(config)
|
backtesting = Backtesting(config)
|
||||||
|
|
||||||
data = load_data_test(contour)
|
data = load_data_test(contour)
|
||||||
@ -97,6 +98,7 @@ def simple_backtest(config, contour, num_results, mocker) -> None:
|
|||||||
'realistic': True
|
'realistic': True
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
# results :: <class 'pandas.core.frame.DataFrame'>
|
# results :: <class 'pandas.core.frame.DataFrame'>
|
||||||
assert len(results) == num_results
|
assert len(results) == num_results
|
||||||
|
|
||||||
|
@ -106,6 +106,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None:
|
|||||||
assert 'just now' in result['Since'].all()
|
assert 'just now' in result['Since'].all()
|
||||||
assert 'ETH/BTC' in result['Pair'].all()
|
assert 'ETH/BTC' in result['Pair'].all()
|
||||||
assert '-0.59%' in result['Profit'].all()
|
assert '-0.59%' in result['Profit'].all()
|
||||||
|
assert 'Value' in result
|
||||||
|
|
||||||
|
|
||||||
def test_rpc_daily_profit(default_conf, update, ticker, fee,
|
def test_rpc_daily_profit(default_conf, update, ticker, fee,
|
||||||
|
@ -29,6 +29,14 @@ def test_load_strategy(result):
|
|||||||
def test_load_strategy_custom_directory(result):
|
def test_load_strategy_custom_directory(result):
|
||||||
resolver = StrategyResolver()
|
resolver = StrategyResolver()
|
||||||
extra_dir = os.path.join('some', 'path')
|
extra_dir = os.path.join('some', 'path')
|
||||||
|
|
||||||
|
if os.name == 'nt':
|
||||||
|
with pytest.raises(
|
||||||
|
FileNotFoundError,
|
||||||
|
match="FileNotFoundError: [WinError 3] The system cannot find the "
|
||||||
|
"path specified: '{}'".format(extra_dir)):
|
||||||
|
resolver._load_strategy('TestStrategy', extra_dir)
|
||||||
|
else:
|
||||||
with pytest.raises(
|
with pytest.raises(
|
||||||
FileNotFoundError,
|
FileNotFoundError,
|
||||||
match=r".*No such file or directory: '{}'".format(extra_dir)):
|
match=r".*No such file or directory: '{}'".format(extra_dir)):
|
||||||
|
@ -444,6 +444,8 @@ def test_migrate_new(default_conf, fee):
|
|||||||
close_profit FLOAT,
|
close_profit FLOAT,
|
||||||
stake_amount FLOAT NOT NULL,
|
stake_amount FLOAT NOT NULL,
|
||||||
amount FLOAT,
|
amount FLOAT,
|
||||||
|
initial_stop_loss FLOAT,
|
||||||
|
max_rate FLOAT,
|
||||||
open_date DATETIME NOT NULL,
|
open_date DATETIME NOT NULL,
|
||||||
close_date DATETIME,
|
close_date DATETIME,
|
||||||
open_order_id VARCHAR,
|
open_order_id VARCHAR,
|
||||||
|
@ -159,6 +159,15 @@ def plot_analyzed_dataframe(args: Namespace) -> None:
|
|||||||
fillcolor="rgba(0,176,246,0.2)",
|
fillcolor="rgba(0,176,246,0.2)",
|
||||||
line={'color': "transparent"},
|
line={'color': "transparent"},
|
||||||
)
|
)
|
||||||
|
bb_middle = go.Scatter(
|
||||||
|
x=data.date,
|
||||||
|
y=data.bb_middleband,
|
||||||
|
name='BB middle',
|
||||||
|
fill="tonexty",
|
||||||
|
fillcolor="rgba(0,176,246,0.2)",
|
||||||
|
line={'color': "red"},
|
||||||
|
)
|
||||||
|
|
||||||
macd = go.Scattergl(x=data['date'], y=data['macd'], name='MACD')
|
macd = go.Scattergl(x=data['date'], y=data['macd'], name='MACD')
|
||||||
macdsignal = go.Scattergl(x=data['date'], y=data['macdsignal'], name='MACD signal')
|
macdsignal = go.Scattergl(x=data['date'], y=data['macdsignal'], name='MACD signal')
|
||||||
volume = go.Bar(x=data['date'], y=data['volume'], name='Volume')
|
volume = go.Bar(x=data['date'], y=data['volume'], name='Volume')
|
||||||
@ -173,7 +182,9 @@ def plot_analyzed_dataframe(args: Namespace) -> None:
|
|||||||
|
|
||||||
fig.append_trace(candles, 1, 1)
|
fig.append_trace(candles, 1, 1)
|
||||||
fig.append_trace(bb_lower, 1, 1)
|
fig.append_trace(bb_lower, 1, 1)
|
||||||
|
fig.append_trace(bb_middle, 1, 1)
|
||||||
fig.append_trace(bb_upper, 1, 1)
|
fig.append_trace(bb_upper, 1, 1)
|
||||||
|
|
||||||
fig.append_trace(buys, 1, 1)
|
fig.append_trace(buys, 1, 1)
|
||||||
fig.append_trace(sells, 1, 1)
|
fig.append_trace(sells, 1, 1)
|
||||||
fig.append_trace(volume, 2, 1)
|
fig.append_trace(volume, 2, 1)
|
||||||
|
94
user_data/strategies/Long.py
Normal file
94
user_data/strategies/Long.py
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
|
||||||
|
# --- Do not remove these libs ---
|
||||||
|
from freqtrade.strategy.interface import IStrategy
|
||||||
|
from typing import Dict, List
|
||||||
|
from hyperopt import hp
|
||||||
|
from functools import reduce
|
||||||
|
from pandas import DataFrame
|
||||||
|
# --------------------------------
|
||||||
|
|
||||||
|
import talib.abstract as ta
|
||||||
|
import freqtrade.vendor.qtpylib.indicators as qtpylib
|
||||||
|
import numpy # noqa
|
||||||
|
|
||||||
|
|
||||||
|
class Long(IStrategy):
|
||||||
|
"""
|
||||||
|
|
||||||
|
author@: Gert Wohlgemuth
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Minimal ROI designed for the strategy.
|
||||||
|
# This attribute will be overridden if the config file contains "minimal_roi"
|
||||||
|
minimal_roi = {
|
||||||
|
"60": 0.05,
|
||||||
|
"30": 0.06,
|
||||||
|
"20": 0.07,
|
||||||
|
"0": 0.08
|
||||||
|
}
|
||||||
|
|
||||||
|
# Optimal stoploss designed for the strategy
|
||||||
|
# This attribute will be overridden if the config file contains "stoploss"
|
||||||
|
stoploss = -0.15
|
||||||
|
|
||||||
|
# Optimal ticker interval for the strategy
|
||||||
|
ticker_interval = 60
|
||||||
|
|
||||||
|
def populate_indicators(self, dataframe: DataFrame) -> DataFrame:
|
||||||
|
|
||||||
|
macd = ta.MACD(dataframe)
|
||||||
|
dataframe['macd'] = macd['macd']
|
||||||
|
dataframe['macdsignal'] = macd['macdsignal']
|
||||||
|
dataframe['macdhist'] = macd['macdhist']
|
||||||
|
dataframe['cci'] = ta.CCI(dataframe)
|
||||||
|
dataframe['tema'] = ta.TEMA(dataframe, timeperiod=50)
|
||||||
|
|
||||||
|
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']
|
||||||
|
|
||||||
|
# RSI
|
||||||
|
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)
|
||||||
|
|
||||||
|
# SAR Parabol
|
||||||
|
dataframe['sar'] = ta.SAR(dataframe)
|
||||||
|
|
||||||
|
return dataframe
|
||||||
|
|
||||||
|
def populate_buy_trend(self, dataframe: DataFrame) -> DataFrame:
|
||||||
|
"""
|
||||||
|
Based on TA indicators, populates the buy signal for the given dataframe
|
||||||
|
:param dataframe: DataFrame
|
||||||
|
:return: DataFrame with buy column
|
||||||
|
"""
|
||||||
|
dataframe.loc[
|
||||||
|
(
|
||||||
|
(dataframe['macd'] > dataframe['macdsignal']) &
|
||||||
|
(dataframe['macd'] > 0) &
|
||||||
|
(dataframe['cci'] <= 0.0)
|
||||||
|
),
|
||||||
|
'buy'] = 1
|
||||||
|
|
||||||
|
return dataframe
|
||||||
|
|
||||||
|
def populate_sell_trend(self, dataframe: DataFrame) -> DataFrame:
|
||||||
|
"""
|
||||||
|
Based on TA indicators, populates the sell signal for the given dataframe
|
||||||
|
:param dataframe: DataFrame
|
||||||
|
:return: DataFrame with buy column
|
||||||
|
"""
|
||||||
|
dataframe.loc[
|
||||||
|
(
|
||||||
|
# (dataframe['tema'] < dataframe['close'])
|
||||||
|
|
||||||
|
(dataframe['sar'] > dataframe['close']) &
|
||||||
|
(dataframe['fisher_rsi'] > 0.3)
|
||||||
|
),
|
||||||
|
'sell'] = 1
|
||||||
|
return dataframe
|
75
user_data/strategies/Quickie.py
Normal file
75
user_data/strategies/Quickie.py
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
# --- Do not remove these libs ---
|
||||||
|
from freqtrade.strategy.interface import IStrategy
|
||||||
|
from typing import Dict, List
|
||||||
|
from hyperopt import hp
|
||||||
|
from functools import reduce
|
||||||
|
from pandas import DataFrame
|
||||||
|
# --------------------------------
|
||||||
|
|
||||||
|
import talib.abstract as ta
|
||||||
|
import freqtrade.vendor.qtpylib.indicators as qtpylib
|
||||||
|
|
||||||
|
|
||||||
|
class Quickie(IStrategy):
|
||||||
|
"""
|
||||||
|
|
||||||
|
author@: Gert Wohlgemuth
|
||||||
|
|
||||||
|
idea:
|
||||||
|
momentum based strategie. The main idea is that it closes trades very quickly, while avoiding excessive losses. Hence a rather moderate stop loss in this case
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Minimal ROI designed for the strategy.
|
||||||
|
# This attribute will be overridden if the config file contains "minimal_roi"
|
||||||
|
minimal_roi = {
|
||||||
|
"60": 0.005,
|
||||||
|
"10": 0.01,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Optimal stoploss designed for the strategy
|
||||||
|
# This attribute will be overridden if the config file contains "stoploss"
|
||||||
|
stoploss = -0.25
|
||||||
|
|
||||||
|
# Optimal ticker interval for the strategy
|
||||||
|
ticker_interval = 5
|
||||||
|
|
||||||
|
def populate_indicators(self, dataframe: DataFrame) -> DataFrame:
|
||||||
|
dataframe['tema'] = ta.TEMA(dataframe, timeperiod=9)
|
||||||
|
dataframe['adx'] = ta.ADX(dataframe)
|
||||||
|
|
||||||
|
dataframe['sma_200'] = ta.SMA(dataframe, timeperiod=200)
|
||||||
|
dataframe['sma_50'] = ta.SMA(dataframe, timeperiod=50)
|
||||||
|
|
||||||
|
|
||||||
|
# required for graphing
|
||||||
|
bollinger = qtpylib.bollinger_bands(dataframe['close'], window=20, stds=2)
|
||||||
|
dataframe['bb_lowerband'] = bollinger['lower']
|
||||||
|
dataframe['bb_middleband'] = bollinger['mid']
|
||||||
|
dataframe['bb_upperband'] = bollinger['upper']
|
||||||
|
|
||||||
|
return dataframe
|
||||||
|
|
||||||
|
def populate_buy_trend(self, dataframe: DataFrame) -> DataFrame:
|
||||||
|
dataframe.loc[
|
||||||
|
(
|
||||||
|
(
|
||||||
|
(dataframe['adx'] > 30) &
|
||||||
|
(dataframe['tema'] < dataframe['bb_middleband']) &
|
||||||
|
(dataframe['tema'] > dataframe['tema'].shift(1)) &
|
||||||
|
(dataframe['sma_200'] > dataframe['close'])
|
||||||
|
)
|
||||||
|
),
|
||||||
|
'buy'] = 1
|
||||||
|
return dataframe
|
||||||
|
|
||||||
|
def populate_sell_trend(self, dataframe: DataFrame) -> DataFrame:
|
||||||
|
dataframe.loc[
|
||||||
|
(
|
||||||
|
(
|
||||||
|
(dataframe['adx'] > 70) &
|
||||||
|
(dataframe['tema'] > dataframe['bb_middleband']) &
|
||||||
|
(dataframe['tema'] < dataframe['tema'].shift(1))
|
||||||
|
)
|
||||||
|
),
|
||||||
|
'sell'] = 1
|
||||||
|
return dataframe
|
76
user_data/strategies/Simple.py
Normal file
76
user_data/strategies/Simple.py
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
# --- Do not remove these libs ---
|
||||||
|
from freqtrade.strategy.interface import IStrategy
|
||||||
|
from typing import Dict, List
|
||||||
|
from hyperopt import hp
|
||||||
|
from functools import reduce
|
||||||
|
from pandas import DataFrame
|
||||||
|
# --------------------------------
|
||||||
|
|
||||||
|
import talib.abstract as ta
|
||||||
|
import freqtrade.vendor.qtpylib.indicators as qtpylib
|
||||||
|
|
||||||
|
|
||||||
|
class Simple(IStrategy):
|
||||||
|
"""
|
||||||
|
|
||||||
|
author@: Gert Wohlgemuth
|
||||||
|
|
||||||
|
idea:
|
||||||
|
this strategy is based on the book, 'The Simple Strategy' and can be found in detail here:
|
||||||
|
|
||||||
|
https://www.amazon.com/Simple-Strategy-Powerful-Trading-Futures-ebook/dp/B00E66QPCG/ref=sr_1_1?ie=UTF8&qid=1525202675&sr=8-1&keywords=the+simple+strategy
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Minimal ROI designed for the strategy.
|
||||||
|
# since this strategy is planned around 5 minutes, we assume any time we have a 5% profit we should call it a day
|
||||||
|
# This attribute will be overridden if the config file contains "minimal_roi"
|
||||||
|
minimal_roi = {
|
||||||
|
"0": 0.01
|
||||||
|
}
|
||||||
|
|
||||||
|
# Optimal stoploss designed for the strategy
|
||||||
|
# This attribute will be overridden if the config file contains "stoploss"
|
||||||
|
stoploss = -0.25
|
||||||
|
|
||||||
|
# Optimal ticker interval for the strategy
|
||||||
|
ticker_interval = 5
|
||||||
|
|
||||||
|
def populate_indicators(self, dataframe: DataFrame) -> DataFrame:
|
||||||
|
# MACD
|
||||||
|
macd = ta.MACD(dataframe)
|
||||||
|
dataframe['macd'] = macd['macd']
|
||||||
|
dataframe['macdsignal'] = macd['macdsignal']
|
||||||
|
dataframe['macdhist'] = macd['macdhist']
|
||||||
|
|
||||||
|
# RSI
|
||||||
|
dataframe['rsi'] = ta.RSI(dataframe, timeperiod=7)
|
||||||
|
|
||||||
|
# required for graphing
|
||||||
|
bollinger = qtpylib.bollinger_bands(dataframe['close'], window=12, stds=2)
|
||||||
|
dataframe['bb_lowerband'] = bollinger['lower']
|
||||||
|
dataframe['bb_upperband'] = bollinger['upper']
|
||||||
|
dataframe['bb_middleband'] = bollinger['mid']
|
||||||
|
|
||||||
|
return dataframe
|
||||||
|
|
||||||
|
def populate_buy_trend(self, dataframe: DataFrame) -> DataFrame:
|
||||||
|
dataframe.loc[
|
||||||
|
(
|
||||||
|
(
|
||||||
|
(dataframe['macd'] > 0) # over 0
|
||||||
|
& (dataframe['macd'] > dataframe['macdsignal']) # over signal
|
||||||
|
& (dataframe['bb_upperband'] > dataframe['bb_upperband'].shift(1)) # pointed up
|
||||||
|
& (dataframe['rsi'] > 70) # optional filter, need to investigate
|
||||||
|
)
|
||||||
|
),
|
||||||
|
'buy'] = 1
|
||||||
|
return dataframe
|
||||||
|
|
||||||
|
def populate_sell_trend(self, dataframe: DataFrame) -> DataFrame:
|
||||||
|
# different strategy used for sell points, due to be able to duplicate it to 100%
|
||||||
|
dataframe.loc[
|
||||||
|
(
|
||||||
|
(dataframe['rsi'] > 80)
|
||||||
|
),
|
||||||
|
'sell'] = 1
|
||||||
|
return dataframe
|
90
user_data/strategies/ZLC.py
Normal file
90
user_data/strategies/ZLC.py
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
# --- Do not remove these libs ---
|
||||||
|
from freqtrade.strategy.interface import IStrategy
|
||||||
|
from typing import Dict, List
|
||||||
|
from hyperopt import hp
|
||||||
|
from functools import reduce
|
||||||
|
from pandas import DataFrame
|
||||||
|
# --------------------------------
|
||||||
|
|
||||||
|
import talib.abstract as ta
|
||||||
|
import freqtrade.vendor.qtpylib.indicators as qtpylib
|
||||||
|
|
||||||
|
|
||||||
|
class ZLC(IStrategy):
|
||||||
|
"""
|
||||||
|
|
||||||
|
author@: Gert Wohlgemuth
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Minimal ROI designed for the strategy.
|
||||||
|
# This attribute will be overridden if the config file contains "minimal_roi"
|
||||||
|
minimal_roi = {
|
||||||
|
"60": 0.01,
|
||||||
|
"30": 0.03,
|
||||||
|
"20": 0.04,
|
||||||
|
"0": 0.01
|
||||||
|
}
|
||||||
|
|
||||||
|
# Optimal stoploss designed for the strategy
|
||||||
|
# This attribute will be overridden if the config file contains "stoploss"
|
||||||
|
stoploss = -0.3
|
||||||
|
|
||||||
|
# Optimal ticker interval for the strategy
|
||||||
|
ticker_interval = 5
|
||||||
|
|
||||||
|
def populate_indicators(self, dataframe: DataFrame) -> DataFrame:
|
||||||
|
dataframe['cci-slow'] = ta.CCI(dataframe, timeperiod=25)
|
||||||
|
dataframe['cci-fast'] = ta.CCI(dataframe, timeperiod=50)
|
||||||
|
dataframe['expo'] = ta.EMA(dataframe, timeperiod=35)
|
||||||
|
|
||||||
|
# required for graphing
|
||||||
|
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']
|
||||||
|
|
||||||
|
macd = ta.MACD(dataframe)
|
||||||
|
dataframe['macd'] = macd['macd']
|
||||||
|
dataframe['macdsignal'] = macd['macdsignal']
|
||||||
|
dataframe['macdhist'] = macd['macdhist']
|
||||||
|
|
||||||
|
return dataframe
|
||||||
|
|
||||||
|
def populate_buy_trend(self, dataframe: DataFrame) -> DataFrame:
|
||||||
|
"""
|
||||||
|
Based on TA indicators, populates the buy signal for the given dataframe
|
||||||
|
:param dataframe: DataFrame
|
||||||
|
:return: DataFrame with buy column
|
||||||
|
"""
|
||||||
|
dataframe.loc[
|
||||||
|
(
|
||||||
|
#don't buy on peak tops
|
||||||
|
(dataframe['close'] < dataframe['bb_middleband'])
|
||||||
|
# this is the main concept of evaluating buys
|
||||||
|
& (dataframe['cci-fast'] > 0)
|
||||||
|
& (dataframe['cci-slow'] > 0)
|
||||||
|
& (dataframe['close'] > dataframe['expo'])
|
||||||
|
|
||||||
|
)
|
||||||
|
,
|
||||||
|
'buy'] = 1
|
||||||
|
|
||||||
|
return dataframe
|
||||||
|
|
||||||
|
def populate_sell_trend(self, dataframe: DataFrame) -> DataFrame:
|
||||||
|
"""
|
||||||
|
Based on TA indicators, populates the sell signal for the given dataframe
|
||||||
|
:param dataframe: DataFrame
|
||||||
|
:return: DataFrame with buy column
|
||||||
|
"""
|
||||||
|
dataframe.loc[
|
||||||
|
(dataframe['close'] >= dataframe['bb_upperband']) |
|
||||||
|
(
|
||||||
|
(dataframe['cci-fast'] < 0)
|
||||||
|
& (dataframe['cci-slow'] < 0)
|
||||||
|
& (dataframe['close'] < dataframe['expo'])
|
||||||
|
|
||||||
|
)
|
||||||
|
,
|
||||||
|
'sell'] = 0
|
||||||
|
return dataframe
|
Loading…
Reference in New Issue
Block a user