author@: Gert Wohlgemuth
Timeframe
N/A
Direction
Long Only
Stoploss
-5.0%
Trailing Stop
No
ROI
0m: 10.0%
Interface Version
N/A
Startup Candles
N/A
Indicators
9
freqtrade/freqtrade-strategies
Strategy 003 author@: Gerald Lonlas github@: https://github.com/freqtrade/freqtrade-strategies
# --- 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, DatetimeIndex, merge
# --------------------------------
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
import numpy # noqa
# DO NOT USE, just playing with smooting and graphs!
class SmoothOperator(IStrategy):
"""
author@: Gert Wohlgemuth
idea:
The concept is about combining several common indicators, with a heavily smoothing, while trying to detect
a none completed peak shape.
"""
# Minimal ROI designed for the strategy.
# we only sell after 100%, unless our sell points are found before
minimal_roi = {
"0": 0.10
}
# Optimal stoploss designed for the strategy
# This attribute will be overridden if the config file contains "stoploss"
# should be converted to a trailing stop loss
stoploss = -0.05
# Optimal ticker interval for the strategy
ticker_interval = '5m'
# resample factor to establish our general trend. Basically don't buy if a trend is not given
resample_factor = 12
def populate_indicators(self, dataframe: DataFrame) -> DataFrame:
# resampled dataframe to establish if we are in an uptrend, downtrend or sideways trend
dataframe = StrategyHelper.resample(dataframe, self.ticker_interval, self.resample_factor)
##################################################################################
# required for entry and exit
# CCI
dataframe['cci'] = ta.CCI(dataframe, timeperiod=20)
dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
dataframe['adx'] = ta.ADX(dataframe)
dataframe['mfi'] = ta.MFI(dataframe)
dataframe['mfi_smooth'] = ta.EMA(dataframe, timeperiod=11, price='mfi')
dataframe['cci_smooth'] = ta.EMA(dataframe, timeperiod=11, price='cci')
dataframe['rsi_smooth'] = ta.EMA(dataframe, timeperiod=11, price='rsi')
##################################################################################
# required for graphing
bollinger = qtpylib.bollinger_bands(dataframe['close'], window=20, stds=2)
dataframe['bb_lowerband'] = bollinger['lower']
dataframe['bb_upperband'] = bollinger['upper']
dataframe['bb_middleband'] = bollinger['mid']
# MACD
macd = ta.MACD(dataframe)
dataframe['macd'] = macd['macd']
dataframe['macdsignal'] = macd['macdsignal']
dataframe['macdhist'] = macd['macdhist']
##################################################################################
# required for entry
bollinger = qtpylib.bollinger_bands(dataframe['close'], window=20, stds=1.6)
dataframe['entry_bb_lowerband'] = bollinger['lower']
dataframe['entry_bb_upperband'] = bollinger['upper']
dataframe['entry_bb_middleband'] = bollinger['mid']
dataframe['bpercent'] = (dataframe['close'] - dataframe['bb_lowerband']) / (
dataframe['bb_upperband'] - dataframe['bb_lowerband']) * 100
dataframe['bsharp'] = (dataframe['bb_upperband'] - dataframe['bb_lowerband']) / (
dataframe['bb_middleband'])
# these seem to be kind useful to measure when bands widen
# but than they are directly based on the moving average
dataframe['bsharp_slow'] = ta.SMA(dataframe, price='bsharp', timeperiod=11)
dataframe['bsharp_medium'] = ta.SMA(dataframe, price='bsharp', timeperiod=8)
dataframe['bsharp_fast'] = ta.SMA(dataframe, price='bsharp', timeperiod=5)
##################################################################################
# rsi and mfi are slightly weighted
dataframe['mfi_rsi_cci_smooth'] = (dataframe['rsi_smooth'] * 1.125 + dataframe['mfi_smooth'] * 1.125 +
dataframe[
'cci_smooth']) / 3
dataframe['mfi_rsi_cci_smooth'] = ta.TEMA(dataframe, timeperiod=21, price='mfi_rsi_cci_smooth')
# playgound
dataframe['candle_size'] = (dataframe['close'] - dataframe['open']) * (
dataframe['close'] - dataframe['open']) / 2
# helps with pattern recognition
dataframe['average'] = (dataframe['close'] + dataframe['open'] + dataframe['high'] + dataframe['low']) / 4
dataframe['sma_slow'] = ta.SMA(dataframe, timeperiod=200, price='close')
dataframe['sma_medium'] = ta.SMA(dataframe, timeperiod=100, price='close')
dataframe['sma_fast'] = ta.SMA(dataframe, timeperiod=50, price='close')
return dataframe
def populate_buy_trend(self, dataframe: DataFrame) -> DataFrame:
dataframe.loc[
(
# protection against pump and dump
# (dataframe['volume'] < (dataframe['volume'].rolling(window=30).mean().shift(1) * 20))
#
# & (dataframe['macd'] < dataframe['macdsignal'])
# & (dataframe['macd'] > 0)
# # spike below entry band for 3 consecutive ticks
# & (dataframe['low'] < dataframe['entry_bb_lowerband'])
# & (dataframe['low'].shift(1) < dataframe['bb_lowerband'].shift(1))
# & (dataframe['low'].shift(2) < dataframe['bb_lowerband'].shift(2))
# # pattern recognition
# & (
# (dataframe['close'] > dataframe['open'])
# | (dataframe['CDLHAMMER'] == 100)
# | (dataframe['CDLINVERTEDHAMMER'] == 100)
# | (dataframe['CDLDRAGONFLYDOJI'] == 100)
# )
# bottom curve detection
# & (dataframe['mfi_rsi_cci_smooth'] < 0)
#
# |
(
# simple v bottom shape (lopsided to the left to increase reactivity)
# which has to be below a very slow average
# this pattern only catches a few, but normally very good buy points
(
(dataframe['average'].shift(5) > dataframe['average'].shift(4))
& (dataframe['average'].shift(4) > dataframe['average'].shift(3))
& (dataframe['average'].shift(3) > dataframe['average'].shift(2))
& (dataframe['average'].shift(2) > dataframe['average'].shift(1))
& (dataframe['average'].shift(1) < dataframe['average'].shift(0))
& (dataframe['low'].shift(1) < dataframe['bb_middleband'])
& (dataframe['cci'].shift(1) < -100)
& (dataframe['rsi'].shift(1) < 30)
)
|
# buy in very oversold conditions
(
(dataframe['low'] < dataframe['bb_middleband'])
& (dataframe['cci'] < -200)
& (dataframe['rsi'] < 30)
& (dataframe['mfi'] < 30)
)
|
# etc tends to trade like this
# over very long periods of slowly building up coins
# does not happen often, but once in a while
(
(dataframe['mfi'] < 10)
& (dataframe['cci'] < -150)
& (dataframe['rsi'] < dataframe['mfi'])
)
)
&
# ensure we have an overall uptrend
(dataframe['close'] > dataframe)
),
'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[
(
(
# This generates very nice sale points, and mostly sit's one stop behind
# the top of the peak
(
(dataframe['mfi_rsi_cci_smooth'] > 100)
& (dataframe['mfi_rsi_cci_smooth'].shift(1) > dataframe['mfi_rsi_cci_smooth'])
& (dataframe['mfi_rsi_cci_smooth'].shift(2) < dataframe['mfi_rsi_cci_smooth'].shift(1))
& (dataframe['mfi_rsi_cci_smooth'].shift(3) < dataframe['mfi_rsi_cci_smooth'].shift(2))
)
|
# This helps with very long, sideways trends, to get out of a market before
# it dumps
(
StrategyHelper.eight_green_candles(dataframe)
)
|
# in case of very overbought market, like some one pumping
# sell
(
(dataframe['cci'] > 200)
& (dataframe['rsi'] > 70)
)
)
),
'sell'] = 1
return dataframe
class StrategyHelper:
"""
simple helper class to predefine a couple of patterns for our
strategy
"""
@staticmethod
def seven_green_candles(dataframe):
"""
evaluates if we are having 7 green candles in a row
:param self:
:param dataframe:
:return:
"""
return (
(dataframe['open'] < dataframe['close']) &
(dataframe['open'].shift(1) < dataframe['close'].shift(1)) &
(dataframe['open'].shift(2) < dataframe['close'].shift(2)) &
(dataframe['open'].shift(3) < dataframe['close'].shift(3)) &
(dataframe['open'].shift(4) < dataframe['close'].shift(4)) &
(dataframe['open'].shift(5) < dataframe['close'].shift(5)) &
(dataframe['open'].shift(6) < dataframe['close'].shift(6)) &
(dataframe['open'].shift(7) < dataframe['close'].shift(7))
)
@staticmethod
def eight_green_candles(dataframe):
"""
evaluates if we are having 8 green candles in a row
:param self:
:param dataframe:
:return:
"""
return (
(dataframe['open'] < dataframe['close']) &
(dataframe['open'].shift(1) < dataframe['close'].shift(1)) &
(dataframe['open'].shift(2) < dataframe['close'].shift(2)) &
(dataframe['open'].shift(3) < dataframe['close'].shift(3)) &
(dataframe['open'].shift(4) < dataframe['close'].shift(4)) &
(dataframe['open'].shift(5) < dataframe['close'].shift(5)) &
(dataframe['open'].shift(6) < dataframe['close'].shift(6)) &
(dataframe['open'].shift(7) < dataframe['close'].shift(7)) &
(dataframe['open'].shift(8) < dataframe['close'].shift(8))
)
@staticmethod
def eight_red_candles(dataframe, shift=0):
"""
evaluates if we are having 8 red candles in a row
:param self:
:param dataframe:
:param shift: shift the pattern by n
:return:
"""
return (
(dataframe['open'].shift(shift) > dataframe['close'].shift(shift)) &
(dataframe['open'].shift(1 + shift) > dataframe['close'].shift(1 + shift)) &
(dataframe['open'].shift(2 + shift) > dataframe['close'].shift(2 + shift)) &
(dataframe['open'].shift(3 + shift) > dataframe['close'].shift(3 + shift)) &
(dataframe['open'].shift(4 + shift) > dataframe['close'].shift(4 + shift)) &
(dataframe['open'].shift(5 + shift) > dataframe['close'].shift(5 + shift)) &
(dataframe['open'].shift(6 + shift) > dataframe['close'].shift(6 + shift)) &
(dataframe['open'].shift(7 + shift) > dataframe['close'].shift(7 + shift)) &
(dataframe['open'].shift(8 + shift) > dataframe['close'].shift(8 + shift))
)
@staticmethod
def four_green_one_red_candle(dataframe):
"""
evaluates if we are having a red candle and 4 previous green
:param self:
:param dataframe:
:return:
"""
return (
(dataframe['open'] > dataframe['close']) &
(dataframe['open'].shift(1) < dataframe['close'].shift(1)) &
(dataframe['open'].shift(2) < dataframe['close'].shift(2)) &
(dataframe['open'].shift(3) < dataframe['close'].shift(3)) &
(dataframe['open'].shift(4) < dataframe['close'].shift(4))
)
@staticmethod
def four_red_one_green_candle(dataframe):
"""
evaluates if we are having a green candle and 4 previous red
:param self:
:param dataframe:
:return:
"""
return (
(dataframe['open'] < dataframe['close']) &
(dataframe['open'].shift(1) > dataframe['close'].shift(1)) &
(dataframe['open'].shift(2) > dataframe['close'].shift(2)) &
(dataframe['open'].shift(3) > dataframe['close'].shift(3)) &
(dataframe['open'].shift(4) > dataframe['close'].shift(4))
)
@staticmethod
def resample( dataframe, interval, factor):
# defines the reinforcement logic
# resampled dataframe to establish if we are in an uptrend, downtrend or sideways trend
df = dataframe.copy()
df = df.set_index(DatetimeIndex(df['date']))
ohlc_dict = {
'open': 'first',
'high': 'max',
'low': 'min',
'close': 'last'
}
df = df.resample(str(int(interval[:-1]) * factor) + 'min', plotoschow=ohlc_dict)
df['resample_sma'] = ta.SMA(df, timeperiod=25, price='close')
df = df.drop(columns=['open', 'high', 'low', 'close'])
df = df.resample(interval[:-1] + 'min')
df = df.interpolate(method='time')
df['date'] = df.index
df.index = range(len(df))
dataframe = merge(dataframe, df, on='date', how='left')
return dataframe