MidasBot: bot trading crypto IA + stratégies Ichimoku validées
- Infra: Freqtrade (futures dry-run) + Redis + dashboard + Docker Compose - Couche IA: ai_analyzer (Claude via abonnement, MCP TradingView, backfill biais) - Stratégies: SampleStrategy, AiBiasStrategy, IchimokuLS (long/short, validée train/test + données vierges + walk-forward), MTFIchimoku, variantes hyperopt - Arbitrage CEX (dry-run), backtesting, walk-forward, volatility targeting - IchimokuLS en dry-run live (config_live.json) Claude-Session: https://claude.ai/code/session_01VHETcFacdnDhQzthLpdYFR
This commit is contained in:
135
freqtrade/user_data/strategies/MTFIchimoku.py
Normal file
135
freqtrade/user_data/strategies/MTFIchimoku.py
Normal file
@@ -0,0 +1,135 @@
|
||||
# pragma pylint: disable=missing-docstring, invalid-name, too-few-public-methods
|
||||
"""
|
||||
MTFIchimoku — Ichimoku MULTI-TIMEFRAME (technique S/R inter-unités).
|
||||
|
||||
Idée (méthode de l'utilisateur) : trader sur une unité basse (15m) en utilisant
|
||||
les Tenkan/Kijun de l'unité SUPÉRIEURE (1h) comme supports/résistances.
|
||||
|
||||
Règles :
|
||||
- Tendance 1h donnée par tenkan_1h vs kijun_1h.
|
||||
- LONG : en tendance 1h haussière, le prix 15m RECLAIME le support (croise au-dessus
|
||||
de la Kijun 1h) → rebond sur support.
|
||||
- SHORT : en tendance 1h baissière, le prix 15m CASSE le support (croise sous la
|
||||
Kijun 1h) → rejet sur résistance.
|
||||
- Sortie : perte du niveau (close repasse de l'autre côté de la Kijun 1h) + ROI/stop.
|
||||
|
||||
La Kijun 1h sert de S/R principal (niveau lent/fort), la Tenkan 1h de filtre de tendance.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import talib.abstract as ta
|
||||
from pandas import DataFrame
|
||||
|
||||
from freqtrade.strategy import (
|
||||
IStrategy,
|
||||
informative,
|
||||
IntParameter,
|
||||
DecimalParameter,
|
||||
)
|
||||
|
||||
|
||||
class MTFIchimoku(IStrategy):
|
||||
INTERFACE_VERSION = 3
|
||||
timeframe = "15m" # unité de trading
|
||||
can_short = True
|
||||
|
||||
# Paramètres optimisables
|
||||
buy_adx_min = IntParameter(15, 40, default=25, space="buy", optimize=True)
|
||||
buy_pullback_pct = DecimalParameter(0.004, 0.03, default=0.012, decimals=3, space="buy", optimize=True)
|
||||
buy_rsi_max = IntParameter(50, 72, default=60, space="buy", optimize=True)
|
||||
sell_rsi_min = IntParameter(28, 50, default=40, space="sell", optimize=True)
|
||||
|
||||
minimal_roi = {"0": 0.02, "60": 0.012, "180": 0.006, "360": 0}
|
||||
stoploss = -0.025
|
||||
trailing_stop = True
|
||||
trailing_stop_positive = 0.008
|
||||
trailing_stop_positive_offset = 0.014
|
||||
trailing_only_offset_is_reached = True
|
||||
|
||||
startup_candle_count: int = 240
|
||||
process_only_new_candles = True
|
||||
use_exit_signal = True
|
||||
|
||||
def leverage(self, pair, current_time, current_rate, proposed_leverage,
|
||||
max_leverage, entry_tag, side, **kwargs) -> float:
|
||||
return 1.0
|
||||
|
||||
@informative("1h")
|
||||
def populate_indicators_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
# Tenkan / Kijun sur l'unité supérieure (1h) → deviendront tenkan_1h / kijun_1h.
|
||||
high, low = dataframe["high"], dataframe["low"]
|
||||
dataframe["tenkan"] = (high.rolling(9).max() + low.rolling(9).min()) / 2
|
||||
dataframe["kijun"] = (high.rolling(26).max() + low.rolling(26).min()) / 2
|
||||
return dataframe
|
||||
|
||||
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
# tenkan_1h / kijun_1h sont injectés automatiquement (forward-fill sur le 15m).
|
||||
dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)
|
||||
dataframe["adx"] = ta.ADX(dataframe, timeperiod=14)
|
||||
# Plus haut/bas récents (pour exiger un VRAI pullback vers le niveau)
|
||||
dataframe["hh8"] = dataframe["high"].rolling(8).max()
|
||||
dataframe["ll8"] = dataframe["low"].rolling(8).min()
|
||||
return dataframe
|
||||
|
||||
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
kijun = dataframe["kijun_1h"]
|
||||
tenkan = dataframe["tenkan_1h"]
|
||||
close, op, low, high = (
|
||||
dataframe["close"], dataframe["open"], dataframe["low"], dataframe["high"]
|
||||
)
|
||||
|
||||
rsi = dataframe["rsi"]
|
||||
adx = dataframe["adx"]
|
||||
adx_min = self.buy_adx_min.value
|
||||
pb = self.buy_pullback_pct.value
|
||||
uptrend_1h = (tenkan > kijun) & (close > kijun) # biais haussier 1h
|
||||
downtrend_1h = (tenkan < kijun) & (close < kijun) # biais baissier 1h
|
||||
|
||||
# LONG : en tendance forte, VRAI pullback vers le support Kijun 1h puis rebond + momentum.
|
||||
dataframe.loc[
|
||||
(
|
||||
uptrend_1h
|
||||
& (adx > adx_min) # tendance forte
|
||||
& (dataframe["hh8"] > kijun * (1 + pb)) # le prix venait nettement au-dessus
|
||||
& (low <= kijun * 1.001) # mèche teste le support
|
||||
& (close > kijun) # clôture au-dessus (support tient)
|
||||
& (close > op) # bougie de rebond
|
||||
& (rsi > rsi.shift(1)) # momentum qui se retourne à la hausse
|
||||
& (rsi < self.buy_rsi_max.value) # pas déjà suracheté
|
||||
& (dataframe["volume"] > 0)
|
||||
),
|
||||
"enter_long",
|
||||
] = 1
|
||||
|
||||
# SHORT : miroir exact (rejet sur résistance en tendance baissière forte).
|
||||
dataframe.loc[
|
||||
(
|
||||
downtrend_1h
|
||||
& (adx > adx_min)
|
||||
& (dataframe["ll8"] < kijun * (1 - pb))
|
||||
& (high >= kijun * 0.999)
|
||||
& (close < kijun)
|
||||
& (close < op)
|
||||
& (rsi < rsi.shift(1))
|
||||
& (rsi > self.sell_rsi_min.value)
|
||||
& (dataframe["volume"] > 0)
|
||||
),
|
||||
"enter_short",
|
||||
] = 1
|
||||
return dataframe
|
||||
|
||||
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
kijun = dataframe["kijun_1h"]
|
||||
tenkan = dataframe["tenkan_1h"]
|
||||
close = dataframe["close"]
|
||||
# Sortie LONG : cassure NETTE du support (clôture sous la Kijun ET sous la Tenkan 1h)
|
||||
dataframe.loc[
|
||||
((close < kijun) & (close < tenkan) & (dataframe["volume"] > 0)),
|
||||
"exit_long",
|
||||
] = 1
|
||||
# Sortie SHORT : reprise NETTE au-dessus de la résistance
|
||||
dataframe.loc[
|
||||
((close > kijun) & (close > tenkan) & (dataframe["volume"] > 0)),
|
||||
"exit_short",
|
||||
] = 1
|
||||
return dataframe
|
||||
Reference in New Issue
Block a user