Hypothesis: Buy BTC when RSI below 30. Integrates Cipher Sentiment Gate and Risk Manager.
Timeframe
5m
Direction
Long Only
Stoploss
-4.0%
Trailing Stop
No
ROI
0m: 10.0%, 60m: 5.0%, 120m: 2.0%
Interface Version
3
Startup Candles
N/A
Indicators
1
freqtrade/freqtrade-strategies
Strategy 003 author@: Gerald Lonlas github@: https://github.com/freqtrade/freqtrade-strategies
import json
import logging
import sys
from datetime import datetime, timedelta
from pathlib import Path
from freqtrade.persistence import Trade
from freqtrade.strategy import IStrategy
from pandas import DataFrame
# Resolve project root from this file's location (works on any machine)
_BASE = Path(__file__).resolve().parent.parent.parent
if str(_BASE) not in sys.path:
sys.path.insert(0, str(_BASE))
from risk.risk_manager import run_all_checks
from sentiment.reader import get_sentiment_signal
logger = logging.getLogger(__name__)
class Auto202605030340(IStrategy):
"""
Hypothesis: Buy BTC when RSI below 30.
Integrates Cipher Sentiment Gate and Risk Manager.
"""
# Strategy Interface Version
INTERFACE_VERSION = 3
# Timeframe and candle settings
timeframe = "5m"
startup_candle_count: int = 50
# Risk Management Settings
stoploss = -0.04
stoploss_on_exchange = True
# Minimal ROI (Empty as we primarily use stoploss or custom exit)
minimal_roi = {
"0": 0.1, # Exit at 10% profit
"60": 0.05,
"120": 0.02,
}
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Calculate indicators using polars.
"""
from qnt.polars_indicators import add_rsi
from qnt.polars_ohlcv import ohlcv_to_pandas, pandas_to_polars
df_pl = pandas_to_polars(dataframe)
# RSI calculation (Standard 14 period)
df_pl = add_rsi(df_pl, period=14, alias="rsi")
dataframe = ohlcv_to_pandas(df_pl)
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Define entry conditions: RSI < 30.
"""
dataframe.loc[((dataframe["rsi"] < 30) & (dataframe["volume"] > 0)), "enter_long"] = 1
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Define exit conditions: RSI > 70 (Standard counter-signal).
"""
dataframe.loc[(dataframe["rsi"] > 70), "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: str,
side: str,
**kwargs,
) -> bool:
"""
Custom confirmation layer integrating Sentiment and Risk checks.
"""
# --- LAYER 1: RISK CHECKS ---
try:
# 1. Gather balance info
total_balance = self.wallets.get_total_stake_amount()
# 2. Fetch recent trades as list of dicts (Requirement)
# Trade.get_trades_proxy returns trade objects
all_recent_trades = Trade.get_trades_proxy(is_open=False)
recent_trades_data = [
{"profit_ratio": t.close_profit, "close_date": t.close_date}
for t in all_recent_trades
][:10] # Take last 10 for analysis
# 3. Count trades in the last hour
one_hour_ago = current_time - timedelta(hours=1)
trades_last_hour = len(
[t for t in all_recent_trades if t.close_date and t.close_date >= one_hour_ago]
)
# 4. Load balance baselines from state file
state_file = _BASE / "risk/balance_state.json"
if state_file.exists():
with open(state_file) as f:
state = json.load(f)
start_of_day = state.get("start_of_day", total_balance)
start_of_week = state.get("start_of_week", total_balance)
else:
start_of_day = total_balance
start_of_week = total_balance
# 5. Execute all Risk Manager checks
risk_result = run_all_checks(
current_balance=total_balance,
start_of_day_balance=start_of_day,
start_of_week_balance=start_of_week,
trade_amount_usdt=amount * rate,
trades_last_hour=trades_last_hour,
recent_trades=recent_trades_data,
)
if not risk_result["safe_to_trade"]:
logger.warning(
f"[RISK BLOCK] {pair} blocked. Reasons: {risk_result['blocking_reasons']}"
)
return False
except Exception as e:
logger.error(f"[RISK ERROR] Failed to perform risk checks: {e}")
# In case of system error, we fail-safe by blocking entry
return False
# --- LAYER 2: SENTIMENT CHECK ---
sentiment_signal = get_sentiment_signal()
if sentiment_signal == "BEARISH":
logger.info(f"[SENTIMENT BLOCK] {pair} entry blocked due to BEARISH market sentiment.")
return False
# If both layers pass
logger.info(f"[ENTRY ALLOWED] {pair} passed all risk and sentiment gates.")
return True