📝 MaĂźtriser les fichiers de stratĂ©gies ...

📝 MaĂźtriser les fichiers de stratĂ©gies Freqtrade : Le Guide de rĂ©fĂ©rence

Jul 25, 2025

Tu as configuré tes bots grùce au guide des fichiers de configuration, mais comment créer le moteur logique de tes robots ? Les fichiers de stratégies définissent la logique de trading : quand acheter, quand vendre, et comment gérer le risque.

Ce guide complet te servira de rĂ©fĂ©rence permanente. Garde-le dans tes favoris ! 📌

image

📌 Note fork : la structure d'une stratĂ©gie est la mĂȘme sur le Freqtrade officiel et sur mon fork freqtrade-ultimate. Mais le fork t'apporte des outils cĂŽtĂ© stratĂ©gie : choix du sampler d'hyperopt + une loss profit/drawdown, un filtre de pairlist pour les shorts, et surtout la gestion automatique des fermetures externes sur Hyperliquid (j'y reviens). PrĂ©sentation complĂšte : mes forks Freqtrade et FreqUI.


🎯 Qu'est-ce qu'un fichier de stratĂ©gie ?

Les fichiers de stratégie (en .py) sont le moteur intelligent de tes bots. Ils contiennent :

  • 📊 Les indicateurs techniques à calculer

  • 🎯 Les conditions d'entrĂ©e en position

  • đŸšȘ Les conditions de sortie et prises de profit

  • đŸ›ĄïžÂ La gestion du risque (stoploss, ROI, trailing)

  • ⚙ Les paramĂštres optimisables pour l'hyperoptimisation

Point crucial : pour lancer n'importe quelle action, tu as TOUJOURS besoin de deux fichiers : un fichier de stratégie (.py) et un fichier de configuration (.json).


📚 Ressources essentielles


đŸ—ïž Structure d'un fichier stratĂ©gie

Un fichier de stratĂ©gie suit toujours la mĂȘme structure :

# 1. Imports nécessaires
from freqtrade.strategy import IStrategy
import pandas as pd
import talib.abstract as ta

# 2. Définition de la classe
class ma_strategie(IStrategy):

    # 3. ParamĂštres de risque
    minimal_roi = {"0": 0.1}
    stoploss = -0.1

    # 4. Configuration
    timeframe = '1h'

    # 5. Trois fonctions principales
    def populate_indicators()
    def populate_entry_trend()
    def populate_exit_trend()

🎓 Niveau 1 : StratĂ©gie simple (basic_emas)

Commençons avec un exemple concret : la stratégie basic_emas.py, qui utilise le croisement de moyennes mobiles.

1ïžâƒŁ Les imports

from functools import reduce
from freqtrade.strategy import IStrategy
from freqtrade.strategy import CategoricalParameter, DecimalParameter, IntParameter
from pandas import DataFrame
import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
  • IStrategy : la classe de base obligatoire

  • pandas : pour manipuler les donnĂ©es (DataFrame)

  • talib : bibliothĂšque d'indicateurs techniques (il existe aussi ta et d'autres)

  • qtpylib : utilitaires supplĂ©mentaires (comme crossed_above)

2ïžâƒŁ DĂ©finition de la classe

class basic_emas(IStrategy):
    # Le code de la stratégie sera ici

⚠ Important : le nom de la classe (basic_emas) doit correspondre EXACTEMENT au champ "strategy" de ta config JSON, ou alors tu dois prĂ©ciser --strategy basic_emas dans ta commande :

freqtrade trade --strategy basic_emas --config live_configs/...
freqtrade backtesting --strategy basic_emas --config backtest_configs/...

3ïžâƒŁ ParamĂštres de risque / take profit

minimal_roi = {
    "0": 1.0,      # 100% de gain → vendre immĂ©diatement
    "10000": 0.40, # 40% aprùs 10000 minutes → vendre
    "25000": 0.15, # 15% aprùs 25000 minutes → vendre
    "50000": 0     # Break-even aprùs 50000 minutes → vendre
}
stoploss = -0.35   # Stop Ă  -35%

📊 Le ROI progressif permet de prendre des profits Ă  diffĂ©rents niveaux selon le temps passĂ© en position. Le dernier niveau ("50000": 0) est un break-even.

💡 Ces valeurs sont en minutes, peu importe la timeframe ! On en parle en dĂ©tail dans Comment optimiser une stratĂ©gie ? (Guide complet + VidĂ©o).

