Dimensionnement, marge et levier

Le moteur applique une logique explicite pour le dimensionnement des positions, la marge et le levier. Il ajoute aussi une possibilité d’ajustement du sizing pour l’optimisation et le grid search, sans changer ce fonctionnement de base.

Les repères utiles

Avant de regarder le calcul d’une entrée, quelques notions reviennent tout au long de cette page.

  • Equité : c’est la valeur totale du portefeuille à un instant donné. Elle comprend le cash disponible, la marge immobilisée lorsqu’elle est active, ainsi que le gain ou la perte latente des positions ouvertes.
  • Cash disponible : c’est la part de l’équité qui n’est pas immobilisée. Elle sert à financer de nouvelles entrées et à payer les frais.
  • Valeur notionnelle : c’est la taille économique d’une position. En pratique, elle correspond à prix × quantité.
  • Marge requise : c’est la part d’équité immobilisée pour maintenir une position ouverte.
  • Levier théorique maximal : c’est le levier implicite permis par le pourcentage de marge configuré.
  • Prix de sizing : c’est le prix utilisé pour calculer la quantité ou le budget d’une entrée.
  • Prix d’exécution : c’est le prix réel auquel l’ordre est finalement exécuté.
  • Liquidation par marge : c’est la réduction automatique d’une position lorsque l’équité devient inférieure ou égale à la marge requise.

La distinction entre prix de sizing et prix d’exécution est importante. Un ordre peut être dimensionné sur un prix, puis exécuté plus tard à un autre prix. C’est toujours le prix réel d’exécution qui sert ensuite au notionnel exécuté, aux frais débités et au contrôle final de solvabilité.

Comment est calculée la taille d’une entrée

Quand une entrée est créée, le calcul se fait en deux temps. Le moteur détermine d’abord le mode de sizing à utiliser, puis il calcule la quantité à partir du prix retenu pour dimensionner l’ordre.

Le mode global se définit dans le bloc [backtest] avec default_qty_type. Trois modes existent. percent_of_equity exprime la taille comme un pourcentage d’équité. fixed exprime la taille comme une quantité absolue d’actif. cash exprime la taille comme un budget monétaire.

Vous pouvez remplacer ce sizing global directement dans [[entry]] ou [[order]]. Si qty est défini, cette quantité locale est utilisée. Si qty_percent est défini, ce pourcentage local est utilisé. Ces deux clefs sont exclusives. Si aucune des deux n’est présente, le moteur revient au sizing global du bloc [backtest].

Le sizing défini dans [backtest]

default_qty_type choisit le mode de sizing. default_qty_value fournit ensuite la valeur utilisée par ce mode.

Si default_qty_type n’est pas renseigné, le mode percent_of_equity est utilisé. Dans ce mode, si default_qty_value n’est pas non plus renseigné, le moteur part d’une base de 100. Les multiplicateurs long_size_multiplier et short_size_multiplier valent 1 tant que vous ne les modifiez pas.

[backtest]
default_qty_type  = "percent_of_equity"
default_qty_value = 100

En mode fixed ou cash, default_qty_value n’a pas de valeur par défaut exploitable. Il faut alors soit un default_qty_value, soit un sizing local défini sur l’ordre avec qty ou qty_percent.

Le mode percent_of_equity

Quand default_qty_type = "percent_of_equity", default_qty_value représente une base commune en pourcentage de l’équité.

[backtest]
default_qty_type  = "percent_of_equity"
default_qty_value = 40

Avec cette configuration, l’entrée visée est proche de 40 % de l’équité.

Vous pouvez ensuite ajuster cette base séparément pour le long et pour le short avec long_size_multiplier et short_size_multiplier.

[backtest]
default_qty_type      = "percent_of_equity"
default_qty_value     = 40
long_size_multiplier  = 1.5
short_size_multiplier = 0.5

Ici, la base est de 40 %. Le multiplicateur du côté concerné est ensuite appliqué. Pour le long, cela donne 40 × 1.5 = 60. Pour le short, cela donne 40 × 0.5 = 20.

L’exposition visée devient donc proche de 60 % de l’équité sur les longs et de 20 % sur les shorts.

Ces multiplicateurs ne changent ni la marge requise ni le levier théorique maximal. Ils servent uniquement à ajuster le sizing global, et seulement lorsque ce sizing global est en percent_of_equity et qu’aucun ordre ne définit déjà qty ou qty_percent.

Dans ce mode, le calcul part toujours de l’équité totale, et pas seulement du cash disponible. La quantité réellement ouverte peut être légèrement inférieure à la cible affichée en pourcentage, car le moteur tient compte des frais puis ajuste le résultat à la taille minimale de contrat.

