Timeframe
4h
Direction
Long Only
Stoploss
-20.0%
Trailing Stop
No
ROI
0m: 15.0%, 10080m: 0.0%
Interface Version
3
Startup Candles
N/A
Indicators
10
freqtrade/freqtrade-strategies
Strategy 003 author@: Gerald Lonlas github@: https://github.com/freqtrade/freqtrade-strategies
import numpy as np
import pandas as pd
import talib.abstract as ta
import pandas_ta as pta
from functools import reduce
from pandas import DataFrame, Series
from datetime import datetime
from typing import Any, Dict, List
from freqtrade.constants import Config
from freqtrade.strategy import BooleanParameter, CategoricalParameter, IStrategy
from freqtrade.optimize.space import Categorical, SKDecimal, Dimension
class Seeker(IStrategy):
INTERFACE_VERSION = 3
process_only_new_candles = True
can_short = False
stoploss = -0.20
minimal_roi = {"0": 0.15, "10080": 0}
max_open_trades = 10
trailing_stop = False
trailing_stop_positive = 0.02
trailing_stop_positive_offset = 0.03
trailing_only_offset_is_reached = True
use_custom_stoploss = False
use_exit_signal = True
exit_profit_only = False
ignore_roi_if_entry_signal = False
timeframe = "4h"
NUM_RULES = 2
SIDES = ["buy", "sell"]
class HyperOpt:
def stoploss_space() -> List[Dimension]:
return [SKDecimal(-0.20, -0.05, decimals=2, name="stoploss")]
def roi_space() -> List[Dimension]:
return [SKDecimal(0.05, 0.30, decimals=2, name="roi")]
def generate_roi_table(p: Dict) -> Dict[int, float]:
return {0: p["roi"], 10080: 0}
def max_open_trades_space() -> List[Dimension]:
return [Categorical(range(5, 10, 1), name="max_open_trades")]
def __init__(self, config: Config):
super().__init__(config)
self.config = config
self.initialize_parameters()
def compare_series(
self,
series1: Series,
series2: Series,
shift: int = 0,
comparison: str = "CROSS_UP",
) -> bool:
value1 = series1.shift(shift + 1)
value2 = series2.shift(shift + 1)
value3 = series1.shift(shift)
value4 = series2.shift(shift)
if comparison == "CROSS_UP":
return (value1 < value2) & (value3 > value4)
elif comparison == "CROSS_DOWN":
return (value1 > value2) & (value3 < value4)
elif comparison == "GREATER_THAN":
return series1.shift(shift) > series2.shift(shift)
elif comparison == "LESSER_THAN":
return series1.shift(shift) < series2.shift(shift)
elif comparison == "RAISING":
return value1 < value3
elif comparison == "FALLING":
return value1 > value3
return False
def initialize_parameters(self):
SERIES = """upperband lowerband macd macdsignal slowk slowd fastk fastd
adx atr cci mom roc rsi supertrend
close volume
""".split()
OPERATORS = [
"DISABLED",
"CROSS_UP",
"CROSS_DOWN",
"GREATER_THAN",
"LESSER_THAN",
"RAISING",
"FALLING",
]
SHIFT_RANGE = range(0, 20, 2)
SMOOTH_RANGE = range(5, 100, 5)
# HOUR_RANGE = range(0, 24)
setattr(
self, "smooth", CategoricalParameter(SMOOTH_RANGE, default=5, space="buy")
)
# setattr(
# self, "min_hour", CategoricalParameter(HOUR_RANGE, default=0, space="buy")
# )
# setattr(
# self, "max_hour", CategoricalParameter(HOUR_RANGE, default=0, space="buy")
# )
for side in self.SIDES:
for idx in range(self.NUM_RULES):
setattr(
self,
f"{side}_{idx}_series",
CategoricalParameter(SERIES, default="close", space=side),
)
setattr(
self,
f"{side}_{idx}_operator",
CategoricalParameter(OPERATORS, default="DISABLED", space=side),
)
setattr(
self,
f"{side}_{idx}_shift",
CategoricalParameter(SHIFT_RANGE, default=0, space=side),
)
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
multiple_output_indicators = {
"bbands": ("upperband", "lowerband"),
"macd": ("macd", "macdsignal"),
"stoch": ("slowk", "slowd"),
"stochf": ("fastk", "fastd"),
}
single_output_indicators = """adx atr cci mom roc rsi""".split()
smoothed_columns = "close volume supertrend".split()
for indicator, columns in multiple_output_indicators.items():
result = getattr(ta, indicator.upper())(dataframe)
for column in columns:
dataframe[column] = result[column]
smoothed_columns.append(column)
for indicator in single_output_indicators:
column = indicator
dataframe[column] = getattr(ta, indicator.upper())(dataframe)
smoothed_columns.append(column)
# PTA
st = pta.supertrend(dataframe["high"], dataframe["low"], dataframe["close"])
dataframe["supertrend"] = st["SUPERT_7_3.0"]
smoothed = {}
for column in smoothed_columns:
for val in self.smooth.range:
column_name = f"{column}_smooth_{val}"
smoothed[column_name] = ta.EMA(dataframe[column], timeperiod=val)
dataframe = pd.concat([dataframe, pd.DataFrame(smoothed)], axis=1)
return dataframe
def populate_trend(self, dataframe: DataFrame, side: str) -> DataFrame:
conditions = []
for idx in range(self.NUM_RULES):
operator = getattr(self, f"{side}_{idx}_operator").value
indicator = getattr(self, f"{side}_{idx}_series").value
shift = getattr(self, f"{side}_{idx}_shift").value
left = dataframe[f"{indicator}"]
right = dataframe[f"{indicator}_smooth_{self.smooth.value}"]
if operator != "DISABLED":
conditions.append(self.compare_series(left, right, shift, operator))
if conditions:
conditions.append(dataframe["volume"] > 0)
return conditions
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
conditions = self.populate_trend(dataframe, "buy")
if conditions:
# max_hour = self.max_hour.value
# min_hour = self.min_hour.value
# data_hour = dataframe["date"].dt.hour
# if min_hour < max_hour:
# conditions.append((data_hour >= min_hour) & (data_hour < max_hour))
# else:
# conditions.append((data_hour < min_hour) & (data_hour >= max_hour))
dataframe.loc[reduce(lambda x, y: x & y, conditions), "enter_long"] = 1
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
if "sell" in self.SIDES:
conditions = self.populate_trend(dataframe, "sell")
if conditions:
dataframe.loc[reduce(lambda x, y: x & y, conditions), "exit_long"] = 1
return dataframe