921 lines
38 KiB
Python
921 lines
38 KiB
Python
# GodStraNew Strategy
|
|
# Author: @Mablue (Masoud Azizi)
|
|
# github: https://github.com/mablue/
|
|
# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces buy roi trailing sell --strategy GodStraNew
|
|
# --- Do not remove these libs ---
|
|
from datetime import timedelta, datetime
|
|
|
|
from freqtrade import data
|
|
from freqtrade.persistence import Trade
|
|
from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter
|
|
|
|
from numpy.lib import math
|
|
from freqtrade.strategy.interface import IStrategy, SellCheckTuple
|
|
from pandas import DataFrame
|
|
|
|
# --------------------------------
|
|
|
|
# Add your lib to import here
|
|
# TODO: talib is fast but have not more indicators
|
|
import talib.abstract as ta
|
|
import freqtrade.vendor.qtpylib.indicators as qtpylib
|
|
from functools import reduce
|
|
import numpy as np
|
|
from random import shuffle
|
|
# TODO: this gene is removed 'MAVP' cuz or error on periods
|
|
all_god_genes = {
|
|
'Overlap Studies': {
|
|
'BBANDS-0', # Bollinger Bands
|
|
'BBANDS-1', # Bollinger Bands
|
|
'BBANDS-2', # Bollinger Bands
|
|
'DEMA', # Double Exponential Moving Average
|
|
'EMA', # Exponential Moving Average
|
|
'HT_TRENDLINE', # Hilbert Transform - Instantaneous Trendline
|
|
'KAMA', # Kaufman Adaptive Moving Average
|
|
'MA', # Moving average
|
|
'MAMA-0', # MESA Adaptive Moving Average
|
|
'MAMA-1', # MESA Adaptive Moving Average
|
|
# TODO: Fix this
|
|
# 'MAVP', # Moving average with variable period
|
|
'MIDPOINT', # MidPoint over period
|
|
'MIDPRICE', # Midpoint Price over period
|
|
'SAR', # Parabolic SAR
|
|
'SAREXT', # Parabolic SAR - Extended
|
|
'SMA', # Simple Moving Average
|
|
'T3', # Triple Exponential Moving Average (T3)
|
|
'TEMA', # Triple Exponential Moving Average
|
|
'TRIMA', # Triangular Moving Average
|
|
'WMA', # Weighted Moving Average
|
|
},
|
|
'Momentum Indicators': {
|
|
'ADX', # Average Directional Movement Index
|
|
'ADXR', # Average Directional Movement Index Rating
|
|
'APO', # Absolute Price Oscillator
|
|
'AROON-0', # Aroon
|
|
'AROON-1', # Aroon
|
|
'AROONOSC', # Aroon Oscillator
|
|
'BOP', # Balance Of Power
|
|
'CCI', # Commodity Channel Index
|
|
'CMO', # Chande Momentum Oscillator
|
|
'DX', # Directional Movement Index
|
|
'MACD-0', # Moving Average Convergence/Divergence
|
|
'MACD-1', # Moving Average Convergence/Divergence
|
|
'MACD-2', # Moving Average Convergence/Divergence
|
|
'MACDEXT-0', # MACD with controllable MA type
|
|
'MACDEXT-1', # MACD with controllable MA type
|
|
'MACDEXT-2', # MACD with controllable MA type
|
|
'MACDFIX-0', # Moving Average Convergence/Divergence Fix 12/26
|
|
'MACDFIX-1', # Moving Average Convergence/Divergence Fix 12/26
|
|
'MACDFIX-2', # Moving Average Convergence/Divergence Fix 12/26
|
|
'MFI', # Money Flow Index
|
|
'MINUS_DI', # Minus Directional Indicator
|
|
'MINUS_DM', # Minus Directional Movement
|
|
'MOM', # Momentum
|
|
'PLUS_DI', # Plus Directional Indicator
|
|
'PLUS_DM', # Plus Directional Movement
|
|
'PPO', # Percentage Price Oscillator
|
|
'ROC', # Rate of change : ((price/prevPrice)-1)*100
|
|
# Rate of change Percentage: (price-prevPrice)/prevPrice
|
|
'ROCP',
|
|
'ROCR', # Rate of change ratio: (price/prevPrice)
|
|
# Rate of change ratio 100 scale: (price/prevPrice)*100
|
|
'ROCR100',
|
|
'RSI', # Relative Strength Index
|
|
'STOCH-0', # Stochastic
|
|
'STOCH-1', # Stochastic
|
|
'STOCHF-0', # Stochastic Fast
|
|
'STOCHF-1', # Stochastic Fast
|
|
'STOCHRSI-0', # Stochastic Relative Strength Index
|
|
'STOCHRSI-1', # Stochastic Relative Strength Index
|
|
# 1-day Rate-Of-Change (ROC) of a Triple Smooth EMA
|
|
'TRIX',
|
|
'ULTOSC', # Ultimate Oscillator
|
|
'WILLR', # Williams' %R
|
|
},
|
|
'Volume Indicators': {
|
|
'AD', # Chaikin A/D Line
|
|
'ADOSC', # Chaikin A/D Oscillator
|
|
'OBV', # On Balance Volume
|
|
},
|
|
'Volatility Indicators': {
|
|
'ATR', # Average True Range
|
|
'NATR', # Normalized Average True Range
|
|
'TRANGE', # True Range
|
|
},
|
|
'Price Transform': {
|
|
'AVGPRICE', # Average Price
|
|
'MEDPRICE', # Median Price
|
|
'TYPPRICE', # Typical Price
|
|
'WCLPRICE', # Weighted Close Price
|
|
},
|
|
'Cycle Indicators': {
|
|
'HT_DCPERIOD', # Hilbert Transform - Dominant Cycle Period
|
|
'HT_DCPHASE', # Hilbert Transform - Dominant Cycle Phase
|
|
'HT_PHASOR-0', # Hilbert Transform - Phasor Components
|
|
'HT_PHASOR-1', # Hilbert Transform - Phasor Components
|
|
'HT_SINE-0', # Hilbert Transform - SineWave
|
|
'HT_SINE-1', # Hilbert Transform - SineWave
|
|
'HT_TRENDMODE', # Hilbert Transform - Trend vs Cycle Mode
|
|
},
|
|
'Pattern Recognition': {
|
|
'CDL2CROWS', # Two Crows
|
|
'CDL3BLACKCROWS', # Three Black Crows
|
|
'CDL3INSIDE', # Three Inside Up/Down
|
|
'CDL3LINESTRIKE', # Three-Line Strike
|
|
'CDL3OUTSIDE', # Three Outside Up/Down
|
|
'CDL3STARSINSOUTH', # Three Stars In The South
|
|
'CDL3WHITESOLDIERS', # Three Advancing White Soldiers
|
|
'CDLABANDONEDBABY', # Abandoned Baby
|
|
'CDLADVANCEBLOCK', # Advance Block
|
|
'CDLBELTHOLD', # Belt-hold
|
|
'CDLBREAKAWAY', # Breakaway
|
|
'CDLCLOSINGMARUBOZU', # Closing Marubozu
|
|
'CDLCONCEALBABYSWALL', # Concealing Baby Swallow
|
|
'CDLCOUNTERATTACK', # Counterattack
|
|
'CDLDARKCLOUDCOVER', # Dark Cloud Cover
|
|
'CDLDOJI', # Doji
|
|
'CDLDOJISTAR', # Doji Star
|
|
'CDLDRAGONFLYDOJI', # Dragonfly Doji
|
|
'CDLENGULFING', # Engulfing Pattern
|
|
'CDLEVENINGDOJISTAR', # Evening Doji Star
|
|
'CDLEVENINGSTAR', # Evening Star
|
|
'CDLGAPSIDESIDEWHITE', # Up/Down-gap side-by-side white lines
|
|
'CDLGRAVESTONEDOJI', # Gravestone Doji
|
|
'CDLHAMMER', # Hammer
|
|
'CDLHANGINGMAN', # Hanging Man
|
|
'CDLHARAMI', # Harami Pattern
|
|
'CDLHARAMICROSS', # Harami Cross Pattern
|
|
'CDLHIGHWAVE', # High-Wave Candle
|
|
'CDLHIKKAKE', # Hikkake Pattern
|
|
'CDLHIKKAKEMOD', # Modified Hikkake Pattern
|
|
'CDLHOMINGPIGEON', # Homing Pigeon
|
|
'CDLIDENTICAL3CROWS', # Identical Three Crows
|
|
'CDLINNECK', # In-Neck Pattern
|
|
'CDLINVERTEDHAMMER', # Inverted Hammer
|
|
'CDLKICKING', # Kicking
|
|
'CDLKICKINGBYLENGTH', # Kicking - bull/bear determined by the longer marubozu
|
|
'CDLLADDERBOTTOM', # Ladder Bottom
|
|
'CDLLONGLEGGEDDOJI', # Long Legged Doji
|
|
'CDLLONGLINE', # Long Line Candle
|
|
'CDLMARUBOZU', # Marubozu
|
|
'CDLMATCHINGLOW', # Matching Low
|
|
'CDLMATHOLD', # Mat Hold
|
|
'CDLMORNINGDOJISTAR', # Morning Doji Star
|
|
'CDLMORNINGSTAR', # Morning Star
|
|
'CDLONNECK', # On-Neck Pattern
|
|
'CDLPIERCING', # Piercing Pattern
|
|
'CDLRICKSHAWMAN', # Rickshaw Man
|
|
'CDLRISEFALL3METHODS', # Rising/Falling Three Methods
|
|
'CDLSEPARATINGLINES', # Separating Lines
|
|
'CDLSHOOTINGSTAR', # Shooting Star
|
|
'CDLSHORTLINE', # Short Line Candle
|
|
'CDLSPINNINGTOP', # Spinning Top
|
|
'CDLSTALLEDPATTERN', # Stalled Pattern
|
|
'CDLSTICKSANDWICH', # Stick Sandwich
|
|
# Takuri (Dragonfly Doji with very long lower shadow)
|
|
'CDLTAKURI',
|
|
'CDLTASUKIGAP', # Tasuki Gap
|
|
'CDLTHRUSTING', # Thrusting Pattern
|
|
'CDLTRISTAR', # Tristar Pattern
|
|
'CDLUNIQUE3RIVER', # Unique 3 River
|
|
'CDLUPSIDEGAP2CROWS', # Upside Gap Two Crows
|
|
'CDLXSIDEGAP3METHODS', # Upside/Downside Gap Three Methods
|
|
|
|
},
|
|
'Statistic Functions': {
|
|
'BETA', # Beta
|
|
'CORREL', # Pearson's Correlation Coefficient (r)
|
|
'LINEARREG', # Linear Regression
|
|
'LINEARREG_ANGLE', # Linear Regression Angle
|
|
'LINEARREG_INTERCEPT', # Linear Regression Intercept
|
|
'LINEARREG_SLOPE', # Linear Regression Slope
|
|
'STDDEV', # Standard Deviation
|
|
'TSF', # Time Series Forecast
|
|
'VAR', # Variance
|
|
}
|
|
|
|
}
|
|
god_genes = set()
|
|
########################### SETTINGS ##############################
|
|
|
|
# god_genes = {'SMA'}
|
|
god_genes |= all_god_genes['Overlap Studies']
|
|
god_genes |= all_god_genes['Momentum Indicators']
|
|
god_genes |= all_god_genes['Volume Indicators']
|
|
god_genes |= all_god_genes['Volatility Indicators']
|
|
god_genes |= all_god_genes['Price Transform']
|
|
god_genes |= all_god_genes['Cycle Indicators']
|
|
god_genes |= all_god_genes['Pattern Recognition']
|
|
god_genes |= all_god_genes['Statistic Functions']
|
|
|
|
#timeperiods = [5, 6, 12, 15, 50, 55, 100, 110]
|
|
timeperiods = [5, 10, 20, 50, 100]
|
|
|
|
operators = [
|
|
"D", # Disabled gene
|
|
">", # Indicator, bigger than cross indicator
|
|
"<", # Indicator, smaller than cross indicator
|
|
"=", # Indicator, equal with cross indicator
|
|
"C", # Indicator, crossed the cross indicator
|
|
"CA", # Indicator, crossed above the cross indicator
|
|
"CB", # Indicator, crossed below the cross indicator
|
|
">R", # Normalized indicator, bigger than real number
|
|
"=R", # Normalized indicator, equal with real number
|
|
"<R", # Normalized indicator, smaller than real number
|
|
"/>R", # Normalized indicator devided to cross indicator, bigger than real number
|
|
"/=R", # Normalized indicator devided to cross indicator, equal with real number
|
|
"/<R", # Normalized indicator devided to cross indicator, smaller than real number
|
|
"UT", # Indicator, is in UpTrend status
|
|
"DT", # Indicator, is in DownTrend status
|
|
"OT", # Indicator, is in Off trend status(RANGE)
|
|
"CUT", # Indicator, Entered to UpTrend status
|
|
"CDT", # Indicator, Entered to DownTrend status
|
|
"COT" # Indicator, Entered to Off trend status(RANGE)
|
|
]
|
|
# number of candles to check up,don,off trend.
|
|
TREND_CHECK_CANDLES = 8
|
|
DECIMALS = 2
|
|
|
|
ind_1 = 'HT_TRENDLINE-100'
|
|
ind_2 = 'MACD-1-100'
|
|
ind_3 = 'CCI-20'
|
|
ind_4 = 'CDLDOJISTAR-10'
|
|
ind_5 = 'WCLPRICE-10'
|
|
ind_6 = 'DEMA-50'
|
|
|
|
sell_1 = "CDLLONGLINE-20"
|
|
sell_2 = "STOCHF-1-50"
|
|
sell_3 = "MACDEXT-0-5"
|
|
sell_4 = "MIDPOINT-10"
|
|
sell_5 = "CDLTRISTAR-10"
|
|
sell_6 = "CDLBELTHOLD-50"
|
|
|
|
########################### END SETTINGS ##########################
|
|
# DATAFRAME = DataFrame()
|
|
|
|
god_genes = list(god_genes)
|
|
# print('selected indicators for optimzatin: \n', god_genes)
|
|
|
|
god_genes_with_timeperiod = list()
|
|
for god_gene in god_genes:
|
|
for timeperiod in timeperiods:
|
|
# print(f'{god_gene}-{timeperiod}')
|
|
god_genes_with_timeperiod.append(f'{god_gene}-{timeperiod}')
|
|
|
|
# Let give somethings to CatagoricalParam to Play with them
|
|
# When just one thing is inside catagorical lists
|
|
# TODO: its Not True Way :)
|
|
if len(god_genes) == 1:
|
|
god_genes = god_genes*2
|
|
if len(timeperiods) == 1:
|
|
timeperiods = timeperiods*2
|
|
if len(operators) == 1:
|
|
operators = operators*2
|
|
|
|
|
|
def normalize(df):
|
|
df = (df-df.min())/(df.max()-df.min())
|
|
return df
|
|
|
|
|
|
def gene_calculator(dataframe, indicator):
|
|
# Cuz Timeperiods not effect calculating CDL patterns recognations
|
|
if 'CDL' in indicator:
|
|
splited_indicator = indicator.split('-')
|
|
splited_indicator[1] = "0"
|
|
new_indicator = "-".join(splited_indicator)
|
|
# print(indicator, new_indicator)
|
|
indicator = new_indicator
|
|
|
|
gene = indicator.split("-")
|
|
|
|
gene_name = gene[0]
|
|
gene_len = len(gene)
|
|
|
|
if indicator in dataframe.keys():
|
|
# print(f"{indicator}, calculated befoure")
|
|
# print(len(dataframe.keys()))
|
|
return dataframe[indicator]
|
|
else:
|
|
result = None
|
|
# For Pattern Recognations
|
|
if gene_len == 1:
|
|
# print('gene_len == 1\t', indicator)
|
|
result = getattr(ta, gene_name)(
|
|
dataframe
|
|
)
|
|
return normalize(result)
|
|
elif gene_len == 2:
|
|
# print('gene_len == 2\t', indicator)
|
|
gene_timeperiod = int(gene[1])
|
|
result = getattr(ta, gene_name)(
|
|
dataframe,
|
|
timeperiod=gene_timeperiod,
|
|
)
|
|
return normalize(result)
|
|
# For
|
|
elif gene_len == 3:
|
|
# print('gene_len == 3\t', indicator)
|
|
gene_timeperiod = int(gene[2])
|
|
gene_index = int(gene[1])
|
|
result = getattr(ta, gene_name)(
|
|
dataframe,
|
|
timeperiod=gene_timeperiod,
|
|
).iloc[:, gene_index]
|
|
return normalize(result)
|
|
# For trend operators(MA-5-SMA-4)
|
|
elif gene_len == 4:
|
|
# print('gene_len == 4\t', indicator)
|
|
gene_timeperiod = int(gene[1])
|
|
sharp_indicator = f'{gene_name}-{gene_timeperiod}'
|
|
dataframe[sharp_indicator] = getattr(ta, gene_name)(
|
|
dataframe,
|
|
timeperiod=gene_timeperiod,
|
|
)
|
|
return normalize(ta.SMA(dataframe[sharp_indicator].fillna(0), TREND_CHECK_CANDLES))
|
|
# For trend operators(STOCH-0-4-SMA-4)
|
|
elif gene_len == 5:
|
|
# print('gene_len == 5\t', indicator)
|
|
gene_timeperiod = int(gene[2])
|
|
gene_index = int(gene[1])
|
|
sharp_indicator = f'{gene_name}-{gene_index}-{gene_timeperiod}'
|
|
dataframe[sharp_indicator] = getattr(ta, gene_name)(
|
|
dataframe,
|
|
timeperiod=gene_timeperiod,
|
|
).iloc[:, gene_index]
|
|
return normalize(ta.SMA(dataframe[sharp_indicator].fillna(0), TREND_CHECK_CANDLES))
|
|
|
|
|
|
def condition_generator(dataframe, operator, indicator, crossed_indicator, real_num):
|
|
|
|
condition = (dataframe['volume'] > 10)
|
|
|
|
# TODO : it ill callculated in populate indicators.
|
|
|
|
dataframe[indicator] = gene_calculator(dataframe, indicator)
|
|
dataframe[crossed_indicator] = gene_calculator(dataframe, crossed_indicator)
|
|
|
|
indicator_trend_sma = f"{indicator}-SMA-{TREND_CHECK_CANDLES}"
|
|
if operator in ["UT", "DT", "OT", "CUT", "CDT", "COT"]:
|
|
dataframe[indicator_trend_sma] = gene_calculator(
|
|
dataframe, indicator_trend_sma)
|
|
|
|
if operator == ">":
|
|
condition = (
|
|
dataframe[indicator] > dataframe[crossed_indicator]
|
|
)
|
|
elif operator == "=":
|
|
condition = (
|
|
np.isclose(dataframe[indicator], dataframe[crossed_indicator])
|
|
)
|
|
elif operator == "<":
|
|
condition = (
|
|
dataframe[indicator] < dataframe[crossed_indicator]
|
|
)
|
|
elif operator == "C":
|
|
condition = (
|
|
(qtpylib.crossed_below(dataframe[indicator], dataframe[crossed_indicator])) |
|
|
(qtpylib.crossed_above(
|
|
dataframe[indicator], dataframe[crossed_indicator]))
|
|
)
|
|
elif operator == "CA":
|
|
condition = (
|
|
qtpylib.crossed_above(
|
|
dataframe[indicator], dataframe[crossed_indicator])
|
|
)
|
|
elif operator == "CB":
|
|
condition = (
|
|
qtpylib.crossed_below(
|
|
dataframe[indicator], dataframe[crossed_indicator])
|
|
)
|
|
elif operator == ">R":
|
|
condition = (
|
|
dataframe[indicator] > real_num
|
|
)
|
|
elif operator == "=R":
|
|
condition = (
|
|
np.isclose(dataframe[indicator], real_num)
|
|
)
|
|
elif operator == "<R":
|
|
condition = (
|
|
dataframe[indicator] < real_num
|
|
)
|
|
elif operator == "/>R":
|
|
condition = (
|
|
dataframe[indicator].div(dataframe[crossed_indicator]) > real_num
|
|
)
|
|
elif operator == "/=R":
|
|
condition = (
|
|
np.isclose(dataframe[indicator].div(
|
|
dataframe[crossed_indicator]), real_num)
|
|
)
|
|
elif operator == "/<R":
|
|
condition = (
|
|
dataframe[indicator].div(dataframe[crossed_indicator]) < real_num
|
|
)
|
|
elif operator == "UT":
|
|
condition = (
|
|
dataframe[indicator] > dataframe[indicator_trend_sma]
|
|
)
|
|
elif operator == "DT":
|
|
condition = (
|
|
dataframe[indicator] < dataframe[indicator_trend_sma]
|
|
)
|
|
elif operator == "OT":
|
|
condition = (
|
|
|
|
np.isclose(dataframe[indicator], dataframe[indicator_trend_sma])
|
|
)
|
|
elif operator == "CUT":
|
|
condition = (
|
|
(
|
|
qtpylib.crossed_above(
|
|
dataframe[indicator],
|
|
dataframe[indicator_trend_sma]
|
|
)
|
|
) &
|
|
(
|
|
dataframe[indicator] > dataframe[indicator_trend_sma]
|
|
)
|
|
)
|
|
elif operator == "CDT":
|
|
condition = (
|
|
(
|
|
qtpylib.crossed_below(
|
|
dataframe[indicator],
|
|
dataframe[indicator_trend_sma]
|
|
)
|
|
) &
|
|
(
|
|
dataframe[indicator] < dataframe[indicator_trend_sma]
|
|
)
|
|
)
|
|
elif operator == "COT":
|
|
condition = (
|
|
(
|
|
(
|
|
qtpylib.crossed_below(
|
|
dataframe[indicator],
|
|
dataframe[indicator_trend_sma]
|
|
)
|
|
) |
|
|
(
|
|
qtpylib.crossed_above(
|
|
dataframe[indicator],
|
|
dataframe[indicator_trend_sma]
|
|
)
|
|
)
|
|
) &
|
|
(
|
|
np.isclose(
|
|
dataframe[indicator],
|
|
dataframe[indicator_trend_sma]
|
|
)
|
|
)
|
|
)
|
|
|
|
return condition, dataframe
|
|
|
|
|
|
class GodStraJD3_6_53_1(IStrategy):
|
|
# #################### RESULTS PASTE PLACE ####################
|
|
# ROI table:
|
|
minimal_roi = {
|
|
"0": 1,
|
|
# "600": 0.12,
|
|
# "1200": 0.08,
|
|
# "2400": 0.06,
|
|
# "3600": 0.04,
|
|
# "7289": 0
|
|
}
|
|
|
|
# Stoploss:
|
|
stoploss = -1
|
|
# Buy hypers
|
|
timeframe = '5m'
|
|
|
|
# Trailing stoploss
|
|
trailing_stop = False
|
|
trailing_stop_positive = 0.15
|
|
trailing_stop_positive_offset = 0.20
|
|
trailing_only_offset_is_reached = True
|
|
|
|
|
|
plot_config = {
|
|
# Main plot indicators (Moving averages, ...)
|
|
'main_plot': {
|
|
'bb_lowerband': {'color': 'red'},
|
|
'bb_upperband': {'color': 'green'},
|
|
'sma100': {'color': 'blue'},
|
|
'sma10': {'color': 'yellow'},
|
|
'min': {'color': 'white'},
|
|
'max': {'color': 'white'},
|
|
'sma20': {'color': 'cyan'}
|
|
},
|
|
'subplots': {
|
|
# Subplots - each dict defines one additional plot
|
|
"BB": {
|
|
'bb_width': {'color': 'white'},
|
|
'bb_min': {'color': 'red'},
|
|
},
|
|
"Ind": {
|
|
ind_1: {'color': 'green'},
|
|
ind_2: {'color': 'blue'},
|
|
ind_3: {'color': 'red'}
|
|
},
|
|
"Ind2": {
|
|
ind_4: {'color': 'green'},
|
|
ind_5: {'color': 'blue'},
|
|
ind_6: {'color': 'red'}
|
|
},
|
|
"Profit": {
|
|
'profit': {'color': 'pink'},
|
|
},
|
|
"Rsi": {
|
|
'rsi': {'color': 'pink'},
|
|
},
|
|
# "rolling": {
|
|
# 'bb_rolling': {'color': '#87e470'},
|
|
# "bb_rolling_min": {'color': '#ac3e2a'}
|
|
# },
|
|
"percent": {
|
|
"percent": {'color': 'green'},
|
|
"percent3": {'color': 'blue'},
|
|
"percent5": {'color': 'red'}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
# #################### END OF RESULT PLACE ####################
|
|
|
|
# TODO: Its not dry code!
|
|
# Buy Hyperoptable Parameters/Spaces.
|
|
buy_crossed_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default="ADD-20", space='buy')
|
|
buy_crossed_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default="ASIN-6", space='buy')
|
|
buy_crossed_indicator2 = CategoricalParameter(god_genes_with_timeperiod, default="CDLEVENINGSTAR-50", space='buy')
|
|
|
|
buy_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default="SMA-100", space='buy')
|
|
buy_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default="WILLR-50", space='buy')
|
|
buy_indicator2 = CategoricalParameter(god_genes_with_timeperiod, default="CDLHANGINGMAN-20", space='buy')
|
|
|
|
buy_operator0 = CategoricalParameter(operators, default="/<R", space='buy')
|
|
buy_operator1 = CategoricalParameter(operators, default="<R", space='buy')
|
|
buy_operator2 = CategoricalParameter(operators, default="CB", space='buy')
|
|
|
|
protection_max_allowed_dd = DecimalParameter(0, 1, decimals=DECIMALS, default=0.04, space='protection')
|
|
protection_stop = IntParameter(0, 100, default=48, space='protection')
|
|
|
|
buy_real_num0 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.89009, space='buy')
|
|
buy_real_num1 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.56953, space='buy')
|
|
buy_real_num2 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.38365, space='buy')
|
|
|
|
# Sell Hyperoptable Parameters/Spaces.
|
|
sell_crossed_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default="CDLSHOOTINGSTAR-150", space='sell')
|
|
sell_crossed_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default="MAMA-1-100", space='sell')
|
|
sell_crossed_indicator2 = CategoricalParameter(god_genes_with_timeperiod, default="CDLMATHOLD-6", space='sell')
|
|
|
|
sell_indicator0 = CategoricalParameter(god_genes_with_timeperiod, default="CDLUPSIDEGAP2CROWS-5", space='sell')
|
|
sell_indicator1 = CategoricalParameter(god_genes_with_timeperiod, default="CDLHARAMICROSS-150", space='sell')
|
|
sell_indicator2 = CategoricalParameter(god_genes_with_timeperiod, default="CDL2CROWS-5", space='sell')
|
|
|
|
sell_operator0 = CategoricalParameter(operators, default="<R", space='sell')
|
|
sell_operator1 = CategoricalParameter(operators, default="D", space='sell')
|
|
sell_operator2 = CategoricalParameter(operators, default="/>R", space='sell')
|
|
|
|
sell_real_num0 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.09731, space='sell')
|
|
sell_real_num1 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.81657, space='sell')
|
|
sell_real_num2 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.87267, space='sell')
|
|
|
|
@property
|
|
def protections(self):
|
|
return [
|
|
{
|
|
"method": "CooldownPeriod",
|
|
"stop_duration_candles": 10
|
|
},
|
|
{
|
|
"method": "MaxDrawdown",
|
|
"lookback_period_candles": 48,
|
|
"trade_limit": 2,
|
|
"stop_duration_candles": self.protection_stop.value,
|
|
"max_allowed_drawdown": self.protection_max_allowed_dd.value,
|
|
"only_per_pair": False
|
|
},
|
|
]
|
|
|
|
def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float,
|
|
current_profit: float, **kwargs):
|
|
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
|
|
last_candle = dataframe.iloc[-1].squeeze()
|
|
previous_last_candle = dataframe.iloc[-2].squeeze()
|
|
previous_5_candle = dataframe.iloc[-5].squeeze()
|
|
#print("last_candle", last_candle)
|
|
#print("previous_last_candle", previous_last_candle)
|
|
|
|
#dataframe.iloc[-1]['profit'] = current_profit
|
|
count = 0
|
|
for coin, balance in self.wallets.get_all_balances().items():
|
|
count = count + 1
|
|
# print(coin, " ", balance)
|
|
# print("count=", count)
|
|
|
|
if (current_profit > 0) \
|
|
& ((previous_5_candle['sma10'] > last_candle['sma10'] * 1.002) \
|
|
| (last_candle['percent3'] < -0.01) | (last_candle['percent5'] < -0.01)) \
|
|
& ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)):
|
|
# print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate)
|
|
return 'sma10'
|
|
|
|
if (current_profit > 0) \
|
|
& ((current_time - trade.open_date_utc).seconds >= 3600) \
|
|
& ((previous_last_candle['sma20'] > last_candle['sma20']) &
|
|
((last_candle['percent10'] < 0) | (last_candle['percent20'] < 0))):
|
|
# print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate)
|
|
return 'sma20'
|
|
|
|
if (current_profit > 0) \
|
|
& ((current_time - trade.open_date_utc).days >= 3) \
|
|
& ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < -0.005)) \
|
|
& ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)):
|
|
# print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate)
|
|
return 'old_sma10'
|
|
|
|
if (current_profit > -0.01) \
|
|
& ((current_time - trade.open_date_utc).days >= 6) \
|
|
& ((previous_5_candle['sma10'] > last_candle['sma10']) | (last_candle['percent3'] < 0)) \
|
|
& ((last_candle['percent'] < 0) & (last_candle['percent3'] < 0)):
|
|
# print("over_bb_band_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate)
|
|
return 'very_old_sma10'
|
|
|
|
if (current_profit > 0) \
|
|
& (last_candle['rsi'] > 88): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)):
|
|
# print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate)
|
|
return 'over_rsi'
|
|
|
|
def informative_pairs(self):
|
|
return []
|
|
|
|
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
|
# MACD
|
|
# macd = ta.MACD(dataframe)
|
|
# dataframe['macd'] = macd['macd']
|
|
# dataframe['macdsignal'] = macd['macdsignal']
|
|
# dataframe['macdhist'] = macd['macdhist']
|
|
|
|
# # # Plus Directional Indicator / Movement
|
|
# dataframe['plus_dm'] = ta.PLUS_DM(dataframe)
|
|
# dataframe['plus_di'] = ta.PLUS_DI(dataframe)
|
|
#
|
|
# # Minus Directional Indicator / Movement
|
|
# dataframe['adx'] = ta.ADX(dataframe)
|
|
|
|
dataframe[ind_1] = gene_calculator(dataframe, ind_1)
|
|
dataframe[ind_2] = gene_calculator(dataframe, ind_2)
|
|
dataframe[ind_3] = gene_calculator(dataframe, ind_3)
|
|
|
|
dataframe[ind_4] = gene_calculator(dataframe, ind_4)
|
|
dataframe[ind_5] = gene_calculator(dataframe, ind_5)
|
|
dataframe[ind_6] = gene_calculator(dataframe, ind_6)
|
|
|
|
dataframe[sell_1] = gene_calculator(dataframe, sell_1)
|
|
dataframe[sell_2] = gene_calculator(dataframe, sell_2)
|
|
dataframe[sell_3] = gene_calculator(dataframe, sell_3)
|
|
|
|
dataframe[sell_4] = gene_calculator(dataframe, sell_4)
|
|
dataframe[sell_5] = gene_calculator(dataframe, sell_5)
|
|
dataframe[sell_6] = gene_calculator(dataframe, sell_6)
|
|
|
|
# dataframe['MINUS_DM-5'] = ta.MINUS_DM(dataframe, timeperiod=5)
|
|
# dataframe['LINEARREG-50'] = ta.LINEARREG(dataframe, timeperiod=50)
|
|
# dataframe['MA-20'] = ta.MA(dataframe, timeperiod=20)
|
|
# stoch = ta.STOCH(dataframe, timeperiod=10)
|
|
# # print(stoch)
|
|
# dataframe['STOCH-1-10'] = stoch['slowd']
|
|
# dataframe['CDLDRAGONFLYDOJI-5'] = ta.CDLDRAGONFLYDOJI(dataframe, timeperiod=5)
|
|
|
|
# dataframe['minus_di'] = ta.MINUS_DI(dataframe)
|
|
|
|
# dataframe['min'] = ta.MIN(dataframe)
|
|
# dataframe['max'] = ta.MAX(dataframe)
|
|
|
|
# # Aroon, Aroon Oscillator
|
|
# aroon = ta.AROON(dataframe)
|
|
# dataframe['aroonup'] = aroon['aroonup']
|
|
# dataframe['aroondown'] = aroon['aroondown']
|
|
# dataframe['aroonosc'] = ta.AROONOSC(dataframe)
|
|
|
|
dataframe['profit'] = 0
|
|
# RSI
|
|
dataframe['rsi'] = ta.RSI(dataframe)
|
|
|
|
# # 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['ema21'] = ta.EMA(dataframe, timeperiod=21)
|
|
# dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50)
|
|
# dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100)
|
|
|
|
# # SMA - Simple Moving Average
|
|
# dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3)
|
|
# dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5)
|
|
dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10)
|
|
dataframe['sma20'] = ta.SMA(dataframe, timeperiod=20)
|
|
dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50)
|
|
dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100)
|
|
# dataframe['sma200'] = ta.SMA(dataframe, timeperiod=200)
|
|
# dataframe['sma200_95'] = ta.SMA(dataframe, timeperiod=200) * 0.95
|
|
# dataframe['sma200_98'] = ta.SMA(dataframe, timeperiod=200) * 0.98
|
|
# dataframe['sma500'] = ta.SMA(dataframe, timeperiod=500)
|
|
# dataframe['sma500_90'] = ta.SMA(dataframe, timeperiod=500) * 0.9
|
|
# dataframe['sma500_95'] = ta.SMA(dataframe, timeperiod=500) * 0.95
|
|
# dataframe['sma500_20'] = ta.SMA(dataframe, timeperiod=500) * 0.2
|
|
dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"]
|
|
dataframe["percent5"] = dataframe["percent"].rolling(5).sum()
|
|
dataframe["percent3"] = dataframe["percent"].rolling(3).sum()
|
|
dataframe["percent10"] = dataframe["percent"].rolling(10).sum()
|
|
dataframe["percent20"] = dataframe["percent"].rolling(20).sum()
|
|
dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200)
|
|
dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20)
|
|
dataframe["volume10"] = dataframe["volume"].rolling(10).mean()
|
|
|
|
dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200)
|
|
dataframe['max_min'] = dataframe['max'] / dataframe['min']
|
|
# 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']
|
|
dataframe["bb_percent"] = (
|
|
(dataframe["close"] - dataframe["bb_lowerband"]) /
|
|
(dataframe["bb_upperband"] - dataframe["bb_lowerband"])
|
|
)
|
|
dataframe["bb_width"] = (
|
|
(dataframe["bb_upperband"] - dataframe["bb_lowerband"]) / dataframe["bb_middleband"]
|
|
)
|
|
dataframe['bb_min'] = ta.MIN(dataframe['bb_lowerband'], timeperiod=36)
|
|
|
|
# Bollinger Bands - Weighted (EMA based instead of SMA)
|
|
# weighted_bollinger = qtpylib.weighted_bollinger_bands(
|
|
# qtpylib.typical_price(dataframe), window=20, stds=2
|
|
# )
|
|
# dataframe["wbb_upperband"] = weighted_bollinger["upper"]
|
|
# dataframe["wbb_lowerband"] = weighted_bollinger["lower"]
|
|
# dataframe["wbb_middleband"] = weighted_bollinger["mid"]
|
|
# dataframe["wbb_percent"] = (
|
|
# (dataframe["close"] - dataframe["wbb_lowerband"]) /
|
|
# (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"])
|
|
# )
|
|
# dataframe["wbb_width"] = (
|
|
# (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) / dataframe["wbb_middleband"]
|
|
# )
|
|
|
|
# # EMA - Exponential Moving Average
|
|
# dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3)
|
|
return dataframe
|
|
|
|
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
|
|
|
conditions = list()
|
|
|
|
# TODO: Its not dry code!
|
|
buy_indicator = self.buy_indicator0.value
|
|
buy_crossed_indicator = self.buy_crossed_indicator0.value
|
|
buy_operator = self.buy_operator0.value
|
|
buy_real_num = self.buy_real_num0.value
|
|
condition, dataframe = condition_generator(
|
|
dataframe,
|
|
buy_operator,
|
|
buy_indicator,
|
|
buy_crossed_indicator,
|
|
buy_real_num
|
|
)
|
|
conditions.append(condition)
|
|
# backup
|
|
buy_indicator = self.buy_indicator1.value
|
|
buy_crossed_indicator = self.buy_crossed_indicator1.value
|
|
buy_operator = self.buy_operator1.value
|
|
buy_real_num = self.buy_real_num1.value
|
|
|
|
condition, dataframe = condition_generator(
|
|
dataframe,
|
|
buy_operator,
|
|
buy_indicator,
|
|
buy_crossed_indicator,
|
|
buy_real_num
|
|
)
|
|
conditions.append(condition)
|
|
|
|
buy_indicator = self.buy_indicator2.value
|
|
buy_crossed_indicator = self.buy_crossed_indicator2.value
|
|
buy_operator = self.buy_operator2.value
|
|
buy_real_num = self.buy_real_num2.value
|
|
condition, dataframe = condition_generator(
|
|
dataframe,
|
|
buy_operator,
|
|
buy_indicator,
|
|
buy_crossed_indicator,
|
|
buy_real_num
|
|
)
|
|
conditions.append(condition)
|
|
|
|
if conditions:
|
|
dataframe.loc[
|
|
(
|
|
(dataframe['close'].shift(2) < dataframe['bb_lowerband'].shift(2))
|
|
& (dataframe['bb_width'].shift(2) >= 0.12)
|
|
& (dataframe['volume'].shift(2) * dataframe['close'].shift(2) / 1000 > 100)
|
|
) | (
|
|
(reduce(lambda x, y: x & y, conditions))
|
|
& (dataframe['volume10'] * dataframe['close'] / 1000 >= 100)
|
|
& (dataframe['close'] < dataframe['sma20'])
|
|
& (dataframe['close'] < dataframe['sma50'])
|
|
# & (dataframe['sma10'].shift(1) <= dataframe['sma10'])
|
|
# (dataframe['close'] * 0.99 < dataframe['bb_lowerband'])
|
|
# (dataframe['rsi'] < 45)
|
|
),
|
|
'buy']=1
|
|
# print(len(dataframe.keys()))
|
|
|
|
return dataframe
|
|
|
|
def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
|
|
|
conditions = list()
|
|
# TODO: Its not dry code!
|
|
sell_indicator = self.sell_indicator0.value
|
|
sell_crossed_indicator = self.sell_crossed_indicator0.value
|
|
sell_operator = self.sell_operator0.value
|
|
sell_real_num = self.sell_real_num0.value
|
|
condition, dataframe = condition_generator(
|
|
dataframe,
|
|
sell_operator,
|
|
sell_indicator,
|
|
sell_crossed_indicator,
|
|
sell_real_num
|
|
)
|
|
conditions.append(condition)
|
|
|
|
sell_indicator = self.sell_indicator1.value
|
|
sell_crossed_indicator = self.sell_crossed_indicator1.value
|
|
sell_operator = self.sell_operator1.value
|
|
sell_real_num = self.sell_real_num1.value
|
|
condition, dataframe = condition_generator(
|
|
dataframe,
|
|
sell_operator,
|
|
sell_indicator,
|
|
sell_crossed_indicator,
|
|
sell_real_num
|
|
)
|
|
conditions.append(condition)
|
|
|
|
sell_indicator = self.sell_indicator2.value
|
|
sell_crossed_indicator = self.sell_crossed_indicator2.value
|
|
sell_operator = self.sell_operator2.value
|
|
sell_real_num = self.sell_real_num2.value
|
|
condition, dataframe = condition_generator(
|
|
dataframe,
|
|
sell_operator,
|
|
sell_indicator,
|
|
sell_crossed_indicator,
|
|
sell_real_num
|
|
)
|
|
conditions.append(condition)
|
|
|
|
#dataframe.loc[(dataframe['close'] < dataframe['bb_lowerband']),'sell']=1
|
|
|
|
# count = 0
|
|
# for coin, balance in self.wallets.get_all_balances().items():
|
|
# count = count + 1
|
|
# # print(coin, " ", balance)
|
|
# if (count == self.config['max_open_trades']) & (current_profit < -0.04) \
|
|
# & ((current_time - trade.open_date_utc).seconds >= 3600 * 6):
|
|
# self.lock_pair(pair, until=current_time + timedelta(hours=10))
|
|
# print("stop_short_loss", pair, trade, " profit=", current_profit, " rate=", current_rate,
|
|
# "count=", count, "max=", self.config['max_open_trades'])
|
|
# return 'stop_short_loss'
|
|
|
|
# print("sell pair=", self.wallets.get_all_balances()[metadata['pair']])
|
|
|
|
# if conditions:
|
|
# dataframe.loc[
|
|
# (
|
|
# reduce(lambda x, y: x & y, conditions) #(dataframe['percent3'] > 0)
|
|
# # | (dataframe['percent3'] < -0.03)
|
|
# ),
|
|
# 'sell']=1
|
|
|
|
dataframe.loc[
|
|
(
|
|
(dataframe['close'] < dataframe['open']) &
|
|
(dataframe['close'].shift(1) < dataframe['open'].shift(1)) &
|
|
(dataframe['close'].shift(2) < dataframe['open'].shift(2)) &
|
|
(dataframe['close'] < dataframe['bb_lowerband']) &
|
|
(((dataframe['bb_lowerband'].shift(2) - dataframe['bb_lowerband']) / dataframe['bb_lowerband']) >= 0.02)
|
|
# (((dataframe['close'].shift(1) - dataframe['close']) / dataframe['close']) >= 0.025)
|
|
), 'sell'] = 1
|
|
|
|
return dataframe
|
|
|