Par exemple, default_qty_value = 60 fixe une base d’exposition à 60 % de l’équité. Le multiplicateur long 1.5 porte alors l’exposition long autour de 0,9x, tandis que le short reste autour de 0,6x. Les marges définissent surtout la marge requise et le plafond théorique de levier, pas la taille d’entrée.

[backtest]
symbol               = "BINANCE:BTCUSDT"
timeframe            = "240"
start_date           = 2024-01-01
end_date             = 2025-01-01
initial_capital      = 1000
default_qty_value    = 60
margin_long          = 50
margin_short         = 100
long_size_multiplier = 1.5

Cette logique se prête aussi au grid search. L’exemple suivant fait varier l’exposition long tout en gardant une marge long fixe à 50.

[backtest]
symbol                     = "BINANCE:BTCUSDT"
timeframe                  = "240"
start_date                 = 2024-01-01
end_date                   = 2025-01-01
initial_capital            = 1000
margin_long                = 50
margin_short               = 100
default_qty_value.start    = 20
default_qty_value.stop     = 80
default_qty_value.step     = 10
long_size_multiplier.start = 1.0
long_size_multiplier.stop  = 2.0
long_size_multiplier.step  = 0.25

Le mode fixed

Quand default_qty_type = "fixed", default_qty_value représente une quantité absolue d’actif.

[backtest]
default_qty_type  = "fixed"
default_qty_value = 0.01

[[entry]]
id       = "buy"
order_id = "main"

Avec cette configuration, l’entrée visée est de 0.01 unité d’actif.

Si un ordre ne définit pas de sizing local, le moteur utilise default_qty_value. Si cette valeur est absente, une entrée qui repose sur le mode global fixed ne peut pas être dimensionnée.

Dans ce mode, long_size_multiplier et short_size_multiplier n’ont aucun effet. La taille demandée est déjà une quantité absolue.

Le mode cash

Quand default_qty_type = "cash", default_qty_value représente un budget monétaire nominal.

[backtest]
default_qty_type  = "cash"
default_qty_value = 500

[[entry]]
id       = "buy"
order_id = "main"

Avec cette configuration, 500 unités de devise quote sont d’abord converties en quantité d’actif à partir du prix utilisé pour dimensionner l’ordre. Cette quantité est ensuite exécutée au prix réel de fill.

Dans ce mode, long_size_multiplier et short_size_multiplier n’ont pas d’effet. Le sizing global repose uniquement sur le budget demandé.

Le budget reste un budget nominal. Si le prix de fill diffère du prix utilisé pour calculer la quantité, le notionnel réellement exécuté peut s’écarter du budget initial. Si default_qty_value est absent, une entrée qui repose sur le mode global cash ne peut pas être dimensionnée.

qty et qty_percent dans [[entry]] et [[order]]

Dans [[entry]] et [[order]], qty désigne toujours une quantité absolue d’actif. qty_percent désigne toujours un pourcentage d’équité, jamais un pourcentage de position.

Pour [[entry]] et [[order]], la documentation ne borne pas qty_percent à 100. Une valeur supérieure peut viser une exposition plus forte, sous réserve des règles de marge et de l’acceptation finale de l’ordre.

Ces deux clefs sont exclusives. Vous ne pouvez pas les utiliser ensemble dans le même bloc.

[backtest]
default_qty_type     = "percent_of_equity"
default_qty_value    = 40
long_size_multiplier = 1.5

[[entry]]
id       = "buy_fixed_qty"
order_id = "main"
qty      = 0.25

[[entry]]
id          = "buy_local_pct"
order_id    = "main"
qty_percent = 25

Dans cet exemple, le premier ordre ouvre 0.25 unité d’actif. Le second vise 25 % de l’équité. Aucun des deux n’utilise le sizing global défini dans [backtest].

Quand qty est présent, l’ordre est traité comme une entrée à quantité fixe. Quand qty_percent est présent, l’ordre est traité comme une entrée en percent_of_equity, même si le mode global est fixed ou cash.

Autrement dit, qty et qty_percent remplacent le sizing global. Ils ne le complètent pas.

Dans [[close]] et [[exit]], qty_percent n’a plus le même sens. Il représente alors un pourcentage de la position déjà ouverte. Si une position vaut 2 BTC, qty_percent = 25 vise une sortie d’environ 0,5 BTC, avant arrondis.