⚠ PiĂšge du trailing stop natif : le trailing_stop intĂ©grĂ© de Freqtrade peut, dans certaines configs, produire des « profits fantĂŽmes » et des sorties incohĂ©rentes. Par expĂ©rience, si tu as besoin d'un stop suiveur fiable, je te conseille de le coder toi-mĂȘme dans custom_exit() plutĂŽt que de te reposer sur le trailing natif.

4ïžâƒŁ Configuration de base

timeframe = '1d'  # Timeframe journaliĂšre

Timeframes courantes : 1m, 5m, 15m, 30m, 1h, 4h, 1d.

5ïžâƒŁ Les trois fonctions principales

A. Calcul des indicateurs

def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
    dataframe['ema12'] = ta.EMA(dataframe, timeperiod=12)
    dataframe['ema80'] = ta.EMA(dataframe, timeperiod=80)
    return dataframe

Cette fonction calcule les indicateurs et les ajoute au DataFrame. Elle est appelée une fois par paire et par bougie.

B. Conditions d'entrée

def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
    dataframe.loc[
        (
            qtpylib.crossed_above(dataframe['ema12'], dataframe['ema80']) &
            (dataframe['volume'] > 0)
        ),
        'enter_long'] = 1
    return dataframe

Signal d'achat quand l'EMA 12 croise l'EMA 80 à la hausse ET que le volume est positif (sécurité).

💡 Alternative sans qtpylib (fait la mĂȘme chose) :

(dataframe['ema12'] > dataframe['ema80']) &
(dataframe['ema12'].shift(1) <= dataframe['ema80'].shift(1))

C. Conditions de sortie

def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -&gt; DataFrame:
    dataframe.loc[
        (
            qtpylib.crossed_above(dataframe['ema80'], dataframe['ema12']) &
            (dataframe['volume'] > 0)
        ),
        'exit_long'] = 1
    return dataframe

Signal de vente : le croisement inverse (baissier).

Le fichier complet forme une stratégie trÚs basique mais parfaitement valide :

from functools import reduce
from freqtrade.strategy import IStrategy
from freqtrade.strategy import CategoricalParameter, DecimalParameter, IntParameter
from pandas import DataFrame

import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib


class basic_emas(IStrategy):
    INTERFACE_VERSION: int = 3
    minimal_roi = {
        "0": 1.0,
        "10000": 0.40,
        "25000": 0.15,
        "50000": 0
    }

    stoploss = -0.35

    timeframe = '1d'

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -&gt; DataFrame:
        dataframe['ema12'] = ta.EMA(dataframe, timeperiod=12)
        dataframe['ema80'] = ta.EMA(dataframe, timeperiod=80)
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -&gt; DataFrame:
        dataframe.loc[
            (
                qtpylib.crossed_above(dataframe['ema12'], dataframe['ema80']) &
                (dataframe['volume'] > 0)
            ),
            'enter_long'] = 1
        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -&gt; DataFrame:
        dataframe.loc[
            (
                qtpylib.crossed_above(dataframe['ema80'], dataframe['ema12']) &
                (dataframe['volume'] > 0)
            ),
            'exit_long'] = 1
        return dataframe

🔧 Customiser davantage la stratĂ©gie (les callbacks)

On peut customiser une stratégie en réécrivant les fonctions dites de « callback », par exemple :

  • custom_stake_amount() : ajuster la somme allouĂ©e Ă  une position

  • custom_exit() : sorties personnalisĂ©es (crash de marchĂ©, objectif de portefeuille atteint, ATH du BTC
) — et c'est aussi lĂ  qu'on code un trailing stop fiable

  • custom_entry_price() / custom_exit_price() : prix d'entrĂ©e/sortie spĂ©cifique

  • adjust_trade_position() : changer la taille d'une position dans le temps (DCA)

📘 Toutes les fonctions de callback sont listĂ©es dans la documentation officielle.

💡 Pour que les exemples de callbacks ci-dessous tournent, ajoute ces imports en haut de ta stratĂ©gie : from datetime import datetime, from typing import Optional, from freqtrade.persistence import Trade.

