1198 lines
53 KiB
Python
1198 lines
53 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.strategy.strategy_helper import merge_informative_pair
|
|
|
|
from freqtrade import data
|
|
from freqtrade.persistence import Trade
|
|
from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter, IntParameter, BooleanParameter
|
|
|
|
from numpy.lib import math
|
|
from freqtrade.strategy.interface import IStrategy
|
|
import pandas
|
|
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
|
|
import user_data.strategies.custom_indicators as csa
|
|
|
|
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
|
|
########################### 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
|
|
|
|
# number of candles to check up,don,off trend.
|
|
TREND_CHECK_CANDLES = 8
|
|
DECIMALS = 2
|
|
|
|
buy_crossed_indicator0 = 'MINUS_DM-5'
|
|
buy_operator0 = "/<R"
|
|
buy_indicator0 = 'MA-20'
|
|
|
|
buy_crossed_indicator1 = 'DX-5'
|
|
buy_operator1 = ">"
|
|
buy_indicator1 = 'STOCH-1-10'
|
|
|
|
buy_crossed_indicator2 = 'LINEARREG-50'
|
|
buy_operator2 = "/<R"
|
|
buy_indicator2 = 'CDLDRAGONFLYDOJI-5'
|
|
|
|
# "buy_real_num0": 0.46,
|
|
# "buy_real_num1": 0.48,
|
|
# "buy_real_num2": 0.67
|
|
|
|
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_9(IStrategy):
|
|
# #################### RESULTS PASTE PLACE ####################
|
|
# ROI table:
|
|
minimal_roi = {
|
|
"0": 10,
|
|
# "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
|
|
|
|
sell_percent = DecimalParameter(0, 0.02, decimals=3, default=0, space='sell')
|
|
sell_percent3 = DecimalParameter(0, 0.03, decimals=3, default=0, space='sell')
|
|
sell_percent5 = DecimalParameter(0, 0.04, decimals=3, default=0, space='sell')
|
|
|
|
# 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')
|
|
|
|
plot_config = {
|
|
# Main plot indicators (Moving averages, ...)
|
|
'main_plot': {
|
|
'bb_lowerband': {'color': 'red'},
|
|
'bb_upperband': {'color': 'green'},
|
|
'sma100': {'color': 'blue'},
|
|
'sma10': {'color': 'yellow'},
|
|
'min20': {'color': 'white'},
|
|
'max20': {'color': 'white'},
|
|
'min50': {'color': 'yellow'},
|
|
'max50': {'color': 'yellow'},
|
|
'sma20': {'color': 'cyan'}
|
|
},
|
|
'subplots': {
|
|
# Subplots - each dict defines one additional plot
|
|
"BB": {
|
|
'bb_width': {'color': 'white'},
|
|
'bb_min': {'color': 'red'},
|
|
},
|
|
"Ind0": {
|
|
buy_crossed_indicator0: {'color': 'green'},
|
|
buy_indicator0: {'color': 'red'}
|
|
},
|
|
"Cond1": {
|
|
"cond1": {'color': 'yellow'}
|
|
},
|
|
"Pentes": {
|
|
"bb_lower_pente": {'color': 'red'},
|
|
"sma10_pente": {'color': 'yellow'},
|
|
"sma20_pente": {'color': 'cyan'}
|
|
},
|
|
# "Ind2": {
|
|
# buy_indicator2: {'color': 'cyan'},
|
|
# buy_crossed_indicator2: {'color': 'blue'},
|
|
# },
|
|
# "Rsi": {
|
|
# 'rsi': {'color': 'pink'},
|
|
# },
|
|
# "rolling": {
|
|
# 'bb_rolling': {'color': '#87e470'},
|
|
# "bb_rolling_min": {'color': '#ac3e2a'}
|
|
# },
|
|
"percent": {
|
|
"percent": {'color': 'green'},
|
|
"percent3": {'color': 'blue'},
|
|
"percent5": {'color': 'red'},
|
|
"profit_percent": {'color': 'yellow'}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
# #################### END OF RESULT PLACE ####################
|
|
|
|
# TODO: Its not dry code!
|
|
# Buy Hyperoptable Parameters/Spaces.
|
|
# "buy_real_num0": 0.46,
|
|
# "buy_real_num1": 0.48,
|
|
# "buy_real_num2": 0.67
|
|
#
|
|
# buy_real_num0 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.46, space='buy')
|
|
# buy_real_num1 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.48, space='buy')
|
|
# buy_real_num2 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.67, space='buy')
|
|
|
|
# profit_no_change = BooleanParameter(default=True, space="buy")
|
|
# profit_quick_lost = BooleanParameter(default=True, space="buy")
|
|
# profit_sma10 = BooleanParameter(default=True, space="buy")
|
|
# profit_sma20 = BooleanParameter(default=True, space="buy")
|
|
# profit_quick_gain = BooleanParameter(default=True, space="buy")
|
|
# profit_quick_gain_3 = BooleanParameter(default=True, space="buy")
|
|
# profit_old_sma10 = BooleanParameter(default=True, space="buy")
|
|
# profit_very_old_sma10 = BooleanParameter(default=True, space="buy")
|
|
# profit_over_rsi = BooleanParameter(default=True, space="buy")
|
|
# profit_short_loss = BooleanParameter(default=True, space="buy")
|
|
|
|
profit_no_change = True
|
|
profit_old_sma10 = True
|
|
profit_over_rsi = True
|
|
profit_quick_gain = True
|
|
profit_quick_gain_3 = True
|
|
profit_quick_lost = True
|
|
profit_short_loss = True
|
|
profit_sma10 = True
|
|
profit_sma20 = True
|
|
profit_very_old_sma10 = False
|
|
|
|
# buy_cond1_num0 = DecimalParameter(0, 10, decimals=DECIMALS, default=1, space='buy')
|
|
# buy_cond1_num1 = DecimalParameter(0, 10, decimals=DECIMALS, default=2, space='buy')
|
|
# buy_bbwidth_num0 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.035, space='buy')
|
|
# buy_bbwidth_num1 = DecimalParameter(0, 1, decimals=DECIMALS, default=0.055, space='buy')
|
|
|
|
buy_signal_condition_high = DecimalParameter(1, 2, decimals=2, default=1, space='buy')
|
|
buy_signal_bb_width = DecimalParameter(1, 2, decimals=2, default=1, space='buy')
|
|
buy_signal_sma_max = DecimalParameter(1, 1.1, decimals=2, default=1, space='buy')
|
|
buy_signal_sma_min = DecimalParameter(1, 1.1, decimals=2, default=1, space='buy')
|
|
|
|
buy_signal_sma10 = DecimalParameter(1, 1.1, decimals=2, default=1, space='buy')
|
|
buy_signal_sma10_pente = DecimalParameter(0, 0.1, decimals=3, default=0.02, space='buy')
|
|
buy_signal_sma_percent50 = DecimalParameter(0, 0.1, decimals=3, default=0.025, space='buy')
|
|
|
|
buy_signal_minus = DecimalParameter(1, 2, decimals=2, default=1, space='buy')
|
|
|
|
protection_max_allowed_dd = DecimalParameter(0, 1, decimals=DECIMALS, default=0.04, space='protection')
|
|
protection_stop = IntParameter(1, 100, default=48, space='protection')
|
|
protection_stoploss_stop = IntParameter(1, 100, default=48, space='protection')
|
|
lookback = IntParameter(1, 200, default=48, space='protection')
|
|
trade_limit = IntParameter(1, 10, default=2, space='protection')
|
|
protection_cooldown = IntParameter(1, 100, default=10, space='protection')
|
|
lookback_stoploss = IntParameter(1, 200, default=48, space='protection')
|
|
trade_limit_stoploss = IntParameter(1, 10, default=2, space='protection')
|
|
|
|
# """
|
|
# Informative Pair Definitions
|
|
# """
|
|
# inf_timeframe = '1h'
|
|
# # Strategy Specific Variable Storage
|
|
# custom_trade_info = {}
|
|
# custom_fiat = "USD" # Only relevant if stake is BTC or ETH
|
|
# custom_btc_inf = False # Don't change this.
|
|
#
|
|
# def informative_pairs(self):
|
|
# # add all whitelisted pairs on informative timeframe
|
|
# pairs = self.dp.current_whitelist()
|
|
# informative_pairs = [(pair, self.inf_timeframe) for pair in pairs]
|
|
#
|
|
# # add extra informative pairs if the stake is BTC or ETH
|
|
# if self.config['stake_currency'] in ('BTC', 'ETH'):
|
|
# for pair in pairs:
|
|
# coin, stake = pair.split('/')
|
|
# coin_fiat = f"{coin}/{self.custom_fiat}"
|
|
# informative_pairs += [(coin_fiat, self.timeframe)]
|
|
#
|
|
# stake_fiat = f"{self.config['stake_currency']}/{self.custom_fiat}"
|
|
# informative_pairs += [(stake_fiat, self.timeframe)]
|
|
# # if BTC/STAKE is not in whitelist, add it as an informative pair on both timeframes
|
|
# else:
|
|
# btc_stake = f"BTC/{self.config['stake_currency']}"
|
|
# if not btc_stake in pairs:
|
|
# informative_pairs += [(btc_stake, self.timeframe)]
|
|
#
|
|
# return informative_pairs
|
|
|
|
def calc_profit(self, price: float, current: float) -> float:
|
|
fee = 1.0007
|
|
profit = ((current*fee) - (price*fee))
|
|
|
|
return float(f"{profit:.8f}")
|
|
|
|
def calc_percentage_lower(self, price: float, current: float) -> float:
|
|
fee = 1.0007
|
|
price = price*fee
|
|
current = current*fee
|
|
lowerpercent = ((price-current)/(price*fee))*100
|
|
return float(f"{lowerpercent:.8f}")
|
|
|
|
@property
|
|
def protections(self):
|
|
return [
|
|
{
|
|
"method": "CooldownPeriod",
|
|
"stop_duration_candles": self.protection_cooldown.value
|
|
},
|
|
# {
|
|
# "method": "MaxDrawdown",
|
|
# "lookback_period_candles": self.lookback.value,
|
|
# "trade_limit": self.trade_limit.value,
|
|
# "stop_duration_candles": self.protection_stop.value,
|
|
# "max_allowed_drawdown": self.protection_max_allowed_dd.value,
|
|
# "only_per_pair": False
|
|
# },
|
|
# {
|
|
# "method": "StoplossGuard",
|
|
# "lookback_period_candles": self.lookback_stoploss.value,
|
|
# "trade_limit": self.trade_limit_stoploss.value,
|
|
# "stop_duration_candles": self.protection_stoploss_stop.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()
|
|
|
|
if (current_profit > 0) & (
|
|
(last_candle['percent5'] <= -0.015)
|
|
| (last_candle['percent10'] <= -0.015)
|
|
| (last_candle['percent20'] <= -0.015)
|
|
) \
|
|
& ((current_time - trade.open_date_utc).seconds >= 2400):
|
|
return "quick_lost"
|
|
|
|
if self.profit_quick_lost:
|
|
if (current_profit >= 0) & (last_candle['percent3'] < -0.01) & (last_candle['percent5'] < 0) \
|
|
& ((current_time - trade.open_date_utc).seconds >= 2400):
|
|
return "quick_lost_1H"
|
|
|
|
if self.profit_no_change:
|
|
if (current_profit > 0.015) & (last_candle['percent20'] < 0.0) & \
|
|
(last_candle['sma10'] < 0.0) & ((current_time - trade.open_date_utc).seconds >= 2400):
|
|
return "no_change"
|
|
|
|
#if (current_profit > 0.01) & (last_candle['rsi'] < 30):
|
|
# return "small_rsi"
|
|
if self.profit_quick_gain_3:
|
|
if (current_profit >= 0.03) & (last_candle['percent3'] < 0) & ((current_time - trade.open_date_utc).seconds <= 3600):
|
|
return "quick_gain_3"
|
|
if self.profit_quick_gain:
|
|
if (0.01 < current_profit < 0.03) & (last_candle['percent3'] < 0): #& ((current_time - trade.open_date_utc).seconds <= 3600)
|
|
return "quick_gain"
|
|
|
|
if self.profit_sma10:
|
|
if (current_profit > 0.01) \
|
|
& ((previous_5_candle['sma10'] > last_candle['sma10'] * 1.005) \
|
|
| (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 self.profit_sma20:
|
|
if (current_profit > 0.005) & (last_candle['percent5'] < 0) & (last_candle['sma20_pente'] < -0.015)\
|
|
& ((current_time - trade.open_date_utc).seconds >= 3600) \
|
|
& ((previous_last_candle['sma20'] > last_candle['sma20'] * 1.005) &
|
|
((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 self.profit_old_sma10:
|
|
# 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['percent5'] < -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 self.profit_very_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['percent5'] < 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 self.profit_over_rsi:
|
|
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'
|
|
|
|
if self.profit_short_loss:
|
|
if (current_profit > -0.01) & (previous_last_candle['percent10'] > 0.04) & (last_candle['percent'] < 0)\
|
|
& ((current_time - trade.open_date_utc).days >= 1): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)):
|
|
# print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate)
|
|
return 'short_lost'
|
|
|
|
# if (current_profit > 0) \
|
|
# & (last_candle['rsi'] > 82) & (previous_last_candle['rsi'] > 75): #| (previous_last_candle['rsi'] > 75 & last_candle['rsi'] < 70)):
|
|
# # print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate)
|
|
# return 'over_rsi_2'
|
|
|
|
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
|
|
|
dataframe['profit'] = 0
|
|
# RSI
|
|
dataframe['rsi'] = ta.RSI(dataframe)
|
|
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["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["percent50"] = dataframe["percent"].rolling(50).sum()
|
|
|
|
# if (dataframe["percent50"] < -0.03) & (dataframe['sma10'] > dataframe['sma10'].shift(2)):
|
|
# dataframe["percent_ok"] = new dataframe()
|
|
# else:
|
|
# dataframe["percent_ok"] = 0
|
|
|
|
dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200)
|
|
dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20)
|
|
dataframe['min50'] = ta.MIN(dataframe['close'], timeperiod=50)
|
|
dataframe["volume10"] = dataframe["volume"].rolling(10).mean()
|
|
|
|
dataframe['max'] = ta.MAX(dataframe['close'], timeperiod=200)
|
|
dataframe['max20'] = ta.MAX(dataframe['close'], timeperiod=20)
|
|
dataframe['max50'] = ta.MAX(dataframe['close'], timeperiod=50)
|
|
|
|
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_lower_pente"] = 100 * (dataframe["bb_lowerband"] - dataframe["bb_lowerband"].shift(1)) / dataframe["bb_lowerband"]
|
|
dataframe["sma10_pente"] = 100 * (dataframe["sma10"] - dataframe["sma10"].shift(1)) / dataframe["sma10"]
|
|
dataframe["sma20_pente"] = 100 * (dataframe["sma20"] - dataframe["sma20"].shift(1)) / dataframe["sma20"]
|
|
|
|
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"]
|
|
)
|
|
|
|
dataframe[buy_crossed_indicator0] = gene_calculator(dataframe, buy_crossed_indicator0)
|
|
dataframe[buy_crossed_indicator1] = gene_calculator(dataframe, buy_crossed_indicator1)
|
|
dataframe[buy_crossed_indicator2] = gene_calculator(dataframe, buy_crossed_indicator2)
|
|
|
|
dataframe[buy_indicator0] = gene_calculator(dataframe, buy_indicator0)
|
|
dataframe[buy_indicator1] = gene_calculator(dataframe, buy_indicator1)
|
|
dataframe[buy_indicator2] = gene_calculator(dataframe, buy_indicator2)
|
|
|
|
dataframe["HT_PHASOR-0-100"] = gene_calculator(dataframe, "HT_PHASOR-0-100")
|
|
dataframe["CDLIDENTICAL3CROWS-100"] = gene_calculator(dataframe, "CDLIDENTICAL3CROWS-100")
|
|
dataframe["DEMA-10"] = gene_calculator(dataframe, "DEMA-10")
|
|
dataframe["CDLMORNINGDOJISTAR-20"] = gene_calculator(dataframe, "CDLMORNINGDOJISTAR-20")
|
|
|
|
dataframe["cond1"] = dataframe[buy_indicator0].div(dataframe[buy_crossed_indicator0])
|
|
# dataframe["q_0.1"] = dataframe.rolling(50).quantile(.1)['close']
|
|
# dataframe["q_0.25"] = dataframe.rolling(50).quantile(.25)['close']
|
|
# dataframe["q_0.33"] = dataframe.rolling(50).quantile(.33)['close']
|
|
|
|
dataframe["MACD-2-10"] = gene_calculator(dataframe, "MACD-2-10")
|
|
dataframe["CDLIDENTICAL3CROWS-10"] = gene_calculator(dataframe, "CDLIDENTICAL3CROWS-10")
|
|
dataframe["PPO-10"] = gene_calculator(dataframe, "PPO-10")
|
|
dataframe["MINUS_DM-10"] = gene_calculator(dataframe, "MINUS_DM-10")
|
|
dataframe["MOM-10"] = gene_calculator(dataframe, "MOM-10")
|
|
dataframe["MACDEXT-0-100"] = gene_calculator(dataframe, "MACDEXT-0-100")
|
|
|
|
# # EMA - Exponential Moving Average
|
|
# dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3)
|
|
|
|
percent_lower = False
|
|
current_price = dataframe['close'].iloc[-1]
|
|
|
|
dataframe['should_sell'] = False
|
|
dataframe['should_buy'] = False
|
|
|
|
# Get the previous trade
|
|
# open_trades = Trade.get_trades_proxy(is_open=True)
|
|
# print(open_trades)
|
|
|
|
trade = Trade.get_trades_proxy(is_open=False, pair=metadata['pair'])
|
|
if trade:
|
|
trade = trade[-1]
|
|
lsp = trade.close_rate
|
|
if lsp:
|
|
percent_lower = self.calc_percentage_lower(price=lsp, current=current_price)
|
|
# Found a bug? When force selling it doesnt close it
|
|
else:
|
|
lsp = trade.open_rate
|
|
if lsp:
|
|
percent_lower = self.calc_percentage_lower(price=lsp, current=current_price)
|
|
else:
|
|
lsp = 0.00
|
|
|
|
# Get the current Trade
|
|
trade = Trade.get_trades_proxy(is_open=True, pair=metadata['pair'])
|
|
if trade:
|
|
trade = trade[-1]
|
|
lbp = trade.open_rate
|
|
open_trade = True
|
|
profit = self.calc_profit(price=lbp, current=current_price)
|
|
profit_percent = (profit/lbp)*100
|
|
else:
|
|
lbp = 0.00
|
|
open_trade = False
|
|
profit = False
|
|
profit_percent = False
|
|
# print("------------")
|
|
# print("Last Sold For:", lsp)
|
|
|
|
# if open_trade:
|
|
# print("Bought for: ", lbp)
|
|
# print("Current Price: ", current_price)
|
|
# if profit:
|
|
# print("Current Profit: ", profit, " ", float(f"{profit_percent:.8f}"), "%")
|
|
# if percent_lower and not open_trade:
|
|
# print("Percent Lower: ", float(f"{percent_lower:.8f}"), "%")
|
|
|
|
# Should we Sell?
|
|
if profit_percent:
|
|
if profit_percent > 1:
|
|
dataframe['should_sell'] = True
|
|
|
|
# Should we buy?
|
|
if not open_trade:
|
|
if (lsp == 0.00) & (lbp == 0.00):
|
|
dataframe['should_buy'] = True
|
|
# Is the percentage of what we sold for and the current price 2% lower
|
|
if percent_lower > 2:
|
|
dataframe['should_buy'] = True
|
|
|
|
dataframe['last_sell_price'] = lsp
|
|
dataframe['last_buy_price'] = lbp
|
|
dataframe['current_price'] = current_price
|
|
dataframe['profit_percent'] = profit_percent
|
|
# print("Current Dataframe:")
|
|
# print(dataframe.tail(1))
|
|
|
|
# ## Base Timeframe / Pair
|
|
#
|
|
# # Kaufmann Adaptive Moving Average
|
|
# dataframe['kama'] = ta.KAMA(dataframe, length=233)
|
|
#
|
|
# # RMI: https://www.tradingview.com/script/kwIt9OgQ-Relative-Momentum-Index/
|
|
# dataframe['rmi'] = csa.RMI(dataframe, length=24, mom=5)
|
|
#
|
|
# # Momentum Pinball: https://www.tradingview.com/script/fBpVB1ez-Momentum-Pinball-Indicator/
|
|
# dataframe['roc-mp'] = ta.ROC(dataframe, timeperiod=1)
|
|
# dataframe['mp'] = ta.RSI(dataframe['roc-mp'], timeperiod=3)
|
|
#
|
|
# # MA Streak: https://www.tradingview.com/script/Yq1z7cIv-MA-Streak-Can-Show-When-a-Run-Is-Getting-Long-in-the-Tooth/
|
|
# dataframe['mastreak'] = csa.mastreak(dataframe, period=4)
|
|
#
|
|
# # Percent Change Channel: https://www.tradingview.com/script/6wwAWXA1-MA-Streak-Change-Channel/
|
|
# upper, mid, lower = csa.pcc(dataframe, period=40, mult=3)
|
|
# dataframe['pcc-lowerband'] = lower
|
|
# dataframe['pcc-upperband'] = upper
|
|
#
|
|
# lookup_idxs = dataframe.index.values - (abs(dataframe['mastreak'].values) + 1)
|
|
# valid_lookups = lookup_idxs >= 0
|
|
# dataframe['sbc'] = np.nan
|
|
# dataframe.loc[valid_lookups, 'sbc'] = dataframe['close'].to_numpy()[lookup_idxs[valid_lookups].astype(int)]
|
|
#
|
|
# dataframe['streak-roc'] = 100 * (dataframe['close'] - dataframe['sbc']) / dataframe['sbc']
|
|
#
|
|
# # Trends, Peaks and Crosses
|
|
# dataframe['candle-up'] = np.where(dataframe['close'] >= dataframe['open'], 1, 0)
|
|
# dataframe['candle-up-trend'] = np.where(dataframe['candle-up'].rolling(5).sum() >= 3, 1, 0)
|
|
#
|
|
# dataframe['rmi-up'] = np.where(dataframe['rmi'] >= dataframe['rmi'].shift(), 1, 0)
|
|
# dataframe['rmi-up-trend'] = np.where(dataframe['rmi-up'].rolling(5).sum() >= 3, 1, 0)
|
|
#
|
|
# dataframe['rmi-dn'] = np.where(dataframe['rmi'] <= dataframe['rmi'].shift(), 1, 0)
|
|
# dataframe['rmi-dn-count'] = dataframe['rmi-dn'].rolling(8).sum()
|
|
#
|
|
# dataframe['streak-bo'] = np.where(dataframe['streak-roc'] < dataframe['pcc-lowerband'], 1, 0)
|
|
# dataframe['streak-bo-count'] = dataframe['streak-bo'].rolling(8).sum()
|
|
#
|
|
# # Indicators used only for ROI and Custom Stoploss
|
|
# ssldown, sslup = csa.SSLChannels_ATR(dataframe, length=21)
|
|
# dataframe['sroc'] = csa.SROC(dataframe, roclen=21, emalen=13, smooth=21)
|
|
# dataframe['ssl-dir'] = np.where(sslup > ssldown, 'up', 'down')
|
|
#
|
|
# Base pair informative timeframe indicators
|
|
# informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe=self.inf_timeframe)
|
|
#
|
|
# # Get the "average day range" between the 1d high and 1d low to set up guards
|
|
# informative['1d-high'] = informative['close'].rolling(24).max()
|
|
# informative['1d-low'] = informative['close'].rolling(24).min()
|
|
# informative['adr'] = informative['1d-high'] - informative['1d-low']
|
|
#
|
|
# # Base pair informative timeframe indicators
|
|
# informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe=self.inf_timeframe)
|
|
#
|
|
# # Get the "average day range" between the 1d high and 1d low to set up guards
|
|
# informative['1d-high'] = informative['close'].rolling(24).max()
|
|
# informative['1d-low'] = informative['close'].rolling(24).min()
|
|
# informative['adr'] = informative['1d-high'] - informative['1d-low']
|
|
#
|
|
# dataframe = merge_informative_pair(dataframe, informative, self.timeframe, self.inf_timeframe, ffill=True)
|
|
# #
|
|
# # Other stake specific informative indicators
|
|
# # e.g if stake is BTC and current coin is XLM (pair: XLM/BTC)
|
|
# if self.config['stake_currency'] in ('BTC', 'ETH'):
|
|
# coin, stake = metadata['pair'].split('/')
|
|
# fiat = self.custom_fiat
|
|
# coin_fiat = f"{coin}/{fiat}"
|
|
# stake_fiat = f"{stake}/{fiat}"
|
|
#
|
|
# # Informative COIN/FIAT e.g. XLM/USD - Base Timeframe
|
|
# coin_fiat_tf = self.dp.get_pair_dataframe(pair=coin_fiat, timeframe=self.timeframe)
|
|
# dataframe[f"{fiat}_rmi"] = csa.RMI(coin_fiat_tf, length=55, mom=5)
|
|
#
|
|
# # Informative STAKE/FIAT e.g. BTC/USD - Base Timeframe
|
|
# stake_fiat_tf = self.dp.get_pair_dataframe(pair=stake_fiat, timeframe=self.timeframe)
|
|
# dataframe[f"{stake}_rmi"] = csa.RMI(stake_fiat_tf, length=55, mom=5)
|
|
#
|
|
# # Informatives for BTC/STAKE if not in whitelist
|
|
# else:
|
|
# pairs = self.dp.current_whitelist()
|
|
# btc_stake = f"BTC/{self.config['stake_currency']}"
|
|
# if not btc_stake in pairs:
|
|
# self.custom_btc_inf = True
|
|
# # BTC/STAKE - Base Timeframe
|
|
# btc_stake_tf = self.dp.get_pair_dataframe(pair=btc_stake, timeframe=self.timeframe)
|
|
# dataframe['BTC_rmi'] = csa.RMI(btc_stake_tf, length=55, mom=5)
|
|
# dataframe['BTC_close'] = btc_stake_tf['close']
|
|
# dataframe['BTC_kama'] = ta.KAMA(btc_stake_tf, length=144)
|
|
|
|
return dataframe
|
|
|
|
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
|
|
|
conditions = list()
|
|
conditions2 = list()
|
|
condition1, dataframe = condition_generator(
|
|
dataframe,
|
|
buy_operator0,
|
|
buy_indicator0,
|
|
buy_crossed_indicator0,
|
|
0.7 #self.buy_real_num0.value
|
|
)
|
|
conditions.append(condition1)
|
|
|
|
dataframe.loc[
|
|
(
|
|
reduce(lambda x, y: x & y, conditions)
|
|
& (dataframe['volume10'] * dataframe['close'] / 1000 >= 10)
|
|
& (dataframe['close'] < dataframe['sma10'])
|
|
& (dataframe['open'] < dataframe['sma10'])
|
|
& (dataframe['low'] < dataframe['min20'])
|
|
& (dataframe['percent'] > 0)
|
|
# & (dataframe['max'] < dataframe['close'] * self.buy_signal_condition_high.value)
|
|
), ['buy', 'buy_tag']] = (1, 'buy_signal_condition')
|
|
|
|
####################################################################
|
|
|
|
condition2, dataframe = condition_generator(dataframe, "/=R", "CMO-10", "HT_DCPERIOD-20", 0.37)
|
|
conditions2.append(condition2)
|
|
|
|
dataframe.loc[
|
|
(reduce(lambda x, y: x & y, conditions2)
|
|
# & (dataframe['volume10'] * dataframe['close'] / 1000 >= 10)
|
|
& (dataframe['close'].shift(9) < dataframe['bb_lowerband'].shift(9))
|
|
# & (dataframe['max'] < dataframe['close'] * self.buy_signal_bb_width.value)
|
|
), ['buy', 'buy_tag']] = (1, 'buy_bb_width_1')
|
|
|
|
####################################################################
|
|
conditions = list()
|
|
|
|
condition, dataframe = condition_generator(dataframe, "/<R", "HT_PHASOR-0-100", "CDLIDENTICAL3CROWS-100", 0.91)
|
|
conditions.append(condition)
|
|
|
|
condition, dataframe = condition_generator(dataframe, "<R", "DEMA-10", "CDLMORNINGDOJISTAR-20", 0.55)
|
|
conditions.append(condition)
|
|
|
|
if conditions:
|
|
dataframe.loc[
|
|
(reduce(lambda x, y: x & y, conditions)
|
|
& (dataframe['volume10'] * dataframe['close'] / 1000 >= 10)
|
|
& (dataframe['sma10_pente'] > self.buy_signal_sma10_pente.value)
|
|
& (dataframe['sma10'].shift(1) < dataframe['sma10'] * self.buy_signal_sma10.value)
|
|
& (dataframe['sma20'].shift(1) < dataframe['sma20'])
|
|
& (dataframe['sma50'].shift(1) < dataframe['sma50'])
|
|
& (dataframe['close'] < dataframe['bb_upperband'])
|
|
& (dataframe['open'] < dataframe['sma10'])
|
|
& (dataframe['percent50'] < self.buy_signal_sma_percent50.value)
|
|
# & (dataframe['max'] < dataframe['close'] * self.buy_signal_sma_max.value)
|
|
# & (dataframe['close'] < dataframe['min'] * self.buy_signal_sma_min.value)
|
|
# & (dataframe['close'] > dataframe['1d-low_1h'] * self.buy_signal_sma_min.value)
|
|
# & (dataframe['close'] < dataframe['1d-high_1h'] * self.buy_signal_sma_max.value)
|
|
),
|
|
['buy', 'buy_tag']] = (1, 'buy_sma')
|
|
# print(len(dataframe.keys()))
|
|
|
|
####################################################################
|
|
# conditions3 = list()
|
|
#
|
|
# condition3, dataframe = condition_generator(dataframe, "D", "MACD-2-10", "CDLIDENTICAL3CROWS-10", 0.88)
|
|
# conditions3.append(condition3)
|
|
#
|
|
# condition3, dataframe = condition_generator(dataframe, "/<R", "PPO-10", "MINUS_DM-10", 0.27)
|
|
# conditions3.append(condition3)
|
|
#
|
|
# condition3, dataframe = condition_generator(dataframe, ">", "MOM-10", "MACDEXT-0-100", 0.87)
|
|
# #conditions3.append(condition3)
|
|
#
|
|
# if conditions3:
|
|
# dataframe.loc[
|
|
# (reduce(lambda x, y: x & y, conditions3)
|
|
# # & (dataframe['volume10'] * dataframe['close'] / 1000 >= 10)
|
|
# & (dataframe['close'].shift(7) < dataframe['bb_lowerband'].shift(7))
|
|
# & (dataframe['max'] < dataframe['close'] * self.buy_signal_minus.value)
|
|
# ),
|
|
# ['buy', 'buy_tag']] = (1, 'buy_minus_dm')
|
|
# print(len(dataframe.keys()))
|
|
|
|
# ###################################################################
|
|
#
|
|
# dataframe.loc[
|
|
# (dataframe['close'] > dataframe['bb_upperband'])
|
|
# & (dataframe['percent'] > 0.01)
|
|
# & (dataframe['percent20'] < 0.035)
|
|
# & (dataframe['cond1'] > 13)
|
|
# & (dataframe['bb_width'] > 0.02)
|
|
# & (dataframe['cond1'].shift(4) < 8)
|
|
# & (dataframe['bb_width'].shift(4) < 0.012)
|
|
# , ['buy', 'buy_tag']] = (1, 'buy_cond1')
|
|
#
|
|
# ####################################################################
|
|
#
|
|
# dataframe.loc[
|
|
# (dataframe['bb_lower_pente'].shift(4) < -0.50)
|
|
# & (dataframe['sma10_pente'] > -0.2)
|
|
# & (dataframe['sma10_pente'].shift(4) < -0.4)
|
|
# & (dataframe['percent5'] < 0.01)
|
|
# , ['buy', 'buy_tag']] = (1, 'buy_lower_pente')
|
|
#
|
|
# ####################################################################
|
|
#
|
|
# dataframe.loc[
|
|
# (dataframe['percent50'].shift(4) < -0.05)
|
|
# & (dataframe['sma10_pente'].shift(1) < 0)
|
|
# & (dataframe['sma10_pente'] >= 0)
|
|
# & (dataframe['open'] < dataframe['sma10'])
|
|
# , ['buy', 'buy_tag']] = (1, 'buy_sma50')
|
|
#
|
|
# pandas.set_option('display.max_rows', dataframe.shape[0] + 1)
|
|
# pandas.set_option('display.max_columns', 30)
|
|
# print(condition1)
|
|
# print(dataframe["q_0.1"])
|
|
|
|
return dataframe
|
|
|
|
def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
|
# conditions = list()
|
|
#
|
|
# condition1, dataframe = condition_generator(
|
|
# dataframe,
|
|
# self.sell_operator0.value,
|
|
# self.sell_indicator0.value,
|
|
# self.sell_crossed_indicator0.value,
|
|
# self.sell_real_num0.value
|
|
# )
|
|
# conditions.append(condition1)
|
|
#
|
|
# # print(self.sell_percent.value, ' ',
|
|
# # self.sell_percent.value + self.sell_percent3.value, ' ',
|
|
# # self.sell_percent.value + self.sell_percent3.value + self.sell_percent5.value)
|
|
# dataframe.loc[
|
|
# (
|
|
# (reduce(lambda x, y: x & y, conditions)) &
|
|
# (dataframe['percent'] < - self.sell_percent.value) &
|
|
# (dataframe['percent3'] < - (self.sell_percent3.value + self.sell_percent.value)) &
|
|
# (dataframe['percent5'] < - (self.sell_percent5.value + self.sell_percent3.value + self.sell_percent.value)) &
|
|
# (dataframe['close'] < dataframe['open'])
|
|
# # (((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
|
|
|