Basics
mode: spot
timeframe: 1d
interface version: 3
Settings
stoploss: -0.99
has minimal roi
dca
process only new candles
startup candle count: 220
Indicators
ADX
EMA
talib
Concepts
dca
15 related strategies (⧉ identical code, ≈ similar name)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 | """BtcSentimentShieldStrategy — Calendar Shield + Fear&Greed momentum tilt. From sentiment_test.py we found that FGI in crypto is MOMENTUM, not contrarian: X-FEAR (FGI<=25): next 30d mean = +2.7% (vs +5.2% baseline) - bearish X-GREED (FGI>=75): next 30d mean = +12.0% (vs +3.6% baseline) - bullish This contradicts traditional wisdom but is statistically robust (n=2340). Sentiment tilt added to cycle_bias: FGI >= 80: +0.20 FGI >= 65: +0.10 FGI 30-65: 0 FGI <= 30: -0.10 FGI <= 20: -0.15 Combined with Calendar Shield's existing tilts, total clamped to ±0.40. """ from __future__ import annotations from pathlib import Path import numpy as np import pandas as pd import talib.abstract as ta from freqtrade.persistence import Trade from freqtrade.strategy import IStrategy def _load_aux(name: str): candidates = [ Path("/freqtrade/user_data/data") / name, Path(__file__).resolve().parents[1] / "data" / name, Path(__file__).resolve().parents[2] / "user_data" / "data" / name, ] for p in candidates: if p.exists(): try: df = pd.read_feather(p) if "date" in df.columns: df["date"] = pd.to_datetime(df["date"], utc=True) df = df.set_index("date").sort_index() return df except Exception: continue return pd.DataFrame() _HALVING = _load_aux("halving_cycle.feather") _ANOMALY = _load_aux("anomaly_flags.feather") _FGI = _load_aux("fgi_signal.feather") PHASE_SHIFTS = { "ACCUMULATION": 0.20, "EARLY_BULL": 0.10, "PARABOLIC": -0.15, "DISTRIBUTION": -0.40, "BEAR": -0.60, "REACCUMULATION": -0.05, } SIGMOID_K = 4.0 CALENDAR_TILTS = { "is_october": 0.15, "is_july": 0.05, "is_wednesday": 0.05, "is_monday": 0.05, "is_end_of_month": 0.05, } TOTAL_TILT_CLAMP = 0.40 # higher than Calendar Shield's 0.30 to leave room for sentiment def _sigmoid(x, k=SIGMOID_K, c=0.0): return 1.0 / (1.0 + np.exp(-k * (x - c))) class BtcSentimentShieldStrategy(IStrategy): INTERFACE_VERSION: int = 3 timeframe: str = "1d" can_short: bool = False process_only_new_candles: bool = True minimal_roi: dict = {"0": 10.0} stoploss: float = -0.99 use_custom_stoploss: bool = False position_adjustment_enable: bool = True max_entry_position_adjustment: int = 5000 use_exit_signal: bool = True exit_profit_only: bool = False startup_candle_count: int = 220 order_types = {"entry": "limit", "exit": "limit", "stoploss": "limit", "stoploss_on_exchange": False} def populate_indicators(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame: df = dataframe.copy() df["ema200"] = ta.EMA(df, timeperiod=200) df["adx"] = ta.ADX(df, timeperiod=14) df["ret_30d"] = df["close"].pct_change(30) bull = (df["close"] > df["ema200"]) & (df["ret_30d"] > 0.05) & (df["adx"] > 20) bear = (df["close"] < df["ema200"]) & (df["ret_30d"] < -0.10) rcode = pd.Series(0.0, index=df.index) rcode[bull] = 1.0 rcode[bear] = -1.0 n = 3 rmin = rcode.rolling(n, min_periods=n).min() rmax = rcode.rolling(n, min_periods=n).max() stable = rmin == rmax df["regime_confirmed_code"] = rcode.where(stable, other=pd.NA).ffill().fillna(0) df["regime_confirmed"] = df["regime_confirmed_code"].map({1.0: "BULL", -1.0: "BEAR", 0.0: "NEUTRAL"}) df["cycle_bias"] = 0.0 df["cycle_phase"] = "NEUTRAL" if not _HALVING.empty: d = pd.to_datetime(df["date"], utc=True).dt.normalize() df["cycle_bias"] = d.map(_HALVING["cycle_bias"]).ffill().fillna(0.0) df["cycle_phase"] = d.map(_HALVING["phase"]).ffill().fillna("NEUTRAL") df["anomaly"] = 0 if not _ANOMALY.empty: btc_anom = _ANOMALY[_ANOMALY["coin"] == "BTC"][["is_anomaly"]] \ if "coin" in _ANOMALY.columns else _ANOMALY d = pd.to_datetime(df["date"], utc=True).dt.normalize() df["anomaly"] = d.map(btc_anom["is_anomaly"]).fillna(0).astype(int) # Calendar tilts d_idx = pd.to_datetime(df["date"], utc=True) is_oct = (d_idx.dt.month == 10).astype(float) is_jul = (d_idx.dt.month == 7).astype(float) is_wed = (d_idx.dt.day_name() == "Wednesday").astype(float) is_mon = (d_idx.dt.day_name() == "Monday").astype(float) is_eom = (d_idx.dt.day >= 26).astype(float) cal_tilt = ( is_oct * CALENDAR_TILTS["is_october"] + is_jul * CALENDAR_TILTS["is_july"] + is_wed * CALENDAR_TILTS["is_wednesday"] + is_mon * CALENDAR_TILTS["is_monday"] + is_eom * CALENDAR_TILTS["is_end_of_month"] ) # Sentiment tilt from FGI df["sentiment_tilt"] = 0.0 if not _FGI.empty: d = pd.to_datetime(df["date"], utc=True).dt.normalize() df["sentiment_tilt"] = d.map(_FGI["sentiment_tilt"]).ffill().fillna(0.0) # Combined tilt (calendar + sentiment), clamped total_tilt = (cal_tilt + df["sentiment_tilt"]).clip(-TOTAL_TILT_CLAMP, TOTAL_TILT_CLAMP) df["total_tilt"] = total_tilt.values # Sigmoid sizing BASE = 0.85 shifts = df["cycle_phase"].map(PHASE_SHIFTS).fillna(0.0).astype(float) adjusted_bias = df["cycle_bias"].astype(float) + shifts + df["total_tilt"] cycle_mult = _sigmoid(adjusted_bias.values, k=SIGMOID_K, c=0.0) df["ai_target"] = (BASE * cycle_mult).clip(0.0, BASE) df.loc[df["anomaly"] == 1, "ai_target"] = 0.0 df.loc[df["regime_confirmed"] == "BEAR", "ai_target"] = 0.0 return df def populate_entry_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame: df = dataframe ready = df["ema200"].notna() enter = ready & (df["regime_confirmed"] == "BULL") & (df["ai_target"] > 0.15) df.loc[enter, "enter_long"] = 1 df.loc[enter, "enter_tag"] = "sentiment_shield" return df def populate_exit_trend(self, dataframe: pd.DataFrame, metadata: dict) -> pd.DataFrame: df = dataframe exit_cond = ((df["regime_confirmed"] == "BEAR") | (df["anomaly"] == 1) | (df["ai_target"] < 0.20)) df.loc[exit_cond, "exit_long"] = 1 df.loc[exit_cond, "exit_tag"] = "sentiment_shield:exit" return df def custom_stake_amount( self, pair, current_time, current_rate, proposed_stake, min_stake, max_stake, leverage, entry_tag, side, **kwargs, ): df, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) if df is None or df.empty: return float(proposed_stake) target = float(df.iloc[-1]["ai_target"]) if max_stake and max_stake > 0: return float(max_stake) * max(target, 0.0) return float(proposed_stake) def adjust_trade_position( self, trade, current_time, current_rate, current_profit, min_stake, max_stake, current_entry_rate, current_exit_rate, current_entry_profit, current_exit_profit, **kwargs, ): df, _ = self.dp.get_analyzed_dataframe(pair=trade.pair, timeframe=self.timeframe) if df is None or df.empty: return None last = df.iloc[-1] target = float(last["ai_target"]) if target <= 0.10: return None btc_qty = float(trade.amount or 0) btc_value = btc_qty * float(current_rate) usdt_free = float(self.wallets.get_free(trade.stake_currency) or 0.0) total = btc_value + usdt_free if total <= 0: return None target_btc_value = target * total drift = btc_value - target_btc_value if drift < -0.05 * total and usdt_free > 1: buy = min(-drift, usdt_free * 0.99) if max_stake and max_stake > 0: buy = min(buy, max_stake) if min_stake and buy < min_stake: return None return float(buy) if drift > 0.10 * total: sell = min(drift, btc_value * 0.99) if min_stake and sell < min_stake: return None return -float(sell) return None __all__ = ["BtcSentimentShieldStrategy"] |
Strategy League — fixed backtest that feeds the ranking
Export report Freqtrade logsRun finished · took 29.4s
pairs 33 pairs
timerange 20210101-20260101
mode spot
timeframe 1d
stake 100 USDT
wallet 1000 USDT
max open trades 10
fee exchange lowest tier
total profit+175.92%
final wallet2759 USDT
win rate33.8%
max drawdown-74.57%
market change+403.38%
vs market-227.46%
timeframe1d
profit factor1.16
expectancy ratio0.108
break-even fee0.1075%
sharpe0.063
sortino0.142
CAGR+22.5%
calmar2.47
avg MFE+43.88%
avg MAE-34.43%
avg profit/trade0.08%
avg duration2276h 52m
best trade+634.50%
worst trade-45.12%
win/loss streak4 / 24
DCA orders102 max / 8.14 avg
max stake exposure9755.82 USDT (97.56x base)
positive months16/39
consistent (3-mo)48.6%
worst 3-mo-181.33%
trades130
revision1
likely annual return+20%
range (5th–95th)-16% … +90%
chance of profit79.8%
worst-5% outcome-22%
significance (p)0.3068
risk of ruin0.0%
- profit isn't statistically significant (p=0.31) — hard to tell apart from luck
- did not beat simply holding the market
- very deep drawdown (-75%)
Resampling the trade sequence 2,000× shows the spread of results this edge could plausibly produce — separating a dependable strategy from one that got lucky once.
Loading charts…
Monthly breakdown
| Month | Regime | Trades | Profit % | Avg % | Win | Loss | Win % | DD % | Avg dur |
|---|---|---|---|---|---|---|---|---|---|
| Jan 2026 | bullish trending low vol | 1 | +3.72 | 2.92 | 1 | 0 | 100.0 | -52.78 | 600h 00m |
| Dec 2025 | bearish trending low vol | 2 | -98.78 | -33.33 | 0 | 2 | 0.0 | -53.41 | 708h 00m |
| Nov 2025 | bearish trending high vol | 4 | -65.23 | -10.74 | 2 | 2 | 50.0 | -36.51 | 2604h 00m |
| Oct 2025 | bearish trending low vol | 8 | +91.37 | -3.86 | 4 | 4 | 50.0 | -44.7 | 5400h 00m |
| Jul 2025 | bullish choppy low vol | 1 | -10.17 | -13.41 | 0 | 1 | 0.0 | -40.98 | 648h 00m |
| Jun 2025 | bearish choppy low vol | 5 | -12.97 | -7.53 | 1 | 4 | 20.0 | -40.94 | 979h 12m |
| Apr 2025 | bullish choppy low vol | 2 | -34.01 | -11.86 | 0 | 2 | 0.0 | -37.02 | 1608h 00m |
| Mar 2025 | bearish trending high vol | 1 | +9.68 | 4.70 | 1 | 0 | 100.0 | -31.2 | 3672h 00m |
| Feb 2025 | bearish trending low vol | 7 | +152.41 | 11.83 | 5 | 2 | 71.4 | -60.71 | 4093h 43m |
| Dec 2024 | bullish trending low vol | 1 | +4.98 | 4.96 | 1 | 0 | 100.0 | -58.94 | 2184h 00m |
| Oct 2024 | bullish choppy low vol | 4 | -23.97 | -10.67 | 0 | 4 | 0.0 | -59.79 | 1152h 00m |
| Sep 2024 | bearish choppy low vol | 1 | +74.64 | 31.61 | 1 | 0 | 100.0 | -55.69 | 7944h 00m |
| Aug 2024 | bearish choppy high vol | 5 | -57.06 | -23.16 | 0 | 5 | 0.0 | -68.46 | 566h 24m |
| Jul 2024 | bearish trending low vol | 3 | +25.32 | 7.33 | 2 | 1 | 66.7 | -65.21 | 4440h 00m |
| Jun 2024 | bearish choppy low vol | 5 | -3.21 | -10.10 | 1 | 4 | 20.0 | -70.13 | 2428h 48m |
| Apr 2024 | bearish choppy high vol | 4 | +59.12 | 12.38 | 3 | 1 | 75.0 | -71.76 | 5214h 00m |
| Feb 2024 | bullish trending low vol | 1 | +6.83 | 18.23 | 1 | 0 | 100.0 | -72.6 | 2952h 00m |
| Jan 2024 | bearish choppy high vol | 1 | -1.14 | -3.90 | 0 | 1 | 0.0 | -73.77 | 2064h 00m |
| Oct 2023 | bullish trending low vol | 1 | +1.38 | 3.30 | 1 | 0 | 100.0 | -73.58 | 2448h 00m |
| Sep 2023 | bearish choppy low vol | 1 | -7.99 | -21.30 | 0 | 1 | 0.0 | -73.81 | 480h 00m |
| Aug 2023 | bearish choppy low vol | 8 | -3.79 | -5.42 | 3 | 5 | 37.5 | -74.57 | 1941h 00m |
| Jun 2023 | bullish trending low vol | 1 | -7.08 | -18.69 | 0 | 1 | 0.0 | -71.8 | 1944h 00m |
| May 2023 | bearish choppy low vol | 6 | +11.91 | -3.69 | 1 | 5 | 16.7 | -73.24 | 1224h 00m |
| Mar 2023 | bullish trending high vol | 7 | -12.91 | -6.09 | 0 | 7 | 0.0 | -72.62 | 1045h 43m |
| Dec 2022 | bearish trending low vol | 3 | -26.85 | -8.55 | 0 | 3 | 0.0 | -70.41 | 1008h 00m |
| Nov 2022 | bearish trending high vol | 2 | -26.59 | -15.40 | 0 | 2 | 0.0 | -65.82 | 708h 00m |
| Sep 2022 | bearish choppy high vol | 1 | -36.02 | -21.94 | 0 | 1 | 0.0 | -61.27 | 1320h 00m |
| Jun 2022 | bearish trending high vol | 1 | -40.71 | -26.82 | 0 | 1 | 0.0 | -55.1 | 1032h 00m |
| May 2022 | bearish trending high vol | 5 | -79.92 | -17.02 | 0 | 5 | 0.0 | -48.13 | 729h 36m |
| Apr 2022 | bearish choppy high vol | 5 | -23.86 | -7.24 | 1 | 4 | 20.0 | -34.46 | 657h 36m |
| Feb 2022 | bearish trending high vol | 2 | -77.55 | -14.23 | 0 | 2 | 0.0 | -30.37 | 1236h 00m |
| Jan 2022 | bearish trending high vol | 6 | +26.80 | -9.73 | 3 | 3 | 50.0 | -17.1 | 2392h 00m |
| Dec 2021 | bearish trending high vol | 7 | +14.69 | 1.47 | 4 | 3 | 57.1 | -6.34 | 2077h 43m |
| Nov 2021 | bullish trending high vol | 1 | +87.75 | 23.51 | 1 | 0 | 100.0 | 0.0 | 7704h 00m |
| Sep 2021 | bearish trending high vol | 5 | -43.27 | -7.24 | 0 | 5 | 0.0 | -18.4 | 1459h 12m |
| Jul 2021 | bearish trending high vol | 1 | -36.82 | -32.77 | 0 | 1 | 0.0 | -8.46 | 1272h 00m |
| Jun 2021 | bearish trending high vol | 6 | +208.27 | 13.53 | 5 | 1 | 83.3 | -5.18 | 3416h 00m |
| May 2021 | bearish trending high vol | 4 | -5.57 | -0.22 | 1 | 3 | 25.0 | -7.05 | 3198h 00m |
| Jan 2021 | bullish trending high vol | 1 | +132.51 | 634.50 | 1 | 0 | 100.0 | 0.0 | 648h 00m |
Yearly breakdown
| Year | Trades | Profit % | Avg % | Win | Loss | Win % | DD % | Avg dur |
|---|---|---|---|---|---|---|---|---|
| 2026 | 1 | +3.72 | 2.92 | 1 | 0 | 100.0 | -52.78 | 600h 00m |
| 2025 | 30 | +32.30 | -4.26 | 13 | 17 | 43.3 | -60.71 | 3204h 00m |
| 2024 | 25 | +85.51 | -3.46 | 9 | 16 | 36.0 | -73.77 | 2756h 10m |
| 2023 | 24 | -18.48 | -6.03 | 5 | 19 | 20.8 | -74.57 | 1461h 00m |
| 2022 | 25 | -284.70 | -12.53 | 4 | 21 | 16.0 | -70.41 | 1222h 05m |
| 2021 | 25 | +357.56 | 27.19 | 12 | 13 | 48.0 | -18.4 | 2590h 05m |
Trade charts — best 2 and worst 2 performing pairs (full OHLC candles are expensive to render for every pair)
Backtests — over a market period
Backtest this strategy over a chosen crypto-cycle period. These don't affect the League ranking, and need that period's candle data downloaded.
Log in or sign up to run backtests.
| Period | Range | Total % | Win % | Max DD | Trades | |
|---|---|---|---|---|---|---|
| 2020 · DeFi Summer & Pre-Halving Rally | 20200101-20210101 | not run | ||||
| 2021 · Institutional Bull Market | 20210101-20220101 | not run | ||||
| 2022 · Post-Bull Crash & Macro Tightening | 20220101-20230101 | not run | ||||
| 2023–2024 · Recovery & ETF Anticipation | 20230101-20250101 | not run | ||||
| 2025–2026 · Current Cycle | 20250101-20260101 | not run | ||||
Walk forward
Out-of-sample backtest on recent data · 33 pairs · 20260101-20260701.
Backtest trust check
no lookahead patterns · 2 thing(s) worth reviewing before trusting the numbers
| Line | Pattern | Detail | |
|---|---|---|---|
| 90 | review | startup_candles_too_small | startup_candle_count is 220, but EMA(timeperiod=200) needing 3x warmup needs at least 600 candles -- so the first 380+ candles of every backtest use an indicator that hasn't warmed up. Recursive indicators (EMA/RSI/ADX/ATR) want several times their period, not exactly it |
| 167 | review | enter_tag_overwrite | enter_tag/exit_tag is written by 2 separate assignments -- they share one column and run in source order, so a row matching more than one condition keeps only the LAST tag. Per-tag statistics won't mean what they appear to |
ran by Ron · took s
Lookahead analysis
freqtrade lookahead-analysis: detects strategies peeking at future candles.