Aller au contenu

Bases de Python pour le trading algorithmique

Les traders utilisent Python pour créer des bots de trading, des indicateurs techniques, des plug-ins et d'autres outils pour faciliter le trading algorithmique ou les opérations manuelles, et cet article explique les bases du codage dans Python.

Python est un langage de programmation populaire, de haut niveau et à usage général utilisé pour le développement web, la science des données, l'automatisation et plus récemment, le trading algorithmique dans cTrader. Que vous créiez des bots de trading simples basés sur des signaux ou des stratégies complexes multi-indicateurs, Python rend le processus plus rapide, plus efficace et plus facile à gérer.

Algorithmes Python en une minute !

  • Profitez d'une prise en charge intégrée de Python et d'un accès transparent à l'API cTrader Algo, cTrader étant la seule plateforme majeure offrant une intégration native prête à l'emploi. Aucun plug-in, adaptateur ou configuration complexe requis.
  • Concentrez-vous davantage sur ce que votre stratégie doit accomplir, car la logique devient plus facile à assembler. Profitez d'une itération rapide, de composants réutilisables et de performances évolutives.
  • Développez rapidement, déboguez intelligemment et collaborez sans barrières. Votre code sera toujours adaptable, fiable et maintenable.
  • Accédez et bénéficiez de milliers de bibliothèques gratuites/open-source, de tutoriels approfondis, d'exemples de code et d'un fort soutien communautaire dans le monde entier.

Variables et types de données

Dans Python, les variables sont typées dynamiquement, ce qui signifie que vous n'avez pas à déclarer explicitement leurs types de données. L'interpréteur détermine le type lors de l'exécution en fonction de la valeur attribuée.

Vous créez des variables en attribuant simplement une valeur à l'une d'elles :

1
2
3
4
price = 105.50         # float
enabled = True         # boolean
symbol = "EURUSD"      # string
orders = []            # empty list

Types de données courants

Type Description Exemple
int Nombres entiers 5, 100, api.StepPips
float Nombres décimaux 1.1234, 100.5, api.Symbol.Ask
bool Valeurs booléennes True, False
str Chaînes de caractères "EURUSD", api.Label
list Collection d'éléments [1, 2, 3], [p for p in api.Positions]

Dans les algorithmes cTrader, les variables stockent des données importantes liées aux informations de marché, aux décisions de trading et à la configuration du cBot. Ces variables représentent souvent des volumes de trading calculés, des valeurs d'indicateur, des drapeaux booléens, des noms de symboles, des étiquettes et plus encore.

Voici un exemple :

1
2
3
4
class GridcBot():
    def on_start(self):
        self.volumeInUnits = api.Symbol.QuantityToVolumeInUnits(api.VolumeInLots)
        self.enoughMoney = True

Dans ce cas, self.volumeInUnits est un float utilisé pour passer des ordres, tandis que self.enoughMoney est un bool contrôlant s'il faut continuer à ouvrir des positions dans une stratégie de grille.

Classes et objets

Python est un langage de programmation orientée objet (POO) qui fournit des outils pour organiser le code à travers des classes et des objets qui modélisent des entités et des comportements du monde réel. Une classe définit un modèle pour créer des objets, qui sont des instances de cette classe avec leurs propres données et comportements uniques. Les concepts clés sont décrits ci-dessous :

  • Une classe est définie à l'aide du mot-clé class.
  • La méthode __init__ (constructeur) est couramment utilisée pour initialiser l'état de l'objet.
  • Les méthodes d'instance définissent le comportement.
  • Les attributs contiennent des données spécifiques à chaque objet.

Par exemple, vous pouvez créer une classe Car :

1
2
3
4
5
6
7
class Car:
    def __init__(self, color, speed):
        self.color = color
        self.speed = speed

    def accelerate(self):
        self.speed += 10

Ensuite, vous pouvez créer des objets de la classe Car et interagir avec eux :

1
2
3
my_car = Car("red", 50)
my_car.accelerate()
print(my_car.speed)  # This returns 60

Dans le contexte de cTrader

Dans cTrader, les cBots, indicateurs et plug-ins sont implémentés sous forme de classes qui encapsulent toute leur logique. Les algorithmes interagissent avec la plateforme cTrader à l'aide d'un objet spécial api qui donne accès aux symboles, graphiques, ordres, indicateurs et plus encore.

Vous pouvez créer une classe SimplecBot dans cTrader de cette manière :

1
2
3
class SimplecBot():
    def on_start(self):
        api.Print("cBot started")

La classe SimplecBot comprend une seule méthode (on_start(self)) qui s'exécute une fois au démarrage du bot de trading. La fonction api.Print écrit un message dans le journal, démontrant comment les méthodes d'objet encapsulent le comportement de trading.