La différence entre ces deux blocs tient à la manière dont le moteur résout qty et qty_percent. Dans [[exit]], les deux clefs restent exclusives. Dans [[close]], elles peuvent coexister. Si qty s’évalue vers une valeur strictement positive, qty est utilisé. Sinon, le moteur utilise qty_percent.

Prix de sizing et prix d’exécution

La quantité n’est pas toujours calculée avec le même prix que celui du fill. Cette différence concerne surtout les cas où la taille dépend d’un budget ou d’un pourcentage d’équité.

Pour un ordre market exécuté à la clôture, l’ordre est dimensionné et exécuté sur le même prix. Pour un ordre market exécuté à l’ouverture suivante, le moteur conserve le prix de sizing de la bougie de signal puis exécute l’ordre au prix d’ouverture. Pour un ordre limit, le prix de sizing est le prix limite. Pour un ordre stop market, c’est le prix stop. Pour un ordre stop-limit, c’est le prix limite.

Le notionnel final peut donc différer de l’estimation initiale quand le prix de sizing diffère du prix réel d’exécution.

[backtest]
default_qty_type  = "cash"
default_qty_value = 500

[[entry]]
id       = "buy_limit"
order_id = "main"
limit    = "close * 0.99"

Dans cet exemple, la quantité est calculée à partir du prix limite, puis exécutée lorsque l’ordre est rempli. Si le fill final se fait à un autre prix, le notionnel exécuté, les frais débités et le contrôle final de solvabilité suivent le prix réel d’exécution.

Ce que la marge change, et ce qu’elle ne change pas

La marge ne définit pas la taille demandée par l’entrée.

La taille d’une entrée dépend d’abord de default_qty_type, de default_qty_value, des multiplicateurs de côté et, si besoin, de qty ou de qty_percent.

margin_long et margin_short interviennent ensuite. Ces paramètres définissent la marge requise, le levier théorique maximal et les conditions de liquidation.

Cette séparation est importante pour bien lire les résultats. Une marge plus faible ne crée pas, à elle seule, une quantité d’entrée plus grande. Elle change surtout le cadre de solvabilité dans lequel cette entrée est acceptée, maintenue ou liquidée.

Comment la marge est appliquée

margin_long et margin_short

margin_long et margin_short sont des pourcentages de marge exprimés directement en pourcentage dans le fichier TOML.

margin_long = 50 signifie 50 % de marge. margin_long = 0.5 signifie 0,5 % de marge.

Ces paramètres ont deux effets. Ils déterminent d’abord la marge requise des positions ouvertes. Ils fixent ensuite le levier théorique maximal.

La marge requise se calcule à partir de la valeur notionnelle :

  • pour un long, valeur notionnelle × (margin_long / 100)
  • pour un short, valeur notionnelle × (margin_short / 100).

Le levier théorique maximal se lit de la même manière :

  • pour un long, 100 / margin_long
  • pour un short, 100 / margin_short.

Cela signifie par exemple qu’une marge de 100 correspond à un cadre théorique proche de 1x, qu’une marge de 50 correspond à un cadre proche de 2x, qu’une marge de 25 correspond à un cadre proche de 4x, et qu’une marge de 0.5 correspond à un cadre proche de 200x.

L’exemple suivant vise une exposition long proche de 1x avec une marge long à 50 % et une marge short à 100 %. Ici, margin_long = 50 ne double pas la taille de l’entrée. Ce paramètre réduit surtout la marge requise à environ la moitié du notionnel et fixe un plafond théorique long proche de 2x.

[backtest]
symbol          = "BINANCE:BTCUSDT"
timeframe       = "240"
start_date      = 2024-01-01
end_date        = 2025-01-01
initial_capital = 1000
margin_long     = 50
margin_short    = 100

Quand une marge vaut 0, la logique de marge est désactivée sur le côté concerné. La liquidation par marge ne s’applique alors plus sur ce côté.

Si une seule marge vaut 0, seul ce côté fonctionne hors marge. L’autre côté conserve sa propre logique. Le comportement global peut donc rester mixte.

Si margin_long = 0 et margin_short = 0, le moteur fonctionne sans marge des deux côtés. Dans ce cas, il n’immobilise pas de marge et n’applique pas de liquidation par marge.

Par exemple :

[backtest]
symbol          = "BINANCE:BTCUSDT"
timeframe       = "240"
start_date      = 2024-01-01
end_date        = 2025-01-01
initial_capital = 1000
margin_long     = 0
margin_short    = 0

Comment la solvabilité d’une entrée est vérifiée

Une fois la taille de l’ordre calculée, le moteur vérifie si l’entrée reste solvable.