Par exemple, pour ajouter à basic_emas une logique de DCA qui recharge la position quand un nouveau signal d'achat apparaßt, on ajoute adjust_trade_position() :

    # Nombre de DCA maximum autorisé par trade
    max_dca = 3

    def adjust_trade_position(self, trade: Trade, current_time, current_rate: float,
                              current_profit: float, min_stake: float, max_stake: float,
                              **kwargs) -&gt; Optional[float]:
        """
        Recharge une position si elle est en perte et que le signal d'entrée est de nouveau valide.
        """
        # 1. En perte ?
        if current_profit >= 0:
            return None

        # 2. Trop de DCA ?
        filled_orders = trade.select_filled_orders(trade.entry_side)
        if len(filled_orders) > self.max_dca:
            return None

        # 3. Nouveau signal d'achat ?
        dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe)
        if dataframe is None or dataframe.empty:
            return None

        last_row = dataframe.iloc[-1]
        if last_row.get('enter_long', 0) != 1:
            return None

        # 4. Recalculer le montant du DCA (simple : x1.5)
        try:
            initial_stake = filled_orders[0].cost
            dca_stake = min(initial_stake * 1.5, max_stake)
            return dca_stake
        except Exception as e:
            logger.error(f"DCA error for {trade.pair}: {e}")
            return None

⚙ Ordre d'exĂ©cution du bot (fonctions et callbacks)

Avant d'introduire des fonctions comme adjust_trade_position(), il faut comprendre ce qui se passe à chaque cycle. Freqtrade exécute chaque boucle d'analyse selon une séquence définie (le bot execution logic). Pour chaque paire de ta pairlist, dans l'ordre :

  • RĂ©cupĂ©ration des paires tradables (+ les paires des positions ouvertes si elles ne sont plus tradables)

  • TĂ©lĂ©chargement des donnĂ©es OHLCV (une fois par bougie)

  • Appel à bot_start()

  • ExĂ©cution (deux fois) de populate_indicators(), populate_entry_trend(), populate_exit_trend()

  • Synchronisation des ordres limites ouverts, puis check_entry_timeout(), check_exit_timeout() et adjust_entry_price() (voir le guide des ordres)

  • Évaluation des sorties : stoploss → ROI → signaux (exit_long/exit_short) → custom_exit() → custom_stoploss()

  • Confirmation des sorties via confirm_trade_exit() avant placement de l'ordre

  • Ajustement de la taille des positions via adjust_trade_position() (rĂ©duire ou recharger — le DCA)

  • VĂ©rification des slots disponibles (max_open_trades) : si un signal d'entrĂ©e est valide, l'entrĂ©e est envoyĂ©e. Le prix vient de entry_pricing ou custom_entry_price(), la taille de custom_stake_amount() (sinon stake_amount), et confirm_trade_entry() doit retourner True

Ces étapes sont rejouées toutes les process_throttle_secs secondes (défini dans la config). Plus ta timeframe est haute, plus tu peux mettre une grande valeur. Détails dans la doc officielle.

🌊 Important si tu trades sur Hyperliquid : une position peut disparaĂźtre de l'exchange sans que ton bot l'ait demandĂ© (ADL, liquidation, fermeture manuelle). Avec le Freqtrade officiel, le bot tourne alors en boucle sur un « trade fantĂŽme ». freqtrade-ultimate dĂ©tecte et ferme proprement ces fermetures externes automatiquement — rien Ă  coder dans ta stratĂ©gie.

⚙ SpĂ©cificitĂ© liĂ©e Ă  l'hyperopt

Par défaut, lors d'un hyperopt : populate_indicators() est appelé une seule fois par paire, puis chaque jeu de paramÚtres testé (epoch) déclenche populate_entry_trend() et populate_exit_trend().

En ajoutant l'option --analyze-per-epoch à freqtrade hyperopt, tu demandes Ă  recalculer les indicateurs à chaque epoch. Utile pour optimiser des indicateurs dont les pĂ©riodes sont elles-mĂȘmes des paramĂštres (EMA, VWAP configurables), mais nettement plus coĂ»teux en calcul. Plus de dĂ©tails : Comment optimiser une stratĂ©gie ?

⚠ Le piĂšge du fichier .json co-localisĂ© : quand tu lances un hyperopt, Freqtrade peut Ă©crire les meilleurs paramĂštres dans un MaStrategie.json posĂ© Ă  cĂŽtĂ© de ton MaStrategie.py. Ce fichier écrase silencieusement tes buy_params/sell_params dĂ©finis dans le code Python. Autrement dit : si tes valeurs live ne correspondent pas Ă  ce que tu lis dans le .py, vĂ©rifie toujours s'il existe un .json du mĂȘme nom — c'est lui qui fait foi.

