# Example Freqtrade strategy that reads Terminal alerts into informational columns. # Entry and exit conditions are yours. This file does not place an order. # # Freqtrade docs, read 2026-10-13: # https://www.freqtrade.io/en/stable/strategy-callbacks/ # https://www.freqtrade.io/en/stable/strategy-customization/ # bot_start runs once after the strategy loads. bot_loop_start runs once per loop. # populate_indicators fills the candle table. See README.md. import logging logger = logging.getLogger(__name__) try: from freqtrade.strategy import IStrategy except ImportError: class IStrategy: """Stand-in so this file imports where Freqtrade is not installed.""" INTERFACE_VERSION = 3 def _load_feed(): try: import terminal_feed return terminal_feed except ImportError: import importlib.util import sys from pathlib import Path path = Path(__file__).resolve().parent / "terminal_feed.py" spec = importlib.util.spec_from_file_location("terminal_feed", path) if spec is None or spec.loader is None: raise ImportError("terminal_feed.py is not next to example_strategy.py") module = importlib.util.module_from_spec(spec) sys.modules["terminal_feed"] = module spec.loader.exec_module(module) return module _feed = _load_feed() TerminalFeed = _feed.TerminalFeed shared_feed = _feed.shared_feed symbol_from_pair = _feed.symbol_from_pair def _live_mode(config): if not isinstance(config, dict): return False runmode = config.get("runmode") value = getattr(runmode, "value", runmode) return value in ("live", "dry_run") class TerminalAlertsStrategy(IStrategy): """Show recent Terminal alerts on the latest candle. You write the conditions.""" INTERFACE_VERSION = 3 # Freqtrade requires these three before it will load a strategy. Terminal does not choose # them for you: an empty minimal_roi and a stoploss of -1.0 (a 100% stop) do not act. # Replace each one with your own. timeframe = "5m" minimal_roi = {} stoploss = -1.0 can_short = False process_only_new_candles = True use_exit_signal = True # How many minutes of alerts to show. Change this number if you want a different window. terminal_recent_minutes = 60 custom_terminal_feed = None def bot_start(self, **kwargs): """Start the feed once, in live and dry-run only. A backtest does not call the API. After /reload_config the new strategy reuses the running feed. Its lines go to Freqtrade's log.""" if self.custom_terminal_feed is not None: return None if not _live_mode(getattr(self, "config", None)): return None self.custom_terminal_feed = shared_feed(log=logger.info) return None def bot_loop_start(self, current_time=None, **kwargs): """Freqtrade calls this once per loop. The feed thread is already reading.""" return None def populate_indicators(self, dataframe, metadata): """Copy the current alerts onto the latest row. Older rows stay at 0.""" pair = "" if isinstance(metadata, dict): pair = metadata.get("pair") or "" symbol = symbol_from_pair(pair) events = [] feed = self.custom_terminal_feed if feed is not None and symbol: events = feed.recent(symbol, self.terminal_recent_minutes) count = len(events) headline = "" if events: text = events[-1].get("headline") if isinstance(text, str): headline = text dataframe["terminal_alert_count"] = 0 dataframe["terminal_alert_headline"] = "" if len(dataframe.index) > 0: last = dataframe.index[-1] dataframe.loc[last, "terminal_alert_count"] = count dataframe.loc[last, "terminal_alert_headline"] = headline return dataframe def populate_entry_trend(self, dataframe, metadata): dataframe["enter_long"] = 0 # your own conditions here return dataframe def populate_exit_trend(self, dataframe, metadata): dataframe["exit_long"] = 0 # your own conditions here return dataframe