Chaque instance d'une classe représente effectivement un algorithme de trading autonome qui gère son propre cycle de vie et ses décisions de trading en utilisant les principes de la POO. Dans un cBot cTrader standard, une classe peut stocker des données d'état internes, telles que le volume, les résultats d'indicateurs, etc., et utiliser plusieurs méthodes pour gérer la logique de trading.

Considérez cet exemple de croisement MACD qui initialise l'indicateur MACD au démarrage et place un ordre d'achat lorsqu'un signal de croisement MACD se produit :

1
2
3
4
5
6
7
class MACDCrossover():
    def on_start(self):
        self.macd = api.Indicators.MacdCrossOver(api.Source, api.LongCycle, api.ShortCycle, api.SignalPeriods)

    def on_bar_closed(self):
        if self.macd.MACD.Last(0) > self.macd.Signal.Last(0):
            api.ExecuteMarketOrder(TradeType.Buy, api.SymbolName, volume)

Le mot-clé self

À l'intérieur d'une classe, self fait référence à l'instance de la classe et donne accès à ses variables et méthodes. self est utilisé pour :

  • Stocker des données telles que les résultats d'indicateurs, les volumes de positions et les valeurs de configuration.
  • Suivre l'état interne à travers différents appels de méthodes tels que on_start(), on_tick() ou on_bar_closed().

Cet exemple démontre comment self est utilisé :

1
2
3
4
class MACDCrossover():
    def on_start(self):
        self.volume = api.Symbol.QuantityToVolumeInUnits(api.VolumeInLots)
        self.macd = api.Indicators.MacdCrossOver(api.Source, api.LongCycle, api.ShortCycle, api.SignalPeriods)

Dans ce cas, self.volume stocke le volume calculé à utiliser pour les trades, tandis que self.macd stocke l'objet indicateur MACD, permettant d'accéder à ses valeurs tout au long du cycle de vie du cBot. Sans self, ces variables seraient locales à on_start() et inaccessibles dans on_bar_closed() où les décisions de trading peuvent être prises.

Méthodes et fonctions

Les fonctions sont des blocs de code réutilisables utilisés pour effectuer une tâche spécifique. Lorsque les fonctions sont définies à l'intérieur d'une classe, elles sont appelées méthodes, et elles opèrent sur l'état interne de la classe via le paramètre self. Dans le contexte du trading algorithmique avec cTrader, les méthodes définissent comment votre algorithme de trading se comporte dans différentes conditions, telles que les ticks de marché, les clôtures de barres ou l'initialisation.

