Zum Inhalt

Python-Grundlagen für algorithmisches Handeln

Händler verwenden Python, um Handelsbots, technische Indikatoren, Plugins und andere Tools zu erstellen, die den algorithmischen Handel oder manuelle Operationen unterstützen, und dieser Artikel erklärt die Grundlagen der Programmierung in Python.

Python ist eine beliebte, hochrangige, universelle Programmiersprache, die für Webentwicklung, Datenwissenschaft, Automatisierung und neuerdings für algorithmisches Handeln in cTrader verwendet wird. Egal, ob Sie einfache signalbasierte Handelsbots oder komplexe Multi-Indikator-Strategien erstellen, Python macht den Prozess schneller, effizienter und einfacher zu verwalten.

Python-Algorithmen in einer Minute!

  • Erleben Sie integrierte Python-Unterstützung und nahtlosen Zugriff auf die cTrader Algo API, wobei cTrader die einzige große Plattform ist, die native Integration sofort einsatzbereit bietet. Keine Plugins, Adapter oder komplexe Setups erforderlich.
  • Konzentrieren Sie sich mehr darauf, was Ihre Strategie erreichen soll, da die Logik einfacher zusammenzustellen ist. Genießen Sie schnelle Iteration, wiederverwendbare Komponenten und skalierbare Performance.
  • Entwickeln Sie schnell, debuggen Sie intelligenter und arbeiten Sie ohne Barrieren zusammen. Ihr Code wird immer anpassungsfähig, zuverlässig und wartbar sein.
  • Greifen Sie auf Tausende von kostenlosen/Open-Source-Bibliotheken zu und profitieren Sie davon, sowie von ausführlichen Tutorials, Codebeispielen und starker Community-Unterstützung weltweit.

Variablen und Datentypen

In Python sind Variablen dynamisch typisiert, was bedeutet, dass Sie ihre Datentypen nicht explizit deklarieren müssen. Der Interpreter bestimmt den Typ zur Laufzeit basierend auf dem zugewiesenen Wert.

Sie erstellen Variablen, indem Sie ihnen einfach einen Wert zuweisen:

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

Gängige Datentypen

Typ Beschreibung Beispiel
int Ganze Zahlen 5, 100, api.StepPips
float Dezimalzahlen 1.1234, 100.5, api.Symbol.Ask
bool Boolesche Werte True, False
str Zeichenketten "EURUSD", api.Label
list Sammlung von Elementen [1, 2, 3], [p for p in api.Positions]

In cTrader-Algorithmen speichern Variablen wichtige Daten im Zusammenhang mit Marktinformationen, Handelsentscheidungen und cBot-Konfiguration. Diese Variablen repräsentieren oft berechnete Handelsvolumina, Indikatorwerte, boolesche Flags, Symbolnamen, Labels und mehr.

Hier ist ein Beispiel:

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

In diesem Fall ist self.volumeInUnits ein float, der zum Platzieren von Orders verwendet wird, während self.enoughMoney ein bool ist, der steuert, ob weiterhin Positionen in einer Grid-Strategie eröffnet werden sollen.

Klassen und Objekte

Python ist eine objektorientierte Programmiersprache (OOP), die Tools zur Organisation von Code durch Klassen und Objekte bereitstellt, die reale Entitäten und Verhaltensweisen modellieren. Eine Klasse definiert einen Bauplan für die Erstellung von Objekten, die Instanzen dieser Klasse mit ihren eigenen eindeutigen Daten und Verhaltensweisen sind. Die Schlüsselkonzepte sind nachfolgend aufgeführt:

  • Eine Klasse wird mit dem Schlüsselwort class definiert.
  • Die Methode __init__ (Konstruktor) wird häufig verwendet, um den Objektzustand zu initialisieren.
  • Instanzmethoden definieren Verhalten.
  • Attribute enthalten Daten, die für jedes Objekt spezifisch sind.

Sie können beispielsweise eine Klasse Car erstellen:

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

Dann können Sie Objekte der Klasse Car erstellen und mit ihnen interagieren:

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

Im cTrader-Kontext

In cTrader werden cBots, Indikatoren und Plugins als Klassen implementiert, die ihre gesamte Logik kapseln. Algos interagieren mit der cTrader-Plattform über ein spezielles api-Objekt, das Zugriff auf Symbole, Charts, Orders, Indikatoren und mehr bietet.

