Files
Freqtrade/GodStraJD3_1.py
Jérôme Delacotte 7c239227d8 first commit
2025-03-06 11:01:43 +01:00

1034 lines
42 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 datetime
from freqtrade import data
from freqtrade.strategy.parameters import CategoricalParameter, DecimalParameter
from numpy.lib import math
from freqtrade.strategy.interface import IStrategy
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]
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 = 1
########################### 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:
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_1(IStrategy):
# #################### RESULTS PASTE PLACE ####################
# ROI table:
minimal_roi = {
"0": 1,
"600": 0.166,
"1200": 0.14,
"2400": 0.1,
"3600": 0.05,
"7289": 0
}
# Stoploss:
stoploss = -1
# Buy hypers
timeframe = '4h'
# Trailing stoploss
trailing_stop = True
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'},
},
# "ADX": {
# 'adx': {'color': 'white'},
# 'minus_dm': {'color': 'blue'},
# 'plus_dm': {'color': 'red'}
# },
"Rsi": {
'rsi': {'color': 'pink'},
},
"rolling": {
'bb_rolling': {'color': '#87e470'},
"bb_rolling_min": {'color': '#ac3e2a'}
},
"percent": {
"percent": {'color': 'green'},
"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')
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')
# def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
# proposed_stake: float, min_stake: float, max_stake: float,
# **kwargs) -> float:
#
# dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe)
# current_candle = dataframe.iloc[-1].squeeze()
#
# # print("proposed_stake=", proposed_stake, " max_stake=", max_stake)
# if current_candle['bb_width'] > 0.065:
# # print("use more stake", pair, " ", proposed_stake * 2)
# return min(max_stake, proposed_stake * 2)
#
# if current_candle['bb_width'] > 0.045:
# # print("use more stake", pair, " ", proposed_stake * 1.5)
# return min(max_stake, proposed_stake * 1.5)
#
# # if current_candle['bb_width'] < 0.020:
# # print("use less stake", pair, " ", proposed_stake / 2)
# # return min(max_stake, proposed_stake / 2)
# # if self.config['stake_amount'] == 'unlimited':
# # # Use entire available wallet during favorable conditions when in compounding mode.
# # return max_stake
# # else:
# # # Compound profits during favorable conditions instead of using a static stake.
# # return self.wallets.get_total_stake_amount() / self.config['max_open_trades']
#
# # Use default stake amount.
# return proposed_stake
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()
# (last_candle['percent5'] < -0.005) \
# if (0 < current_profit < 0.005) \
# & ((current_time - trade.open_date_utc).seconds >= 3600 * 2):
# # & (previous_last_candle['sma10'] > last_candle['sma10']):
# print("too_small_gain", pair, trade, " profit=", current_profit, " rate=", current_rate, " percent5=",
# last_candle['percent5'])
# return 'too_small_gain'
# if (current_profit < -0.05) \
# & ((current_time - trade.open_date_utc).days >= 3):
# print("lost_half_profit", pair, trade, " profit=", current_profit, " rate=", current_rate)
# return 'stop_loss_profit'
# if (current_profit > 0.02) \
# & (last_candle['percent'] < 0.01) \
# & ((current_time - trade.open_date_utc).seconds >= 3600):
# print("lost_half_profit", pair, trade, " profit=", current_profit, " rate=", current_rate)
# return 'lost_half_profit'
if (current_profit > 0) \
& ((current_time - trade.open_date_utc).seconds >= 3600 * 2) \
& (previous_5_candle['sma20'] > last_candle['sma20']) \
& (last_candle['percent'] < 0) \
& (last_candle['percent5'] < 0):
# print("over_bb_band_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate)
return 'over_bb_band_sma20_desc'
if (current_profit > 0) \
& (last_candle['rsi'] > 75):
# print("over_rsi", pair, trade, " profit=", current_profit, " rate=", current_rate)
return 'over_rsi'
# description trade
# Trade(id=0, pair=CAKE/USDT, amount=4.19815281, open_rate=11.91000000, open_since=2021-12-22 17:55:00)
# print(last_candle)
if 0.015 < current_profit < 1:
if (
(previous_last_candle['sma10'] > last_candle['sma10'] * 1.005) &
(current_time - trade.open_date_utc).seconds >= 3600 * 3
# ) | (
# (current_time - trade.open_date_utc).seconds >= 3600 * 6
):
# self.lock_pair(pair, until=current_time + timedelta(hours=3))
# print("profit_3h_sma10_desc", pair, trade, " profit=", current_profit, " rate=", current_rate)
return 'profit_3h_sma10_desc'
if (0 < current_profit < 0.1) \
& (previous_last_candle['sma20'] > last_candle['sma20']) \
& ((current_time - trade.open_date_utc).seconds >= 3600 * 5):
# print("profit_5h_sma20_desc", pair, trade, " profit=", current_profit, " rate=", current_rate)
return 'profit_5h_sma20_desc'
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['minus_dm'] = ta.MINUS_DM(dataframe)
# 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)
# 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["percent20"] = dataframe["percent"].rolling(20).sum()
dataframe['min'] = ta.MIN(dataframe['close'], timeperiod=200)
dataframe['min20'] = ta.MIN(dataframe['close'], timeperiod=20)
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'] < dataframe['bb_lowerband'])
& (dataframe['bb_width'] >= 0.12)
& (dataframe['volume'] * dataframe['close'] / 1000 > 100)
) |
reduce(lambda x, y: x & y, conditions),
'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
if conditions:
dataframe.loc[
(
reduce(lambda x, y: x & y, conditions)
),
'sell']=1
return dataframe
class StrategyHelperLocal:
"""
simple helper class to predefine a couple of patterns for our
strategy
"""
@staticmethod
def two_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))
)
@staticmethod
def no_more_three_green_candles(dataframe):
"""
evaluates if we are having not more than 3 green candles in a row
:param self:
:param dataframe:
:return:
"""
return not (
(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))
)
@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 two_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(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_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 two_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))
)
@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))
)