💡 Bonus fork : freqtrade-ultimate te laisse choisir l'algorithme d'optimisation via --sampler (TPE, CMA-ES, NSGA-II/III, GP, QMC) et fournit une loss profit/drawdown. Tout est expliquĂ© dans le guide Hyperopt & Hyperopt-Loss et dans le post sur les forks.


🚀 Niveau 2 : StratĂ©gie avancĂ©e (simple_vwap_v1)

Pour les traders plus expĂ©rimentĂ©s, examinons simple_vwap_v1, prĂ©sentĂ©e en dĂ©tail dansÂ đŸ§© Simple VWAP v1 — la stratĂ©gie du placement constant.

🎯 Philosophie : exposition maximale au marchĂ© — conditions d'entrĂ©e ultra-permissives et DCA intelligent.

📊 Indicateurs sophistiquĂ©s

def VWAPB(dataframe, window_size=20, num_of_std=1):
    """Bandes VWAP avec écart-type"""
    df = dataframe.copy()
    df['vwap'] = qtpylib.rolling_vwap(df, window=window_size)
    rolling_std = df['vwap'].rolling(window=window_size).std()
    df['vwap_low'] = df['vwap'] - (rolling_std * num_of_std)
    df['vwap_high'] = df['vwap'] + (rolling_std * num_of_std)
    return df['vwap_low'], df['vwap'], df['vwap_high']

ParamĂštres optimisables

# Stoploss personnalisé optimisable
my_custom_stoploss = DecimalParameter(
    -0.45, -0.2,         # Plage : -45% Ă  -20%
    decimals=2,
    default=-0.37,       # Valeur par défaut : -37%
    space="sell",
    optimize=True        # Peut ĂȘtre optimisĂ©
)

# Ratio du capital Ă  utiliser
tradable_balance_ratio = CategoricalParameter(
    [0.25, 0.35, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0],
    default=0.35,
    space="buy",
    optimize=True
)

Ces paramĂštres peuvent ĂȘtre optimisĂ©s automatiquement avec la commande hyperopt (voir aussi DecimalParameter et CategoricalParameter).

Gestion avancée des positions

1. Taille dynamique selon la volatilité (voir aussi le post sur le position sizing) :

def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
                        proposed_stake: float, min_stake: float, max_stake: float,
                        **kwargs) -&gt; float:
    # Calcul de l'ATR pour mesurer la volatilité
    recent_atr_pct = (recent_high - recent_low).mean() / recent_close

    # Réduction si volatilité élevée
    if recent_atr_pct > 0.102:    # >10.2%
        volatility_adjustment = 0.5   # -50%
    elif recent_atr_pct > 0.061:  # >6.1%
        volatility_adjustment = 0.75  # -25%
    else:
        volatility_adjustment = 1.0   # Taille normale

2. SystÚme DCA intelligent (rechargement progressif des positions perdantes) :

def adjust_trade_position(self, trade: Trade, current_time: datetime, current_rate: float,
                          current_profit: float, min_stake: float, max_stake: float,
                          **kwargs) -&gt; Optional[float]:
    """
    SystĂšme de DCA (Dollar Cost Averaging) intelligent.
    Rechargements déclenchés à des seuils de perte croissants, avec des montants croissants.

    Exemple avec les paramÚtres par défaut :
    - 1er DCA Ă  -1.2% avec 1.8x le montant initial
    - 2e DCA Ă  -3.6% avec 3.24x le montant initial
    - 3e DCA Ă  -10.8% avec 5.83x le montant initial
    - 4e DCA Ă  -32.4% avec 10.5x le montant initial
    """
    # Ne rien faire si on n'est pas en perte suffisante
    if current_profit > self.initial_safety_order_trigger.value:
        return None

    # Récupérer l'historique des ordres
    filled_buys = trade.select_filled_orders(trade.entry_side)
    count_of_buys = len(filled_buys)

    # Vérifier qu'on n'a pas atteint la limite de DCA
    if not (1 <= count_of_buys <= self.max_so_multiplier_orig.value):
        return None

    # Calcul du seuil de déclenchement pour ce DCA
    if self.safety_order_step_scale.value == 1:
        # Espacement linéaire
        safety_order_trigger = abs(self.initial_safety_order_trigger.value) * count_of_buys
    else:
        # Espacement exponentiel (recommandé)
        initial_trigger = abs(self.initial_safety_order_trigger.value)
        scale = self.safety_order_step_scale.value
        if scale > 1:
            numerator = scale * (pow(scale, count_of_buys - 1) - 1)
            denominator = scale - 1
            safety_order_trigger = initial_trigger + (initial_trigger * scale * numerator / denominator)
        else:
            numerator = 1 - pow(scale, count_of_buys - 1)
            denominator = 1 - scale
            safety_order_trigger = initial_trigger + (initial_trigger * scale * numerator / denominator)

    # Vérifier si on a atteint le seuil de DCA
    if current_profit > (-1 * abs(safety_order_trigger)):
        return None

    # Calcul du montant du DCA
    try:
        actual_initial_stake = filled_buys[0].cost
        stake_amount = actual_initial_stake * pow(
            self.safety_order_volume_scale.value,
            count_of_buys - 1
        )
        return stake_amount
    except Exception as e:
        logger.error(f'Erreur DCA pour {trade.pair}: {str(e)}')
        return None