Sie können eine Klasse SimplecBot in cTrader auf diese Weise erstellen:

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

Die Klasse SimplecBot enthält eine einzelne Methode (on_start(self)), die einmal ausgeführt wird, wenn der Handelsbot startet. Die Funktion api.Print schreibt eine Nachricht in das Protokoll und demonstriert, wie Objektmethoden Handelsverhalten kapseln.

Jede Instanz einer Klasse repräsentiert effektiv einen eigenständigen Handelsalgorithmus, der seinen eigenen Lebenszyklus und Handelsentscheidungen mithilfe von OOP-Prinzipien verwaltet. In einem Standard-cTrader-cBot könnte eine Klasse interne Zustandsdaten wie Volumen, Indikatorergebnisse usw. speichern und mehrere Methoden verwenden, um die Handelslogik zu verwalten.

Betrachten Sie dieses MACD-Crossover-Beispiel, das den MACD-Indikator beim Start initialisiert und eine Kauforder platziert, wenn ein MACD-Crossover-Signal auftritt:

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)

Das Schlüsselwort self

Innerhalb einer Klasse bezieht sich self auf die Instanz der Klasse und bietet Zugriff auf ihre Variablen und Methoden. self wird verwendet, um:

  • Daten wie Indikatorergebnisse, Positionsvolumina und Konfigurationswerte zu speichern.
  • Internen Zustand über verschiedene Methodenaufrufe wie on_start(), on_tick() oder on_bar_closed() hinweg zu verfolgen.

Dieses Beispiel zeigt, wie self verwendet wird:

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)

In diesem Fall speichert self.volume das berechnete Volumen, das für Transaktionen verwendet werden soll, während self.macd das MACD-Indikatorobjekt speichert, was den Zugriff auf seine Werte während des gesamten Lebenszyklus des cBots ermöglicht. Ohne self wären diese Variablen lokal für on_start() und in on_bar_closed() nicht zugänglich, wo Handelsentscheidungen getroffen werden können.

Methoden und Funktionen

Funktionen sind Blöcke wiederverwendbaren Codes, die verwendet werden, um eine bestimmte Aufgabe auszuführen. Wenn Funktionen innerhalb einer Klasse definiert werden, werden sie Methoden genannt, und sie arbeiten mit dem internen Zustand der Klasse über den Parameter self. Im Kontext des algorithmischen Handelns mit cTrader definieren Methoden, wie sich Ihr Handelsalgorithmus unter verschiedenen Bedingungen verhält, wie z. B. Markt-Ticks, Balkenabschlüssen oder Initialisierung.

Die Plattform ruft automatisch spezielle Methoden (bekannt als Event-Handler) zu geeigneten Zeitpunkten auf, wie z. B.:

on_start(self) – wird einmal aufgerufen, wenn der Bot initialisiert wird.

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

on_tick(self) – wird bei jedem eingehenden Markt-Tick aufgerufen.

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

on_bar_closed(self) – wird aufgerufen, wenn eine neue Kerze/ein neuer Balken schließt (ideal für zeitbasierte Strategien).

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

Benutzerdefinierte oder Hilfsmethoden

Um das Umschreiben und Duplizieren von Code zu vermeiden, können Sie Ihre eigenen Methoden definieren. Solche benutzerdefinierten Methoden sollten gängige Logik kapseln, die Ihr Handelsroboter wiederverwenden kann.

Die folgende Methode gibt alle offenen Positionen zurück, die mit dem Label des aktuellen cBots verknüpft sind, was es einfach macht, Positionen konsistent zu verfolgen oder zu schließen.

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

Betrachten Sie eine weitere Methode, die den Abstand in Pips zwischen dem aktuellen Kurs und dem Einstieg der Position berechnet, was für Entscheidungen in Grid-basierten Strategien entscheidend ist.

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

Sobald eine Methode definiert wurde, können Sie sie innerhalb derselben Klasse mit dem Schlüsselwort self aufrufen. Dieser Ansatz ermöglicht es Ihnen, die Logik überall in Ihrem Bot wiederzuverwenden, ohne Code zu duplizieren.

Nachdem Sie beispielsweise get_bot_positions() definiert haben, können Sie es innerhalb einer anderen Methode aufrufen, um alle Positionen zu schließen:

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)