La plateforme appelle automatiquement des méthodes spéciales (appelées gestionnaires d'événements) aux moments appropriés, telles que :

on_start(self) – appelée une fois lorsque le bot est initialisé.

1
2
def on_start(self):
    api.Print("cBot started")

on_tick(self) – appelée à chaque tick de marché entrant.

1
2
def on_tick(self):
    api.Print("Tick received")

on_bar_closed(self) – appelée lorsqu'une nouvelle bougie/barre se ferme (idéale pour les stratégies basées sur le temps).

1
2
3
4
def on_bar_closed(self):
    if Functions.HasCrossedAbove(...):
        self.close_positions(TradeType.Sell)
        api.ExecuteMarketOrder(...)

Méthodes personnalisées ou auxiliaires

Pour éviter de réécrire et de dupliquer le code, vous pouvez définir vos propres méthodes. Ces méthodes personnalisées doivent encapsuler la logique commune que votre robot de trading peut réutiliser.

La méthode ci-dessous renvoie toutes les positions ouvertes associées au label du cBot actuel, facilitant le suivi ou la fermeture cohérente des positions.

1
2
def get_bot_positions(self):
    return api.Positions.FindAll(api.Label)

Considérez une autre méthode qui calcule la distance en pips entre le cours actuel et l'entrée de la position, ce qui est essentiel pour les décisions dans les stratégies basées sur la grille.

1
2
def get_distance_in_pips(self, position):
    return (position.EntryPrice - api.Symbol.Ask if position.TradeType == TradeType.Buy else api.Symbol.Bid - position.EntryPrice) / api.Symbol.PipSize

Une fois qu'une méthode a été définie, vous pouvez l'appeler depuis la même classe en utilisant le mot-clé self. Cette approche vous permet de réutiliser la logique n'importe où dans votre bot sans dupliquer le code.

Par exemple, après avoir défini get_bot_positions(), vous pouvez l'appeler à l'intérieur d'une autre méthode pour fermer toutes les positions :

1
2
3
4
def close_positions(self, tradeType):
    for position in self.get_bot_positions():   # Helper method call
        if position.TradeType == tradeType:
            api.ClosePosition(position)

Ici, self.get_bot_positions() invoque la méthode auxiliaire, renvoyant uniquement les positions liées au label du bot actuel.

De même, la méthode get_distance_in_pips() peut être réutilisée pour prendre des décisions de trading. Par exemple, dans une stratégie de grille, vous pouvez vouloir ouvrir un nouvel ordre uniquement si le marché s'est suffisamment éloigné de la dernière position :

1
2
3
4
5
6
7
def on_tick(self):
    grid_positions = self.get_bot_positions()
    if len(grid_positions) > 0:
        position_with_highest_pips = sorted(grid_positions, key=lambda pos: pos.Pips, reverse=True)[0]
        distance = self.get_distance_in_pips(position_with_highest_pips)  # Helper method call
        if distance >= api.StepPips:
            self.open_position()

Autres concepts

Espaces de noms et importations

Les espaces de noms en Python aident à organiser le code et à éviter les collisions de noms en regroupant les identifiants associés (variables, fonctions, classes). Les importations vous permettent d'intégrer des modules, bibliothèques ou packages externes pour étendre les fonctionnalités au-delà des fonctionnalités standard de Python.

En Python, vous pouvez importer des modules standard ou des packages personnalisés :

1
2
import math
print(math.sqrt(16))  # 4.0

Vous pouvez également importer des fonctions ou des classes spécifiques :

1
2
from math import sqrt
print(sqrt(25))  # 5.0

Lors de l'écriture d'algorithmes pour cTrader en Python, les importations fonctionnent un peu différemment car cTrader s'intègre avec l'API cTrader Algo basée sur .NET. Cette configuration implique un pont qui permet au code Python d'interagir avec les assemblages .NET, ce qui explique pourquoi un cBot typique commence par :

1
2
3
4
5
6
7
8
9
import clr

clr.AddReference("cAlgo.API")

# Import cAlgo API types
from cAlgo.API import *

# Import trading wrapper functions
from robot_wrapper import *

Où :

  • import clr charge le Common Language Runtime (CLR), permettant à Python d'interagir avec les bibliothèques .NET.
  • clr.AddReference("cAlgo.API") référence l'assemblage de l'API cAlgo, qui contient toutes les classes, méthodes et objets liés au trading.
  • from cAlgo.API import * importe tous les types de l'API (par exemple, TradeType, Color, Position).
  • from robot_wrapper import * charge les fonctions auxiliaires et les wrappers qui simplifient les opérations de trading courantes.

Le système d'espaces de noms garantit que :

  • Les noms intégrés de Python (tels que print) évitent les conflits avec les noms de cTrader (tels que api.Print).
  • Tous les objets spécifiques au trading (api.Symbol, api.Indicators, api.ExecuteMarketOrder) proviennent de l'espace de noms de l'API cTrader Algo, les gardant organisés et distincts.
  • Vous pouvez étendre les fonctionnalités en important vos propres packages personnalisés pour travailler aux côtés des types intégrés de cTrader.

Boucles

Les boucles vous permettent de répéter des opérations sur une séquence de valeurs ou jusqu'à ce qu'une condition soit remplie. Dans le trading algorithmique, les boucles sont souvent utilisées pour traiter les positions (par exemple, fermer, filtrer ou mettre à jour), vérifier les conditions sur plusieurs actifs ou indicateurs, implémenter des stratégies de grille ou de martingale où le placement répété d'ordres est requis, et plus encore.

En Python, l'itération est simple et prend en charge à la fois les boucles for (pour itérer à travers des collections) et les boucles while (pour répéter jusqu'à ce qu'une condition change).

Vous pouvez écrire une boucle pour fermer les positions :

1
2
3
4
def close_positions(self, tradeType):
    for position in self.get_bot_positions():
        if position.TradeType == tradeType:
            api.ClosePosition(position)

Dans ce cas :

  • self.get_bot_positions() renvoie toutes les positions appartenant au cBot actuel.
  • La boucle for parcourt chaque position.
  • Les positions correspondantes sont fermées avec api.ClosePosition.

Considérez une boucle différente qui itère sur toutes les positions de grille ouvertes, les ferme une par une et vérifie de manière récursive s'il reste des positions (sécurité en cas de fermetures partielles).

1
2
3
4
5
6
def close_grid_positions(self):
    for position in self.get_grid_positions():
        position.Close()

    if len(self.get_grid_positions()) > 0:
        self.close_grid_positions()

Instructions conditionnelles

Les instructions conditionnelles contrôlent le flux d'exécution en Python en permettant à votre algorithme de prendre des décisions basées sur des expressions logiques. Les conditionnelles constituent l'épine dorsale de la logique de stratégie, déterminant quand entrer, sortir ou gérer des positions.

Python prend en charge trois constructions conditionnelles principales :

  • if – exécute le code lorsque la condition est vraie.
  • elif (else if) – vérifie une autre condition si la première était fausse.
  • else – fournit une solution de repli lorsqu'aucune condition n'est remplie.

Considérez les conditions de croisement MACD exprimées dans ce code :

1
2
3
4
if self.macd.MACD.Last(0) > self.macd.Signal.Last(0):
    api.ExecuteMarketOrder(TradeType.Buy, api.SymbolName, self.volumeInUnits, api.Label, api.StopLossInPips, api.TakeProfitInPips)
elif self.macd.MACD.Last(0) < self.macd.Signal.Last(0):
    api.ExecuteMarketOrder(TradeType.Sell, api.SymbolName, self.volumeInUnits, api.Label, api.StopLossInPips, api.TakeProfitInPips)

Cette structure :

  • Place un ordre d'achat lorsque la ligne MACD est au-dessus de la ligne de signal.
  • Place un ordre de vente lorsque la ligne MACD est en dessous de la ligne de signal.
  • Ignore le trading s'il n'y a pas de signal clair.

Listes

Python fournit des outils puissants et concis pour travailler avec des collections de données, telles que les trades, les prix ou les indicateurs. Deux des plus courants sont les compréhensions de listes et les fonctions d'agrégation intégrées telles que sum().

Par exemple, vous pouvez écrire un code qui itère sur numbers et ne conserve que les nombres pairs de cette manière :

1
2
numbers = [1, 2, 3, 4, 5]
squares = [n**2 for n in numbers if n % 2 == 0]

Vous pouvez utiliser une compréhension de liste pour sélectionner uniquement les positions pour le symbole actuel, produisant une liste filtrée qui est utile dans les environnements multi-symboles.

1
positions = [p for p in api.Positions if p.SymbolName == api.SymbolName]

L'agrégation du profit ou du volume à l'aide de sum() est une approche standard dans les stratégies de trading en grille pour déterminer quand le profit total atteint l'objectif.

1
net_profit_sum = sum([p.NetProfit for p in grid_positions])

Vous pouvez combiner le filtrage et l'agrégation de cette manière :

1
2
buy_positions = [p for p in api.Positions if p.TradeType == TradeType.Buy]
avg_buy_price = sum([p.EntryPrice * p.VolumeInUnits for p in buy_positions]) / sum([p.VolumeInUnits for p in buy_positions])

Opérations synchrones et asynchrones

Python suit un modèle d'exécution synchrone par défaut. Les méthodes de cycle de vie de votre algorithme, telles que on_start, on_tick et on_bar_closed, sont déclenchées par la boucle d'événements de la plateforme, une à la fois.

Voici une représentation de base d'un flux de trading synchrone :

graph TD
    A(Détecter un signal) ==> B(Exécuter un ordre)
    B ==> C(La position<br>atteint le TP/SL)
    C ==> D(Clôturer la<br>position)
    D ==> A

Dans les flux synchrones, toute opération de longue durée peut bloquer le thread, retardant potentiellement l'événement suivant. Cependant, dans les stratégies réelles, les algorithmes initient généralement des actions non bloquantes, telles que la soumission d'un ordre ou la définition de stop-loss et de take-profit, puis ils attendent les événements futurs (tels qu'un nouveau tick ou une nouvelle barre) pour traiter les résultats. Cette configuration aboutit à une orchestration de type asynchrone, même si les constructions async/await de Python ne sont pas utilisées.

Remarque

Pour la plupart des algorithmes de trading, la programmation asynchrone explicite n'est pas nécessaire.

cTrader gère la concurrence en sérialisant vos rappels basés sur les événements. Chaque méthode est invoquée indépendamment et jamais en parallèle, éliminant le besoin de protections multithreading. Cette conception permet aux stratégies de répondre à plusieurs conditions de trading à la fois, dans un sens logique, sans blocage.

Dans l'exemple ci-dessous, la stratégie évalue les signaux d'achat et de vente de manière indépendante et répond en conséquence, sans aucun chevauchement dans le contexte d'exécution.

graph TD
    A([Détecter un signal]) ==> B([Ouvrir un<br>ordre d'achat]) & C([Ouvrir un ordre de<br>vente de<br>couverture]) ==> D([Un ordre<br>atteint son<br>take profit ou<br>son stop loss])
    D ==> E([Clôturer la<br>position])
    E ==> A

Même si les décisions peuvent sembler être en parallèle, elles sont traitées séquentiellement par le système de mise en file d'attente interne de cTrader. L'architecture de la plateforme vise à prévenir les problèmes de concurrence tout en maintenant les robots de trading réactifs aux changements du marché.

Image title