⚠ Le DCA agressif (rechargement des positions perdantes) peut transformer une petite perte en gros trou si le marchĂ© continue contre toi. Dimensionne toujours en imaginant le pire scĂ©nario — voir le post sur le position sizing.

📈 Configuration graphique

Affiche tes indicateurs dans FreqUI :

plot_config = {
    "main_plot": {
        "EMA": {"color": "#ffffff", "type": "line"},
        "VWAP_low": {"color": "#53c3eb", "type": "line"},
    },
    "subplots": {
        "CCI": {
            "CCI": {"color": "#ffffff", "type": "line"},
        }
    }
}

Résultat dans FreqUI :

image


🔧 Bonnes pratiques

✅ À faire

  • Nommage cohĂ©rent : nom de classe = nom dans la config, en minuscules avec underscores (ma_super_strategie)

  • Logs pour debug : un simple print(dataframe) dans populate_indicators() aide Ă©normĂ©ment Ă  comprendre ce qui se passe

  • Commentaires dĂ©taillĂ©s : explique ta logique, documente les paramĂštres

❌ Erreurs courantes

  • Nom de classe invalide : class Ma-Super-Strategie ❌ (tirets interdits) → class ma_super_strategie ✅

  • Oubli du volume > 0 dans les conditions (Ă©vite de trader sur des bougies sans volume)

  • StratĂ©gie en avance sur le futur (look-ahead bias) : ne te sers jamais d'une bougie non clĂŽturĂ©e. Voir le piĂšge du look-ahead bias


🎓 Progression recommandĂ©e

đŸ„‰ DĂ©butant : commence avec basic_emas, modifie les pĂ©riodes des EMA, ajoute un indicateur simple (RSI), teste en dry-run.

đŸ„ˆ IntermĂ©diaire : customise avec des callbacks, implĂ©mente des paramĂštres optimisables.

đŸ„‡ Avancé : cherche une config rentable avec ces paramĂštres et callbacks, implĂ©mente des sorties custom, optimise avec hyperopt, puis re-teste en dry-run.

Quand ton dry-run est validĂ© et que tu es prĂȘt Ă  encaisser le pire scĂ©nario de ta stratĂ©gie → tu peux passer en live.

💡 Mon approche perso : je valide en live avec un petit capital plutît que de sur-croire à un backtest. Un backtest ne vaut rien, ce qui vaut quelque chose c'est le live — mais commence toujours en dry-run, puis avec une mise minuscule.


💡 Ressources complĂ©mentaires

🎯 Conclusion

Les fichiers de stratégie sont l'ùme de tes bots Freqtrade. Tu as maintenant les clés pour comprendre leur structure, créer tes conditions, optimiser les performances et gérer le risque.

Prochaine étape : valide tes stratégies en dry-run, puis passe en live avec une petite mise.


☕ Si ce guide t'a aidĂ©, tu peux me soutenir ici : buymeacoffee.com/freqtrade_france.

💡 Si tu trades sur Hyperliquid, tu peux passer par mon lien d'affiliation (rĂ©duction sur les frais).

⚠ Je ne suis pas conseiller financier. Ce contenu reflĂšte mon expĂ©rience personnelle. Les performances passĂ©es ne prĂ©jugent pas des performances futures. Il existe d'autres mĂ©thodes tout aussi valables.

— Mouton 🐑

Ti piace questo post?

Offri un caffĂš a Freqtrade France

Altro da Freqtrade France