# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement # flake8: noqa: F401 # isort: skip_file # --- Do not remove these imports --- import numpy as np import pandas as pd from datetime import datetime, timedelta, timezone from pandas import DataFrame from typing import Optional, Union from freqtrade.strategy import ( IStrategy, Trade, Order, PairLocks, informative, # @informative decorator # Hyperopt Parameters BooleanParameter, CategoricalParameter, DecimalParameter, IntParameter, RealParameter, # timeframe helpers timeframe_to_minutes, timeframe_to_next_date, timeframe_to_prev_date, # Strategy helper functions merge_informative_pair, stoploss_from_absolute, stoploss_from_open, ) # -------------------------------- # Add your lib to import here import talib.abstract as ta from technical import qtpylib class AEGIS_Strategy_Candidate(IStrategy): """ AEGIS Living Strategy (Project Genesis). This strategy is not static. It is autonomously evolved by the AEGIS Evolution Engine. It adapts its logic dyamicallly based on multi-modal feedback loops (Macro, Social, History). It operates without bias towards any specific indicator, mutating its logic to survive. CURRENT GENE EXPRESSION: - Base Logic: Bollinger Bands + RSI (refined for confirmation) - Context: High Volatility, Neutral/Fearful Sentiment. """ # Strategy interface version - allow new iterations of the strategy interface. INTERFACE_VERSION = 3 # Can this strategy go short? can_short: bool = False # Minimal ROI designed for the strategy. # [EVOLUTION NOTE]: Adjust ROI based on Market Volatility. # High volatility suggests the potential for sharper moves, but also greater risk. # The current ROI schedule remains, but aware that quick profit-taking is key. minimal_roi = { "60": 0.01, "30": 0.03, "0": 0.04 } # Optimal stoploss designed for the strategy. # [EVOLUTION NOTE]: Tighten stoploss in Bear Markets (-0.05), loosen in Bull (-0.15). # Adapting for Neutral/Fearful sentiment and High Volatility: # A slightly tighter stoploss to protect against sharp drops in a fearful, volatile market. stoploss = -0.07 # Adjusted stoploss for a cautious stance # Trailing stoploss trailing_stop = False # Optimal timeframe for the strategy. timeframe = "5m" # Run "populate_indicators()" only for new candle. process_only_new_candles = False # These values can be overridden in the config. use_exit_signal = True exit_profit_only = False ignore_roi_if_entry_signal = False # Number of candles the strategy requires before producing valid signals startup_candle_count: int = 30 # Optional order type mapping. order_types = { "entry": "limit", "exit": "limit", "stoploss": "market", "stoploss_on_exchange": False, } # Optional order time in force. order_time_in_force = {"entry": "GTC", "exit": "GTC"} # -------------------------------------------------------------------------- # [GENOME SECTION] - Hyperoptable Parameters # The Evolution Engine should introduce/remove genes here. # -------------------------------------------------------------------------- # Buy RSI default adjusted lower for fearful markets, targeting deeper oversold conditions buy_rsi = IntParameter(low=10, high=40, default=25, space="buy", optimize=True, load=True) # Adjusted default sell_rsi = IntParameter(low=60, high=90, default=70, space="sell", optimize=True, load=True) # Plot configuration plot_config = { "main_plot": { "bb_upperband": {"color": "teal"}, "bb_lowerband": {"color": "teal"}, "bb_middleband": {"color": "orange"}, }, "subplots": { "RSI": { "rsi": {"color": "red"}, }, }, } def informative_pairs(self): """ Define additional pairs/timeframes to monitor. [EVOLUTION NOTE]: Add 'Whale Watcher' pairs here during High Correlation phases. No change needed as per current context. """ return [] def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ Adds several different TA indicators to the given DataFrame [EVOLUTION NOTE]: - Penalty for Complexity: Do not add indicators blindly. - Use TA-Lib abstract (ta.EMA, ta.RSI, etc.) No new indicators added to adhere to complexity penalty. """ # [GENE] RSI dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) # [GENE] 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'] # [GENE] Volume Check (currently commented out, keeping it so) # dataframe['volume_mean'] = dataframe['volume'].rolling(24).mean() return dataframe def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ Based on TA indicators, populates the entry signal for the given dataframe [EVOLUTION NOTE]: Define Entry Logic based on current Market Context (Bull/Bear/Crab). Refined entry for High Volatility & Neutral/Fearful sentiment: - Instead of simply being below the lower Bollinger Band (potential falling knife), wait for the price to cross *above* the lower Bollinger Band, indicating a confirmed bounce. - RSI still confirms an oversold condition, supporting the bounce thesis. """ dataframe.loc[ ( # Signal: Price crosses above Lower Bollinger Band, indicating a potential bounce/reversal (qtpylib.crossed_above(dataframe['close'], dataframe['bb_lowerband'])) & # Signal: RSI still in oversold territory (dataframe['rsi'] < self.buy_rsi.value) & # Guardrail: Volume exists (dataframe['volume'] > 0) ), 'enter_long'] = 1 return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """ Based on TA indicators, populates the exit signal for the given dataframe [EVOLUTION NOTE]: Define Exit Logic (Profit Taking). Corrected exit logic for profit-taking: - RSI in overbought territory. - Price above Upper Bollinger Band, indicating overextension and potential for reversal. """ dataframe.loc[ ( # Signal: RSI Overbought (corrected from '<' to '>') (dataframe['rsi'] > self.sell_rsi.value) & # Signal: Price above Upper Bollinger Band (dataframe['close'] > dataframe['bb_upperband']) & # Guardrail: Volume exists (dataframe['volume'] > 0) ), 'exit_long'] = 1 return dataframe def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, current_time: datetime, entry_tag: Optional[str], side: str, **kwargs) -> bool: """ Called right before entering a trade. [EVOLUTION NOTE]: Use this for "Mental Checks" (News Filters, High Risk Avoidance). No change needed as per current context. """ return True def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float, rate: float, time_in_force: str, sell_reason: str, current_time: datetime, **kwargs) -> bool: """ Called right before exiting a trade. """ return True