Quand margin_long = 0 et margin_short = 0, il n’y a plus de contrôle de marge sur l’entrée. Le sizing reste piloté par default_qty_type, default_qty_value, long_size_multiplier, short_size_multiplier, qty et qty_percent.

Quand la marge est active, le moteur compare l’équité à la marge requise après l’entrée. Avec une marge inférieure à 100, ce contrôle se lit directement comme un contrôle de marge et n’ajoute pas les frais à cette comparaison. Avec une marge égale à 100, la décision finale de solvabilité peut être prise après le fill, à partir du prix réellement exécuté et des frais réellement débités.

C’est pour cette raison qu’une entrée peut être exécutée puis liquidée immédiatement sur la même bougie. La quantité n’est pas réduite automatiquement pour faire entrer l’ordre dans la marge.

Le cas percent_of_equity demande ici une lecture précise. default_qty_value et les multiplicateurs définissent l’exposition visée. margin_long et margin_short définissent surtout la marge requise et le levier théorique maximal. Une marge plus faible n’augmente donc pas, à elle seule, la quantité d’entrée.

Comment une liquidation par marge se produit

Une liquidation par marge se produit lorsque l’équité devient inférieure ou égale à la marge requise. Cette règle n’existe que sur les côtés où la marge est active, c’est-à-dire lorsque margin_long > 0 pour les positions long et margin_short > 0 pour les positions short.

Le contrôle ne se fait pas seulement à la clôture de la bougie. Le moteur vérifie d’abord l’ouverture, puis l’extrême défavorable de la bougie : le plus bas pour un long et le plus haut pour un short. Une liquidation peut donc se produire en cours de bougie, même si le prix revient ensuite avant la clôture.

Quand ce seuil est franchi, une partie de la position peut être réduite pour rétablir la marge requise. Selon les arrondis et la taille minimale de contrat, cette réduction partielle peut devenir une fermeture complète.

Avec margin_long = 100 ou margin_short = 100, le cadre reste théoriquement proche de 1x, mais un margin call immédiat peut tout de même apparaître. Ce cas se rencontre notamment lorsqu’une entrée vise presque 100 % de l’équité, puis que le prix réel d’exécution et les frais déplacent l’équilibre au moment du contrôle final. Dans cette situation, le prix de liquidation estimé peut rester non défini (NaN) tout en laissant exister un risque de liquidation.

Exemple concret de marge, levier et liquidation

Prenons un cas simple, volontairement sans frais ni arrondis pour garder les chiffres lisibles. Supposons une équité initiale de 1 000 USDT et une exposition long de 4 000 USDT sur Bitcoin, avec margin_long = 20. Cette marge de 20 % correspond à un cadre théorique proche de 5x, car 100 / 20 = 5. Au moment de l’entrée, la marge requise vaut donc 4 000 × 20 % = 800 USDT.

L’exposition ouverte vaut 4 000 USDT, mais toute cette valeur n’a pas besoin d’être couverte par l’équité pour maintenir la position. Tant que l’équité reste strictement supérieure à la marge requise, la position peut rester ouverte.

Si le prix du Bitcoin monte de 2 %, le gain latent est de 80 USDT (4 000 × 2 %). L’équité passe alors de 1 000 à 1 080 USDT, tandis que la valeur notionnelle de la position devient proche de 4 080 USDT. La marge requise devient donc 816 USDT (4 080 × 20 %). L’équité reste au-dessus de la marge requise.

Si le prix baisse de 2 %, la perte latente est de 80 USDT (4 000 × 2 %). L’équité descend à 920 USDT, tandis que la valeur notionnelle tombe à environ 3 920 USDT. La marge requise devient alors 784 USDT (3 920 × 20 %). Ici encore, l’équité reste au-dessus de la marge requise.

Le point critique apparaît quand l’équité rejoint la marge requise. Dans cet exemple simplifié, cela se produit autour d’une baisse de 6,25 %, car la perte vaut alors 250 USDT (4 000 × 6,25 %). L’équité tombe donc à 750 USDT (1 000 - 250) et la valeur notionnelle à 3 750 USDT (4 000 - 250). La marge requise vaut alors elle aussi 750 USDT (3 750 × 20 %). A ce niveau, le seuil où une liquidation par marge peut commencer est atteint.

Cet exemple illustre deux points. D’abord, une marge de 20 % ne force pas une position à 5x : elle définit surtout la marge requise et le levier théorique maximal. Ensuite, la liquidation dépend de la relation entre l’équité et la marge requise, pas seulement du pourcentage de baisse du prix.