Hier ruft self.get_bot_positions() die Hilfsmethode auf und gibt nur die Positionen zurück, die mit dem aktuellen Bot-Label verknüpft sind.

Ebenso kann die Methode get_distance_in_pips() wiederverwendet werden, um Handelsentscheidungen zu treffen. In einer Grid-Strategie möchten Sie beispielsweise möglicherweise nur dann eine neue Order eröffnen, wenn sich der Markt weit genug von der letzten Position entfernt hat:

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()

Weitere Konzepte

Namespaces und Importe

Namespaces in Python helfen dabei, Code zu organisieren und Namenskonflikte zu vermeiden, indem verwandte Bezeichner (Variablen, Funktionen, Klassen) gruppiert werden. Importe ermöglichen es Ihnen, externe Module, Bibliotheken oder Pakete einzubinden, um die Funktionalität über die Standardfunktionen von Python hinaus zu erweitern.

In Python können Sie Standardmodule oder benutzerdefinierte Pakete importieren:

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

Sie können auch bestimmte Funktionen oder Klassen importieren:

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

Beim Schreiben von Algorithmen für cTrader in Python funktionieren Importe etwas anders, da cTrader in die .NET-basierte cTrader Algo API integriert ist. Dieses Setup umfasst eine Brücke, die es Python-Code ermöglicht, mit .NET-Assemblys zu interagieren, und dies erklärt, warum ein typischer cBot mit Folgendem beginnt:

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 *

Dabei gilt:

  • import clr lädt die Common Language Runtime (CLR) und ermöglicht Python die Interoperabilität mit .NET-Bibliotheken.
  • clr.AddReference("cAlgo.API") verweist auf die cAlgo API-Assembly, die alle handelsbezogenen Klassen, Methoden und Objekte enthält.
  • from cAlgo.API import * importiert alle Typen aus der API (z. B. TradeType, Color, Position).
  • from robot_wrapper import * lädt Hilfsfunktionen und Wrapper, die gängige Handelsoperationen vereinfachen.

Das Namespace-System stellt sicher, dass:

  • Pythons integrierte Namen (wie print) Konflikte mit cTraders Namen (wie api.Print) vermeiden.
  • Alle handelsspezifischen Objekte (api.Symbol, api.Indicators, api.ExecuteMarketOrder) aus dem cTrader Algo API-Namespace stammen und organisiert und eindeutig bleiben.
  • Sie können die Funktionalität erweitern, indem Sie Ihre eigenen benutzerdefinierten Pakete importieren, um zusammen mit den integrierten Typen von cTrader zu arbeiten.

Schleifen

Schleifen ermöglichen es Ihnen, Operationen über eine Sequenz von Werten zu wiederholen oder bis eine Bedingung erfüllt ist. Im algorithmischen Handel werden Schleifen häufig verwendet, um Positionen zu verarbeiten (z. B. schließen, filtern oder aktualisieren), Bedingungen über mehrere Assets oder Indikatoren zu prüfen, Grid- oder Martingale-Strategien zu implementieren, bei denen wiederholte Ordererteilung erforderlich ist, und mehr.

In Python ist die Iteration unkompliziert und unterstützt sowohl for-Schleifen (zum Durchlaufen von Sammlungen) als auch while-Schleifen (zum Wiederholen, bis sich eine Bedingung ändert).

Sie können eine Schleife schreiben, um Positionen zu schließen:

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

In diesem Fall:

  • self.get_bot_positions() gibt alle Positionen zurück, die zum aktuellen cBot gehören.
  • Die for-Schleife durchläuft jede Position.
  • Übereinstimmende Positionen werden mit api.ClosePosition geschlossen.

Betrachten Sie eine andere Schleife, die über alle offenen Grid-Positionen iteriert, sie nacheinander schließt und rekursiv prüft, ob noch Positionen vorhanden sind (Sicherheit bei teilweisen Schließungen).

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()

Bedingte Anweisungen

Bedingte Anweisungen steuern den Ausführungsfluss in Python, indem sie Ihrem Algorithmus ermöglichen, Entscheidungen auf der Grundlage logischer Ausdrücke zu treffen. Bedingungen sind das Rückgrat der Strategielogik und bestimmen, wann Positionen eröffnet, geschlossen oder verwaltet werden.

