Timeframe
N/A
Direction
Long Only
Stoploss
N/A
Trailing Stop
No
ROI
N/A
Interface Version
N/A
Startup Candles
N/A
Indicators
1
iterativv/NostalgiaForInfinity
import itertools
import logging
from datetime import datetime, timedelta
from typing import Callable, Dict, Iterable, List, Optional, Tuple, Union
import numpy as np
import talib.abstract as ta
from freqtrade.constants import Config
from freqtrade.exchange import timeframe_to_prev_date
from freqtrade.strategy import BooleanParameter, CategoricalParameter, IStrategy
from pandas import DataFrame, Series, concat
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
# Simply ignore matrix_profile dependency missing warning
from tsfresh import extract_features
from tsfresh.feature_extraction import MinimalFCParameters
logger = logging.getLogger(__name__)
class DisableLogger:
def __enter__(self):
logging.disable(logging.CRITICAL)
def __exit__(self, exit_type, exit_value, exit_traceback):
logging.disable(logging.NOTSET)
class RotatorTSFreshStrategy(IStrategy):
INTERFACE_VERSION: int = 3
timeframe: str = "1d"
can_short: bool = False
process_only_new_candles: bool = True
use_exit_signal: bool = True
ignore_buying_expired_candle_after: int = 600
startup_candle_count: int = 100
minimal_roi: Dict[str, float] = {}
stoploss: float = -1.0
max_open_trades: int = 5
roc_period = CategoricalParameter(range(5, 50, 2), default=29, space="buy")
pair_threshold = CategoricalParameter(range(2, 20, 2), default=6, space="buy")
cooldown_lookback = CategoricalParameter(
range(2, 48, 2), default=10, space="protection"
)
stop_duration = CategoricalParameter(
range(2, 200, 2), default=100, space="protection"
)
use_stop_protection = BooleanParameter(default=True, space="protection")
top_pairs: List = []
def __init__(self, config: Config) -> None:
super().__init__(config)
self.config = config
@property
def protections(self):
prot = []
prot.append(
{
"method": "CooldownPeriod",
"stop_duration_candles": self.cooldown_lookback.value,
}
)
if self.use_stop_protection.value:
prot.append(
{
"method": "StoplossGuard",
"lookback_period_candles": 24 * 3,
"trade_limit": 4,
"stop_duration_candles": self.stop_duration.value,
"only_per_pair": False,
}
)
return prot
def populate_indicators(self, dataframe: DataFrame, metadata: Dict) -> DataFrame:
dataframe["roc"] = ta.ROC(dataframe, timeperiod=self.roc_period.value)
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: Dict) -> DataFrame:
dataframe[["enter_long", "enter_tag"]] = (1, "always_enter")
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: Dict) -> DataFrame:
return dataframe
def bot_loop_start(self, current_time: datetime, **kwargs) -> None:
prev_candle_time = timeframe_to_prev_date(self.timeframe, current_time)
if (current_time - prev_candle_time) >= timedelta(minutes=5):
return
pairs = self.config["exchange"]["pair_whitelist"]
data, roc = {}, {}
for pair in pairs:
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
if dataframe.empty:
continue
df = dataframe[["date", "close", "roc"]].iloc[-50:]
df["pair"] = pair
data[pair] = df
roc[pair] = dataframe["roc"].iat[-1]
if not data:
return
# CLUSTERING
df = concat(data, axis=0).dropna()
with DisableLogger():
features = extract_features(
df,
column_id="pair",
column_sort="date",
column_kind=None,
column_value=None,
n_jobs=0, # disable parallelisation
disable_progressbar=True,
show_warnings=False,
default_fc_parameters=MinimalFCParameters(),
)
scaler = StandardScaler()
X = scaler.fit_transform(features)
n = self.pair_threshold.value
km = KMeans(n_clusters=n, random_state=0)
clusters = km.fit_predict(X)
top_pairs = []
for key, group in itertools.groupby(
sorted(zip(clusters, pairs)), lambda x: x[0]
):
group = list(group)
if not group:
continue
group_pairs = sorted(
[(x[1], roc.get(x[1], 0.0)) for x in group],
key=lambda x: x[1],
reverse=True,
)
top_pairs.append(group_pairs[0][0])
self.top_pairs = top_pairs
def confirm_trade_entry(self, pair: str, *args, **kwargs) -> bool:
if pair not in self.top_pairs:
return False
return True
def custom_exit(self, pair: str, *args, **kwargs):
if pair not in self.top_pairs:
return "exit_trade"