Python unterstützt drei hauptsächliche bedingte Konstrukte:

  • if – führt Code aus, wenn die Bedingung wahr ist.
  • elif (else if) – prüft eine andere Bedingung, wenn die erste falsch war.
  • else – bietet eine Rückfalloption, wenn keine Bedingungen erfüllt sind.

Betrachten Sie die MACD-Crossover-Bedingungen, die in diesem Code ausgedrückt werden:

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)

Diese Struktur:

  • Erteilt eine Kauforder, wenn die MACD-Linie über der Signallinie liegt.
  • Erteilt eine Verkaufsorder, wenn die MACD-Linie unter der Signallinie liegt.
  • Überspringt den Handel, wenn kein klares Signal vorliegt.

Listen

Python bietet leistungsstarke und prägnante Tools für die Arbeit mit Datensammlungen wie Transaktionen, Preisen oder Indikatoren. Zwei der häufigsten sind List Comprehensions und integrierte Aggregationsfunktionen wie sum().

Sie können beispielsweise einen Code schreiben, der über numbers iteriert und nur gerade Zahlen auf diese Weise behält:

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

Sie können eine List Comprehension verwenden, um nur Positionen für das aktuelle Symbol auszuwählen und eine gefilterte Liste zu erstellen, die in Multi-Symbol-Umgebungen nützlich ist.

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

Die Aggregation von Gewinn oder Volumen mit sum() ist ein Standardansatz in Grid-Trading-Strategien, um zu bestimmen, wann der Gesamtgewinn das Ziel erreicht.

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

Sie können Filterung und Aggregation auf diese Weise kombinieren:

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])

Synchrone und asynchrone Operationen

Python folgt standardmäßig einem synchronen Ausführungsmodell. Die Lebenszyklusmethoden Ihres Algorithmus, wie on_start, on_tick und on_bar_closed, werden nacheinander von der Ereignisschleife der Plattform ausgelöst.

Hier ist eine grundlegende Darstellung eines synchronen Handelsablaufs:

graph TD
    A(Signal erkennen) ==> B(Order ausführen)
    B ==> C(Position erreicht<br>TP/SL)
    C ==> D(Position schließen)
    D ==> A

In synchronen Abläufen kann jede langwierige Operation den Thread blockieren und möglicherweise das nächste Ereignis verzögern. In realen Strategien initiieren Algorithmen jedoch typischerweise nicht blockierende Aktionen, wie das Übermitteln einer Order oder das Setzen von Stop-Loss und Take-Profit, und warten dann auf zukünftige Ereignisse (wie einen neuen Tick oder Bar), um Ergebnisse zu verarbeiten. Dieses Setup führt zu einer asynchronen Orchestrierung, obwohl Pythons async/await-Konstrukte nicht verwendet werden.

Hinweis

Für die meisten Handelsalgorithmen ist explizite asynchrone Programmierung nicht erforderlich.

cTrader verwaltet die Nebenläufigkeit, indem es Ihre ereignisbasierten Callbacks serialisiert. Jede Methode wird unabhängig und niemals parallel aufgerufen, wodurch die Notwendigkeit von Multithreading-Schutzmaßnahmen entfällt. Dieses Design ermöglicht es Strategien, auf mehrere Handelsbedingungen gleichzeitig zu reagieren, im logischen Sinne, ohne zu blockieren.

Im folgenden Beispiel bewertet die Strategie Kauf- und Verkaufssignale unabhängig und reagiert entsprechend, ohne dass sich die Ausführungskontexte überschneiden.

graph TD
    A([Signal erkennen]) ==> B([Kauforder<br>eröffnen]) & C([Verkaufsorder zur<br>Absicherung<br>eröffnen]) ==> D([Eine Order<br>erreicht<br>Take Profit oder<br>Stop Loss])
    D ==> E([Position schließen])
    E ==> A

Obwohl Entscheidungen parallel zu sein scheinen, werden sie sequenziell vom internen Warteschlangensystem von cTrader verarbeitet. Die Plattformarchitektur zielt darauf ab, Nebenläufigkeitsprobleme zu verhindern und gleichzeitig Handelsroboter reaktionsfähig auf Marktveränderungen zu halten.

Image title