English
preview
Python-MetaTrader 5 Strategietester (Teil 03): MetaTrader 5-ähnliche Handelsoperationen – Handhabung und Verwaltung

Python-MetaTrader 5 Strategietester (Teil 03): MetaTrader 5-ähnliche Handelsoperationen – Handhabung und Verwaltung

MetaTrader 5Handel |
27 0
Omega J Msigwa
Omega J Msigwa

Inhalt


Einführung

Im vorangegangenen Artikel haben wir in unserem Simulator eine ähnliche Syntax und ähnliche Funktionen implementiert, wie sie das Python-MetaTrader-Modul bietet. Mit ähnlichen Aufträgen, Deals, Positionen und Strukturen. In diesem Beitrag werden wir einen MetaTrader 5-ähnlichen Ansatz für die Handhabung solcher Strukturen (Handelsoperationen) implementieren. 


Die Methode order_send

Alle Handelsaktionen für MetaTrader 5, wie z. B. das Platzieren von schwebenden Aufträgen, das Eröffnen von Kauf- oder Verkaufspositionen, das Ändern von Aufträgen und das Löschen von Aufträgen, werden durch den Aufruf einer einzigen Funktion als Code-Baustein erzielt.

In MQL5 heißt diese Funktion OrderSend, in Python-MetaTrader 5 heißt sie order_send.

In der Dokumentation heißt es dazu.

Die Methode order_send sendet eine Anfrage zur Durchführung einer Handelsoperation vom Terminal an den Handelsserver. Sie ist ähnlich wie OrderSend.

order_send(
   request      // request structure
   );

Sie benötigt einen einzigen Parameter namens request. Die Struktur vom Typ MqlTradeRequest beschreibt eine erforderliche Handelsaktion.

In der folgenden Tabelle sind die Felder aufgeführt, die die „Anforderungsstruktur“ enthalten muss.

Feld

Beschreibung

action

Art der Handelsoperation. Der Wert kann einer der Werte aus der Enumeration TRADE_REQUEST_ACTIONS sein

magic

EA-ID. Ermöglicht die Organisation der analytischen Bearbeitung von Handelsaufträgen. Jeder EA kann beim Senden einer Handelsanfrage eine eindeutige ID festlegen

order

Ticket des Auftrags. Erforderlich für die Änderung schwebender Aufträge

symbol

Der Name des Handelsinstruments, für das der Auftrag erteilt wird. Nicht erforderlich bei der Änderung von Aufträgen und der Schließung von Positionen

volume

Angefordertes Volumen eines Deals in Losen. Das tatsächliche Volumen beim Abschluss eines Deals hängt von der Art der Auftragsausführung ab.

price

Preis, zu dem ein Auftrag ausgeführt werden soll. Der Preis wird für ein Instrument bei Marktaufträgen der Art „Marktausführung“ (SYMBOL_TRADE_EXECUTION_MARKET) mit der Art TRADE_ACTION_DEAL nicht festgelegt

stoplimit

Ein preisabhängiger Limitauftrag wird erteilt, wenn der Preis den Wert „price“ erreicht (diese Option ist obligatorisch). Der schwebende Auftrag wird erst zu diesem Zeitpunkt an das Handelssystem weitergeleitet.

sl

Ein Kurs, bei dem ein Stop-Loss-Auftrag aktiviert wird, wenn sich der Kurs in eine ungünstige Richtung bewegt

tp

Ein Kurs, bei dem ein Take-Profit-Auftrag aktiviert wird, wenn sich der Kurs in eine günstige Richtung bewegt

deviation

Maximal akzeptable Abweichung vom geforderten Preis, angegeben in Punkten

type

Typ des Auftrags. Der Wert kann einer der Werte aus der Enumeration ORDER_TYPE sein

type_filling

Art der Auftragsabwicklung. Der Wert kann einer der Werte aus der Enumeration ORDER_TYPE_FILLING sein.

type_time

Auftragsart mit einem Ablauf. Der Wert kann einer der Werte von ORDER_TYPE_TIME sein.

expiration

Ablaufzeit für schwebende Aufträge (für Aufträge vom Typ TIME_SPECIFIED )

comment

Kommentar eines Auftrags

position

Ticketnummer der Position. Geben Sie diese Option an, wenn Sie eine Position ändern oder schließen, um sie eindeutig zu identifizieren. In der Regel ist es dasselbe wie das Ticket des Auftrags, der die Position eröffnet hat.

position_by

Ticket für die entgegengesetzte Position. Sie wird verwendet, wenn eine Position eröffnet wird, um dadurch eine entgegengesetzte Position (mit demselben Symbol) zu schließen.


Wir benötigen eine ähnliche Funktion in unserer Klasse.

    def order_send(self, request: dict):
        """
        Sends a request to perform a trading operation from the terminal to the trade server. The function is similar to OrderSend in MQL5.
        """
        
        if not self.IS_TESTER:
            result = self.mt5_instance.order_send(request)
            if result is None or result.retcode != self.mt5_instance.TRADE_RETCODE_DONE:
                self.__GetLogger().warning(f"MT5 failed: {self.mt5_instance.last_error()}")
                return None
            return result

Im vorangegangenen Artikel haben wir der Simulatorklasse den Modus Strategietester hinzugefügt, d. h., wenn die Variable IS_TESTER = true ist. Wenn sich der Simulator nicht in diesem Modus befindet, greift er auf Informationen des MetaTrader 5-Clients zurück, und öffnet und verwaltet alle Handelsoperationen dort.

Das obige Code-Fragment sendet eine Order-Anfrage an MetaTrader 5, wenn sich der Nutzer nicht im Tester-Modus befindet.

Andernfalls extrahieren wir die Anmeldedaten der Anfrage.

        action     = request.get("action")
        order_type = request.get("type")
        symbol     = request.get("symbol")
        volume     = float(request.get("volume", 0))
        price      = float(request.get("price", 0))
        sl         = float(request.get("sl", 0))
        tp         = float(request.get("tp", 0))
        ticket     = int(request.get("ticket", -1))
        
        ticks_info = self.tick_cache[symbol]
        
        now = utils.ensure_utc(ticks_info.time)
        ts  = int(now.timestamp())
        msc = int(now.timestamp() * 1000)

Wir müssen alle Vorgänge innerhalb dieser Funktion manuell ausführen, einschließlich des Schreibens (Eröffnung und Schließung von Positionen) und des Übertragens der Deals in einen Container (letztlich übernehmen wir damit die Arbeit von MetaTrader 5).

I: Schwebende Aufträge platzieren 

In einem Simulator ist ein schwebender Auftrag nichts anderes als eine Information über einen Auftrag, die in einem temporären Auftrags-Container-Array (self.__orders_container__) gespeichert ist.

 if action == self.mt5_instance.TRADE_ACTION_PENDING:
            order_ticket = self.__generate_order_ticket()

            order = self.TradeOrder(
                    ticket=order_ticket,
                    time_setup=ts,
                    time_setup_msc=msc,
                    time_done=0,
                    time_done_msc=0,
                    time_expiration=request.get("expiration", 0),
                    type=order_type,
                    type_time=request.get("type_time", 0),
                    type_filling=request.get("type_filling", 0),
                    state=self.mt5_instance.ORDER_STATE_PLACED,
                    magic=request.get("magic", 0),
                    position_id=0,
                    position_by_id=0,
                    reason=self.mt5_instance.DEAL_REASON_EXPERT,
                    volume_initial=volume,
                    volume_current=volume,
                    price_open=price,
                    sl=sl,
                    tp=tp,
                    price_current=price,
                    price_stoplimit=request.get("price_stoplimit", 0),
                    symbol=symbol,
                    comment=request.get("comment", ""),
                    external_id="",
                )

Nachdem ein Auftrag erstellt wurde, wird er dem Container-Array für die Aufträge hinzugefügt und auch im Array für den Auftragsverlauf (Container) protokolliert.

            self.__orders_container__.append(order)
            self.__orders_history_container__.append(order)
            
            return {
                "retcode": self.mt5_instance.TRADE_RETCODE_DONE,
                "order": order_ticket,
            }

II: Positionen eröffnen

In MetaTrader 5 sind Positionen Verträge, die für ein Finanzinstrument gekauft oder verkauft werden. Eine Long-Position (Long) entsteht durch den Kauf in Erwartung eines Preisanstiegs, eine Short-Position (Short) durch den Verkauf eines Vermögenswerts in Erwartung eines zukünftigen Preisrückgangs.

In einem Simulator ist eine Position ein Bündel von „positionsähnlichen“ Informationen, die in einem Container gespeichert sind.

        if action == self.mt5_instance.TRADE_ACTION_DEAL:
            

            position_ticket = self.__generate_position_ticket()
            order_ticket    = self.__generate_order_ticket()
            deal_ticket     = self.__generate_deal_ticket()

            position = self.TradePosition(
                ticket=position_ticket,
                time=ts,
                time_msc=msc,
                time_update=ts,
                time_update_msc=msc,
                type=order_type,
                magic=request.get("magic", 0),
                identifier=position_ticket,
                reason=self.mt5_instance.DEAL_REASON_EXPERT,
                volume=volume,
                price_open=price,
                sl=sl,
                tp=tp,
                price_current=price,
                swap=0,
                profit=0,
                symbol=symbol,
                comment=request.get("comment", ""),
                external_id="",
            )
            
            self.__positions_container__.append(position)

Die Vorgänge der Eröffnung und Schließung von Positionen haben die Aktion TRADE_ACTION_DEAL.  Das Ergebnis eines solchen Vorgangs kann als Deal bezeichnet werden, daher müssen wir einen solchen Datensatz in einem Container der Deals protokollieren.

            self.__deals_history_container__.append(
                self.TradeDeal(
                    ticket=deal_ticket,
                    order=order_ticket,
                    time=ts,
                    time_msc=msc,
                    type=order_type,
                    entry=self.mt5_instance.DEAL_ENTRY_IN,
                    magic=request.get("magic", 0),
                    position_id=position_ticket,
                    reason=self.mt5_instance.DEAL_REASON_EXPERT,
                    volume=volume,
                    price=price,
                    commission=self.__calc_commission(),
                    swap=0,
                    profit=0,
                    fee=0,
                    symbol=symbol,
                    comment=request.get("comment", ""),
                    external_id="",
                )
            )
            
            return {
                "retcode": self.mt5_instance.TRADE_RETCODE_DONE,
                "deal": deal_ticket,
                "order": order_ticket,
                "position": position_ticket,
            }

Ein Deal ist einfach eine Buchung (in der Vergangenheit) der Eröffnung und Schließung von Positionen im MetaTrader 5 Terminal.

 III: Positionen schließen

Der Antrag auf Schließung einer Position ist dem Antrag auf Eröffnung einer neuen Position sehr ähnlich. Beides sind Deals, aber mit unterschiedlichen Eröffnungen.

Um einen Antrag auf Schließung einer Position zu erkennen, prüfen wir, ob er einen Positionsschlüssel in seinem Antrag hat (das Ticket einer bestehenden Position).

        if action == self.mt5_instance.TRADE_ACTION_DEAL:
            
            # ---------- CLOSE POSITION ----------
            
            ticket = request.get("position", -1)
            if ticket != -1:
                pos = next(
                    (p for p in self.__positions_container__ if p.ticket == ticket),
                    None,
                )
                
                if not pos:
                    return {"retcode": self.mt5_instance.TRADE_RETCODE_INVALID}

                self.__positions_container__.remove(pos)
                
                deal_ticket = self.__generate_deal_ticket()
                self.__deals_history_container__.append(
                    self.TradeDeal(
                        ticket=deal_ticket,
                        order=0,
                        time=ts,
                        time_msc=msc,
                        type=order_type,
                        entry=self.mt5_instance.DEAL_ENTRY_OUT,
                        magic=request.get("magic", 0),
                        position_id=pos.ticket,
                        reason=self.mt5_instance.DEAL_REASON_EXPERT,
                        volume=volume,
                        price=price,
                        commission=self.__calc_commission(),
                        swap=0,
                        profit=0,
                        fee=0,
                        symbol=symbol,
                        comment=request.get("comment", ""),
                        external_id="",
                    )
                )

                return {
                    "retcode": self.mt5_instance.TRADE_RETCODE_DONE,
                    "deal": deal_ticket,
                }

Wir können jedoch nicht jeden Antrag auf Schließung einer Position blindlings akzeptieren, sondern müssen ihn prüfen.

Bevor wir einen Deal abschließen und eine Position aus dem Container entfernen, müssen wir zwei wichtige Details überprüfen.

  1. Im MetaTrader 5 werden Kaufpositionen zum Geldkurs geschlossen, während Verkaufspositionen zum Briefkurs geschlossen werden.
  2. Wir prüfen, ob eine in der Anfrage angegebene Auftragsart (Positionstyp) das Gegenteil eines bestehenden Auftrags ist, d. h., wenn eine Anfrage für einen bestehenden Kaufauftrag ORDER_TYPE_BUY gesendet wird, sollte er für die Schließung infrage kommen, wenn er ORDER_TYPE_SELL erhält.
        if action == self.mt5_instance.TRADE_ACTION_DEAL:
            
            # ---------- CLOSE POSITION ----------
            
            ticket = request.get("position", -1)
            if ticket != -1:
                pos = next(
                    (p for p in self.__positions_container__ if p.ticket == ticket),
                    None,
                )
                
                if not pos:
                    return {"retcode": self.mt5_instance.TRADE_RETCODE_INVALID}

                # validate position close request
                
                if pos.type == order_type:
                    self.__GetLogger().critical("Failed to close an order. Order type must be the opposite")
                    return None
                
                if order_type == self.mt5_instance.ORDER_TYPE_BUY: # For a sell order/position
                    
                    if not TradeValidators.price_equal(a=price, b=ticks_info.ask, eps=pow(10, -symbol_info.digits)):
                        self.__GetLogger().critical(f"Failed to close ORDER_TYPE_SELL. Price {price} is not equal to bid {ticks_info.bid}")
                        return None
                        
                elif order_type == self.mt5_instance.ORDER_TYPE_SELL: # For a buy order/position
                    if not TradeValidators.price_equal(a=price, b=ticks_info.bid, eps=pow(10, -symbol_info.digits)):
                        self.__GetLogger().critical(f"Failed to close ORDER_TYPE_BUY. Price {price} is not equal to bid {ticks_info.bid}")
                        return None
                        
                    
                self.__positions_container__.remove(pos)
                
                deal_ticket = self.__generate_deal_ticket()
                self.__deals_history_container__.append(
                    self.TradeDeal(
                        ticket=deal_ticket,
                        order=0,
                        time=ts,
                        time_msc=msc,
                        type=order_type,
                        entry=self.mt5_instance.DEAL_ENTRY_OUT,
                        magic=request.get("magic", 0),
                        position_id=pos.ticket,
                        reason=self.mt5_instance.DEAL_REASON_EXPERT,
                        volume=volume,
                        price=price,
                        commission=self.__calc_commission(),
                        swap=0,
                        profit=0,
                        fee=0,
                        symbol=symbol,
                        comment=request.get("comment", ""),
                        external_id="",
                    )
                )

                return {
                    "retcode": self.mt5_instance.TRADE_RETCODE_DONE,
                    "deal": deal_ticket,
                }

IV: Ändern von Positionen

Um eine Position zu ändern, konzentrieren wir uns nur auf zwei Details. Die Werte von Stop-Loss und Take-Profit.

        elif action == self.mt5_instance.TRADE_ACTION_SLTP:
            
            ticket = request.get("position", -1)

            pos = next((p for p in self.__positions_container__ if p.ticket == ticket), None)
            if not pos:
                return {"retcode": self.mt5_instance.TRADE_RETCODE_INVALID}

            # --- Correct reference prices ---
            entry_price = pos.price_open
            market_price = ticks_info.bid if pos.type == self.mt5_instance.POSITION_TYPE_BUY else ticks_info.ask

            # --- Validate SL / TP relative to ENTRY ---
            if sl > 0:
                if not trade_validators.is_valid_sl(entry=entry_price, sl=sl, order_type=pos.type):
                    return None

            if tp > 0:
                if not trade_validators.is_valid_tp(entry=entry_price, tp=tp, order_type=pos.type):
                    return None

            # --- Validate freeze level against MARKET ---
            if sl > 0:
                if not trade_validators.is_valid_freeze_level(entry=market_price, stop_price=sl, order_type=pos.type):
                    return None

            if tp > 0:
                if not trade_validators.is_valid_freeze_level(entry=market_price, stop_price=tp, order_type=pos.type):
                    return None

            # --- APPLY MODIFICATION ---
            idx = self.__positions_container__.index(pos)

            updated_pos = pos._replace(
                sl=sl,
                tp=tp,
                time_update=ts,
                time_update_msc=msc
            )

            self.__positions_container__[idx] = updated_pos

            return {"retcode": self.mt5_instance.TRADE_RETCODE_DONE}

V: Löschen von schwebenden Aufträgen

Dies ist ein einfaches Verfahren, um einen Auftrag aus seinem Container-Array zu entfernen. Eine Validierungen ist nicht erforderlich.

        if action == self.mt5_instance.TRADE_ACTION_REMOVE:
            
            ticket = request.get("order", -1)
            
            self.__orders_container__ = [
                o for o in self.__orders_container__ if o.ticket != ticket
            ]
            
            return {"retcode": self.mt5_instance.TRADE_RETCODE_DONE}

VI: Ändern von schwebenden Aufträgen

Um einen schwebenden Auftrag zu ändern, müssen wir uns auf fünf entscheidende Details konzentrieren. Eröffnungskurs, Stop-Loss, Take-Profit, Ablaufzeit und Stop-Limit der Order.

        elif action == self.mt5_instance.TRADE_ACTION_SLTP:
            
            ticket = request.get("position", -1)

            pos = next((p for p in self.__positions_container__ if p.ticket == ticket), None)
            if not pos:
                return {"retcode": self.mt5_instance.TRADE_RETCODE_INVALID}

            # --- Correct reference prices ---
            entry_price = pos.price_open
            market_price = ticks_info.bid if pos.type == self.mt5_instance.POSITION_TYPE_BUY else ticks_info.ask

            # --- Validate SL / TP relative to ENTRY ---
            if sl > 0:
                if not trade_validators.is_valid_sl(entry=entry_price, sl=sl, order_type=pos.type):
                    return None

            if tp > 0:
                if not trade_validators.is_valid_tp(entry=entry_price, tp=tp, order_type=pos.type):
                    return None

            # --- Validate freeze level against MARKET ---
            if sl > 0:
                if not trade_validators.is_valid_freeze_level(entry=market_price, stop_price=sl, order_type=pos.type):
                    return None

            if tp > 0:
                if not trade_validators.is_valid_freeze_level(entry=market_price, stop_price=tp, order_type=pos.type):
                    return None

            # --- APPLY MODIFICATION ---
            idx = self.__positions_container__.index(pos)

            updated_pos = pos._replace(
                sl=sl,
                tp=tp,
                time_update=ts,
                time_update_msc=msc
            )

            self.__positions_container__[idx] = updated_pos

            return {"retcode": self.mt5_instance.TRADE_RETCODE_DONE}
        

All diese Maßnahmen funktionieren jedoch nicht, wenn es keine zentrale(n) Methode(n) gibt, um die Anmeldeinformationen der Anfrage überhaupt zu validieren.

Im ersten Simulator, den wir in dieser Artikelserie implementiert haben, hatten wir eine unorganisierte Art und Weise, die Berechtigungsnachweise der Aufträge zu validieren. Dieses Mal verbessern wir sie, indem wir eine eigene Klasse für diese Aufgabe implementieren.


Die Klasse für die Handelsvalidierung

Wie wir wissen, akzeptiert MetaTrader 5 nicht alle übergebenen Anfragen. Es prüft auf ungültige Anmeldedaten und gibt eine Fehlermeldung aus, wenn dies der Fall ist, und lehnt solche Aufträge ab.

Diese Berechtigungsnachweise werden entsprechend der Spezifikation eines bestimmten Instruments, eines Kontotyps, der Anforderungen des Brokers und manchmal auch der MetaTrader 5 Kundenlimits validiert.

I: Prüfung der richtigen Losgröße 

Für eine Losgröße, die von MetaTrader 5 akzeptiert wird:

  • Sie muss größer sein als die minimal zulässige Losgröße (Volumen) für ein bestimmtes Instrument (Symbol).
  • Sie muss kleiner sein als die maximal zulässige Losgröße für ein bestimmtes Instrument.
  • Sie muss ein Vielfaches der Schrittweite des Volumens sein.

Innerhalb von validators.py

class TradeValidators:
    def __init__(self, symbol_info: namedtuple, ticks_info: any, logger: any, mt5_instance: mt5=mt5):
        
        self.symbol_info = symbol_info
        self.ticks_info = ticks_info
        self.logger = logger
        self.mt5_instance = mt5_instance
        
    def is_valid_lotsize(self, lotsize: float) -> bool:
        
        # Validate lotsize
        
        if lotsize < self.symbol_info.volume_min: # check if the received lotsize is smaller than minimum accepted lot of a symbol
            self.logger.info(f"Trade validation failed: lotsize ({lotsize}) is less than minimum allowed ({self.symbol_info.volume_min})")
            return False
        
        if lotsize > self.symbol_info.volume_max: # check if the received lotsize is greater than the maximum accepted lot
            self.logger.info(f"Trade validation failed: lotsize ({lotsize}) is greater than maximum allowed ({self.symbol_info.volume_max})")
            return False
        
        step_count = lotsize / self.symbol_info.volume_step 
        
        if abs(step_count - round(step_count)) > 1e-7: # check if the stoploss is a multiple of the step size
            self.logger.info(f"Trade validation failed: lotsize ({lotsize}) must be a multiple of step size ({self.symbol_info.volume_step})")
            return False

        return True

II: Sicherstellen, dass genügend Geld für eine neue Stelle vorhanden ist

Das MetaTrader 5-Terminal prüft, ob genügend freie Marge auf dem Konto vorhanden ist, um eine neue Position zu eröffnen.

Nachfolgend finden Sie eine ähnliche Funktion für die Aufgabe.

    def is_there_enough_money(self, margin_required: float, free_margin: float) -> bool:
        
        if margin_required < 0:
            self.logger.info("Trade validation failed: Cannot calculate margin requirements")
            return False
        
        # Check free margin
        if margin_required > free_margin:
            self.logger.info(f'Trade validation failed: Not enough money to open trade. '
                f'Required: {margin_required:.2f}, '
                f'Free margin: {free_margin:.2f}')
            
            return False

        return True

III: Prüfen, ob ein gültiger Eintrag vorhanden ist

Bei einer Kaufposition muss der Preis gleich dem Briefkurs sein, bei einer Verkaufsposition muss der Preis gleich dem Geldkurs sein. Diese Prüfung gilt nur für Positionen. 

    def is_valid_entry(self, price: float, order_type: int) -> bool:
        
        eps = pow(10, -self.symbol_info.digits)
        if order_type == self.mt5_instance.ORDER_TYPE_BUY:  # BUY
            if not self.price_equal(a=price, b=self.ticks_info.ask, eps=eps):
                self.logger.info(f"Trade validation failed: Buy price {price} != ask {self.ticks_info.ask}")
                return False

        elif order_type == self.mt5_instance.ORDER_TYPE_SELL:  # SELL
            if not self.price_equal(a=price, b=self.ticks_info.bid, eps=eps):
                self.logger.info(f"Trade validation failed: Sell price {price} != bid {self.ticks_info.bid}")
                return False
        else:
            self.logger.error("Unknown MetaTrader 5 position type")
            return False

        return True

IV: Sicherstellen, dass die Stop-Loss- und Take-Profit-Werte nicht zu nahe am Markt liegen

Alle Symbole verfügen über einen kleinen Schwellenwert, der den Mindestabstand angibt, in dem Stop-Loss- und Take-Profit-Werte vom Markt platziert werden müssen.

Dieser Schwellenwert wird SYMBOL_TRADE_STOPS_LEVEL genannt.

    def is_valid_stops_level(self, entry: float, stop_price: float, stops_type: str='') -> bool:
        
        point = self.symbol_info.point
        stop_level   = self.symbol_info.trade_stops_level * point
        
        distance = abs(entry-stop_price)
        
        if stop_price <= 0:
            return True
        
        if distance < stop_level:
            self.logger.info(f"{'Either SL or TP' if stops_type=='' else stops_type} is too close to the market. Min allowed distance = {stop_level}")
            return False
        
        return True

V: Prüfen auf gültige Stop-Loss- und Take-Profit-Werte

Bei einem Kaufauftrag muss ein Stop-Loss unter dem Einstiegskurs liegen, während der Take-Profit über dem Einstiegskurs liegen muss.

Bei einem Verkaufsauftrag muss der Take-Profit-Wert unter dem Einstiegskurs liegen, während der Stop-Loss-Wert über dem Einstiegskurs liegen muss. 

    def is_valid_sl(self, entry: float, sl: float, order_type: int) -> bool:
        
        if not self.is_valid_stops_level(entry, sl, "Stoploss"): # check for stops levels
            return False
            
        if sl > 0:
            if order_type in self.BUY_ACTIONS: # buy action
                
                if sl >= entry:
                    self.logger.info(f"Trade validation failed: Buy-based order's stop loss ({sl}) must be below order opening price ({entry})")
                    return False
                
            elif order_type in self.SELL_ACTIONS: # sell action
                
                if sl <= entry:
                    self.logger.info(f"Trade validation failed: Sell-based order's stop loss ({sl}) must be above order opening price ({entry})")
                    return False
            
            else:
                self.logger.error("Unknown MetaTrader 5 order type")
                return False
        
        return True

    def is_valid_tp(self, entry: float, tp: float, order_type: int) -> bool:
        
        if not self.is_valid_stops_level(entry, tp, "Takeprofit"): # check for stops and freeze levels
            return False
        
        if tp > 0:
            if order_type in self.BUY_ACTIONS: # buy position
                if tp <= entry:
                    self.logger.info(f"Trade validation failed: {self.ORDER_TYPES_MAP[order_type]} take profit ({tp}) must be above order opening price ({entry})")
                    return False
            elif order_type in self.SELL_ACTIONS: # sell position
                if tp >= entry:
                    self.logger.info(f"Trade validation failed: {self.ORDER_TYPES_MAP[order_type]} take profit ({tp}) must be below order opening price ({entry})")
                    return False
            else:
                self.logger.error("Unknown MetaTrader 5 order type")
                return False
        
        return True

VI: Eine Prüfung, um sicherzustellen, dass die maximale Losgröße für ein Instrument nicht erreicht wird

Bei einigen Symbolen gibt es bei einigen Brokern eine Obergrenze für die Gesamtgröße der Lots in einzelnen offenen Aufträgen und Positionen.

    def is_symbol_volume_reached(self, symbol_volume: float, volume_limit: float) -> bool:
        
        """Checks if the maximum allowed volume is reached for a particular instrument

        Returns:
            bool: True if the condition is reached and False when it is not.
        """
    
        if symbol_volume >= volume_limit and volume_limit > 0:
            self.logger.critical(f"Symbol Volume limit of {volume_limit} is reached!")
            return True
        
        return False

VII: Eine Überprüfung, um sicherzustellen, dass die maximale Anzahl von Aufträgen nicht erreicht wird

Bei einigen Konten ist die Anzahl der gleichzeitig offenen schwebenden Aufträge begrenzt. Wir müssen dies überprüfen, um sicherzustellen, dass wir nicht gegen die Regeln eines Demokontos verstoßen, genauso wie die MetaTrader-5-Plattform uns nicht erlauben würde, gegen die Regeln eines Echtgeldkontos zu verstoßen.

    def is_max_orders_reached(self, open_orders: int, ac_limit_orders: int) -> bool:
        """Checks whether the maximum number of orders for the account is reached

        Args:
            open_orders (int): The number of opened orders
            ac_limit_orders (int): Maximum number of orders allowed for the account

        Returns:
            bool: True if the threshold is reached, otherwise, it returns false.
        """
        
        if open_orders >= ac_limit_orders and ac_limit_orders > 0:
            self.logger.critical(f"Pending Orders limit of {ac_limit_orders} is reached!")
            return True
        
        return False

VIII: Prüfen des „Freeze Levels“

Der Parameter SYMBOL_TRADE_FREEZE_LEVEL kann in der Symbolspezifikation gesetzt werden. Sie zeigt den Abstand des Einfrierens der Handelsoperationen für schwebende Aufträge und offene Positionen in Punkten an. Wenn beispielsweise ein Handel mit einem Finanzinstrument zur Bearbeitung an ein externes Handelssystem weitergeleitet wird, kann ein schwebender BuyLimit-Auftrag derzeit zu nahe am aktuellen Ask-Kurs liegen. Und wenn eine Anfrage zur Änderung dieses Auftrags zu einem Zeitpunkt gesendet wird, an dem der Eröffnungskurs nahe genug am Ask-Kurs liegt, kann es passieren, dass der Auftrag bereits ausgeführt wurde und eine Änderung unmöglich ist.
Art der Order/Position
Aktivierungspreis
Prüfung
Buy Limit order
 Ask
Ask-OpenPrice >= SYMBOL_TRADE_FREEZE_LEVEL
Buy Stop order  Ask OpenPrice-Ask >= SYMBOL_TRADE_FREEZE_LEVEL
Sell Limit order  Bid OpenPrice-Bid >= SYMBOL_TRADE_FREEZE_LEVEL
Sell Stop order  Bid Bid-OpenPrice >= SYMBOL_TRADE_FREEZE_LEVEL
Buy position
 Bid TakeProfit-Bid >= SYMBOL_TRADE_FREEZE_LEVEL
Bid-StopLoss >= SYMBOL_TRADE_FREEZE_LEVEL
Sell position
 Ask Ask-TakeProfit >= SYMBOL_TRADE_FREEZE_LEVEL
StopLoss-Ask >= SYMBOL_TRADE_FREEZE_LEVEL
    def is_valid_freeze_level(self, entry: float, stop_price: float, order_type: int) -> bool:
        """
        Check SYMBOL_TRADE_FREEZE_LEVEL for pending orders and open positions.
        """

        freeze_level = self.symbol_info.trade_freeze_level
        if freeze_level <= 0:
            return True  # No freeze restriction

        point = self.symbol_info.point
        freeze_distance = freeze_level * point

        bid = self.ticks_info.bid
        ask = self.ticks_info.ask

        def log_fail(msg: str, dist: float):
            self.logger.info(
                f"{msg} | distance={dist/point:.1f} pts < "
                f"freeze_level={freeze_level} pts"
            )

        # ---------------- Pending Orders ----------------

        if order_type == self.mt5_instance.ORDER_TYPE_BUY_LIMIT:
            dist = ask - entry
            if dist < freeze_distance:
                log_fail("BuyLimit cannot be modified: Ask - OpenPrice", dist)
                return False
            return True

        if order_type == self.mt5_instance.ORDER_TYPE_SELL_LIMIT:
            dist = entry - bid
            if dist < freeze_distance:
                log_fail("SellLimit cannot be modified: OpenPrice - Bid", dist)
                return False
            return True

        if order_type == self.mt5_instance.ORDER_TYPE_BUY_STOP:
            dist = entry - ask
            if dist < freeze_distance:
                log_fail("BuyStop cannot be modified: OpenPrice - Ask", dist)
                return False
            return True

        if order_type == self.mt5_instance.ORDER_TYPE_SELL_STOP:
            dist = bid - entry
            if dist < freeze_distance:
                log_fail("SellStop cannot be modified: Bid - OpenPrice", dist)
                return False
            return True

        # ---------------- Open Positions (SL / TP modification) ----------------

        # Buy position
        if order_type == self.mt5_instance.ORDER_TYPE_BUY:
            if stop_price <= 0:
                return True

            if stop_price < entry:  # StopLoss
                dist = bid - stop_price
                if dist < freeze_distance:
                    log_fail("Buy position SL cannot be modified: Bid - SL", dist)
                    return False
            else:  # TakeProfit
                dist = stop_price - bid
                if dist < freeze_distance:
                    log_fail("Buy position TP cannot be modified: TP - Bid", dist)
                    return False

            return True

        # Sell position
        if order_type == self.mt5_instance.ORDER_TYPE_SELL:
            if stop_price <= 0:
                return True

            if stop_price > entry:  # StopLoss
                dist = stop_price - ask
                if dist < freeze_distance:
                    log_fail("Sell position SL cannot be modified: SL - Ask", dist)
                    return False
            else:  # TakeProfit
                dist = ask - stop_price
                if dist < freeze_distance:
                    log_fail("Sell position TP cannot be modified: Ask - TP", dist)
                    return False

            return True

        self.logger.error("Unknown MetaTrader 5 order type")
        return False


Alle validierenden Funktionen innerhalb von order_send (TL;DR)

Mit all diesen Funktionen in einer Klasse namens TradeValidators in der Datei validators.py, die auf die Funktion order_send angewendet wird, sieht das Ganze wie folgt aus:

    def order_send(self, request: dict):
        """
        Sends a request to perform a trading operation from the terminal to the trade server. The function is similar to OrderSend in MQL5.
        """

        # -----------------------------------------------------
        
        if not self.IS_TESTER:
            result = self.mt5_instance.order_send(request)
            if result is None or result.retcode != self.mt5_instance.TRADE_RETCODE_DONE:
                self.__GetLogger().warning(f"MT5 failed: {self.mt5_instance.last_error()}")
                return None
            return result

        # -------------------- Extract request -----------------------------
        
        action     = request.get("action")
        order_type = request.get("type")
        symbol     = request.get("symbol")
        volume     = float(request.get("volume", 0))
        price      = float(request.get("price", 0))
        sl         = float(request.get("sl", 0))
        tp         = float(request.get("tp", 0))
        ticket     = int(request.get("ticket", -1))
        
        ticks_info = self.tick_cache[symbol]
        symbol_info = self.symbol_info(symbol)
        ac_info = self.account_info()
        
        now = ticks_info.time
        ts  = int(now)
        msc = int(now * 1000)
        
        if order_type not in self.ORDER_TYPES:
            return {"retcode": self.mt5_instance.TRADE_RETCODE_INVALID}
        
        
        trade_validators = TradeValidators(symbol_info=symbol_info, 
                                           ticks_info=ticks_info, 
                                           logger=self.__GetLogger(), 
                                           mt5_instance=self.mt5_instance)
        
        # -------------------- REMOVE pending order ------------------------
        
        if action == self.mt5_instance.TRADE_ACTION_REMOVE:
            
            ticket = request.get("order", -1)
            
            self.__orders_container__ = [
                o for o in self.__orders_container__ if o.ticket != ticket
            ]
            
            return {"retcode": self.mt5_instance.TRADE_RETCODE_DONE}

        # --------------------- PENDING order --------------------------
        
        if action == self.mt5_instance.TRADE_ACTION_PENDING:
            
            if trade_validators.is_max_orders_reached(open_orders=len(self.__orders_container__), 
                                                      ac_limit_orders=ac_info.limit_orders):
                return None
            
            if not trade_validators.is_valid_sl(entry=price, sl=sl, order_type=order_type) or not trade_validators.is_valid_tp(entry=price, tp=tp, order_type=order_type):
                return None
            
            total_volume = sum([pos.volume for pos in self.__positions_container__]) + sum([order.volume for order in self.__orders_container__])
            if trade_validators.is_symbol_volume_reached(symbol_volume=total_volume, volume_limit=symbol_info.volume_limit):
                return None
            
            order_ticket = self.__generate_order_ticket()

            order = self.TradeOrder(
                    ticket=order_ticket,
                    time_setup=ts,
                    time_setup_msc=msc,
                    time_done=0,
                    time_done_msc=0,
                    time_expiration=request.get("expiration", 0),
                    type=order_type,
                    type_time=request.get("type_time", 0),
                    type_filling=request.get("type_filling", 0),
                    state=self.mt5_instance.ORDER_STATE_PLACED,
                    magic=request.get("magic", 0),
                    position_id=0,
                    position_by_id=0,
                    reason=self.mt5_instance.DEAL_REASON_EXPERT,
                    volume_initial=volume,
                    volume_current=volume,
                    price_open=price,
                    sl=sl,
                    tp=tp,
                    price_current=price,
                    price_stoplimit=request.get("price_stoplimit", 0),
                    symbol=symbol,
                    comment=request.get("comment", ""),
                    external_id="",
                )
                
            self.__orders_container__.append(order) 
            self.__orders_history_container__.append(order)
            
            return {
                "retcode": self.mt5_instance.TRADE_RETCODE_DONE,
                "order": order_ticket,
            }

        # ------------------ MARKET DEAL (open or close) ------------------
        
        if action == self.mt5_instance.TRADE_ACTION_DEAL:
            
            # ---------- CLOSE POSITION ----------
            
            ticket = request.get("position", -1)
            if ticket != -1:
                pos = next(
                    (p for p in self.__positions_container__ if p.ticket == ticket),
                    None,
                )
                
                if not pos:
                    return {"retcode": self.mt5_instance.TRADE_RETCODE_INVALID}

                # validate position close request
                
                if pos.type == order_type:
                    self.__GetLogger().critical("Failed to close an order. Order type must be the opposite")
                    return None
                
                if order_type == self.mt5_instance.ORDER_TYPE_BUY: # For a sell order/position
                    
                    if not TradeValidators.price_equal(a=price, b=ticks_info.ask, eps=pow(10, -symbol_info.digits)):
                        self.__GetLogger().critical(f"Failed to close ORDER_TYPE_SELL. Price {price} is not equal to bid {ticks_info.bid}")
                        return None
                        
                elif order_type == self.mt5_instance.ORDER_TYPE_SELL: # For a buy order/position
                    if not TradeValidators.price_equal(a=price, b=ticks_info.bid, eps=pow(10, -symbol_info.digits)):
                        self.__GetLogger().critical(f"Failed to close ORDER_TYPE_BUY. Price {price} is not equal to bid {ticks_info.bid}")
                        return None
                        
                    
                self.__positions_container__.remove(pos)
                
                deal_ticket = self.__generate_deal_ticket()
                self.__deals_history_container__.append(
                    self.TradeDeal(
                        ticket=deal_ticket,
                        order=0,
                        time=ts,
                        time_msc=msc,
                        type=order_type,
                        entry=self.mt5_instance.DEAL_ENTRY_OUT,
                        magic=request.get("magic", 0),
                        position_id=pos.ticket,
                        reason=self.mt5_instance.DEAL_REASON_EXPERT,
                        volume=volume,
                        price=price,
                        commission=self.__calc_commission(),
                        swap=0,
                        profit=0,
                        fee=0,
                        symbol=symbol,
                        comment=request.get("comment", ""),
                        external_id="",
                    )
                )

                return {
                    "retcode": self.mt5_instance.TRADE_RETCODE_DONE,
                    "deal": deal_ticket,
                }
                
            # ---------- OPEN POSITION ----------
            
            # validate new stops 
            
            if not trade_validators.is_valid_sl(entry=price, sl=sl, order_type=order_type):
                return None
            if not trade_validators.is_valid_tp(entry=price, tp=tp, order_type=order_type):
                return None
            
            # validate the lotsize
            
            if not trade_validators.is_valid_lotsize(lotsize=volume):
                return None
            
            total_volume = sum([pos.volume for pos in self.__positions_container__]) + sum([order.volume for order in self.__orders_container__])
            if trade_validators.is_symbol_volume_reached(symbol_volume=total_volume, volume_limit=symbol_info.volume_limit):
                return None
            
            
            if not trade_validators.is_there_enough_money(margin_required=self.order_calc_margin(order_type=order_type, 
                                                                                                 symbol=symbol,
                                                                                                 volume=volume,
                                                                                                 price=price), 
                                                          free_margin=ac_info.margin_free):
                return None
            
            position_ticket = self.__generate_position_ticket()
            order_ticket    = self.__generate_order_ticket()
            deal_ticket     = self.__generate_deal_ticket()

            position = self.TradePosition(
                ticket=position_ticket,
                time=ts,
                time_msc=msc,
                time_update=ts,
                time_update_msc=msc,
                type=order_type,
                magic=request.get("magic", 0),
                identifier=position_ticket,
                reason=self.mt5_instance.DEAL_REASON_EXPERT,
                volume=volume,
                price_open=price,
                sl=sl,
                tp=tp,
                price_current=price,
                swap=0,
                profit=0,
                symbol=symbol,
                comment=request.get("comment", ""),
                external_id="",
            )
            
            self.__positions_container__.append(position)

            self.__deals_history_container__.append(
                self.TradeDeal(
                    ticket=deal_ticket,
                    order=order_ticket,
                    time=ts,
                    time_msc=msc,
                    type=order_type,
                    entry=self.mt5_instance.DEAL_ENTRY_IN,
                    magic=request.get("magic", 0),
                    position_id=position_ticket,
                    reason=self.mt5_instance.DEAL_REASON_EXPERT,
                    volume=volume,
                    price=price,
                    commission=self.__calc_commission(),
                    swap=0,
                    profit=0,
                    fee=0,
                    symbol=symbol,
                    comment=request.get("comment", ""),
                    external_id="",
                )
            )
            
            return {
                "retcode": self.mt5_instance.TRADE_RETCODE_DONE,
                "deal": deal_ticket,
                "order": order_ticket,
                "position": position_ticket,
            }
            
        elif action == self.mt5_instance.TRADE_ACTION_MODIFY: # Modifying pending orders

            ticket = request.get("order", -1)

            order = next(
                (o for o in self.__orders_container__ if o.ticket == ticket),
                None,
            )

            if not order:
                return {"retcode": self.mt5_instance.TRADE_RETCODE_INVALID}

            # validate new stops 
            
            if not trade_validators.is_valid_freeze_level(entry=price, stop_price=sl, order_type=order_type):
                return None
            if not trade_validators.is_valid_freeze_level(entry=price, stop_price=tp, order_type=order_type):
                return None
                
            # Modify ONLY allowed fields
            order.price_open      = price
            order.sl              = sl
            order.tp              = tp
            order.time_expiration = request.get("expiration", order.time_expiration)
            order.price_stoplimit = request.get("price_stoplimit", order.price_stoplimit)

            return {"retcode": self.mt5_instance.TRADE_RETCODE_DONE}
            
        elif action == self.mt5_instance.TRADE_ACTION_SLTP: # Modifying an open position
            
            ticket = request.get("position", -1)

            pos = next(
                (p for p in self.__positions_container__ if p.ticket == ticket),
                None,
            )

            if not pos:
                return {"retcode": self.mt5_instance.TRADE_RETCODE_INVALID}

            # Check for valid stoplosses and TPs ensuring they are not too close to the market
            
            if pos.type == self.mt5_instance.ORDER_TYPE_BUY:
                if not trade_validators.is_valid_sl(entry=ticks_info.bid, sl=sl, order_type=order_type) or not trade_validators.is_valid_tp(entry=ticks_info.bid, tp=tp, order_type=order_type):
                    return None
                
            elif pos.type == self.mt5_instance.ORDER_TYPE_SELL:
                if not trade_validators.is_valid_sl(entry=ticks_info.ask, sl=sl, order_type=order_type) or not trade_validators.is_valid_tp(entry=ticks_info.ask, tp=tp, order_type=order_type):
                    return None
            
            if not trade_validators.is_valid_freeze_level(entry=price, stop_price=sl, order_type=order_type):
                return None
            if not trade_validators.is_valid_freeze_level(entry=price, stop_price=sl, order_type=order_type):
                return None
            
            pos.sl = sl
            pos.tp = tp
            pos.time_update = ts
            pos.time_update_msc = msc

            return {"retcode": self.mt5_instance.TRADE_RETCODE_DONE}

        return {
            "retcode": self.mt5_instance.TRADE_RETCODE_INVALID,
            "comment": "Unsupported trade action",
        }


Die Klasse CTrade innerhalb eines Simulators

Genau wie MQL5 ist das Python-MetaTrader 5-Modul ein Low-Level-Modul, das uns die Kommunikation mit dem MetaTrader 5-Terminal ermöglicht. Wie wir soeben gesehen haben, braucht es mehr als nur einen Antrag auf Eröffnung von Positionen und Aufträgen (ein langwieriger und mühsamer Prozess).

In MQL5 haben wir die sogenannten Handelsklassen, die uns eine einfache Schnittstelle für die Eröffnung und Verwaltung von Geschäften bieten. In Python haben wir eine ähnliche Klasse erstellt. Passen wir jetzt die Klasse CTrade an die Bedürfnisse unseres Simulators an.

import MetaTrader5 as mt5
from datetime import datetime, timezone
import config

class CTrade:
    
    def __init__(self, simulator, magic_number: int, filling_type_symbol: str, deviation_points: int):
        
        self.simulator = simulator
        self.mt5_instance = simulator.mt5_instance
        self.magic_number = magic_number
        self.deviation_points = deviation_points
        self.filling_type = self._get_type_filling(filling_type_symbol)
        
        if self.filling_type == -1:
            print("Failed to initialize the class, Invalid filling type. Check your symbol")
            return

Wir geben unserer Klasse eine Simulatorinstanz, um einige Methoden daraus zu extrahieren, anstatt sie direkt vom MetaTrader 5 zu übernehmen. Das hilft unserer Klasse, überschriebene Methoden zu verwenden, die im vorherigen Artikel besprochen wurden.

Die Klasse CTrade verfügt über zahlreiche Methoden, die alle auf einer Methode namens order_send basieren, die mit dem Python-MetaTrader 5-Modul geliefert wird.

In der gesamten Klasse werden keine Methoden aus dem Python-MetaTrader 5-Modul aufgerufen, sondern überschriebene Methoden aus einer Simulator-Klasse.

class CTrade:
    
    def __init__(self, simulator, magic_number: int, filling_type_symbol: str, deviation_points: int):
        
        self.simulator = simulator
        self.mt5_instance = simulator.mt5_instance
        self.magic_number = magic_number
        self.deviation_points = deviation_points
        self.filling_type = self._get_type_filling(filling_type_symbol)
        
        if self.filling_type == -1:
            print("Failed to initialize the class, Invalid filling type. Check your symbol")
            return
        
    def _get_type_filling(self, symbol):
        
        symbol_info = self.simulator.symbol_info(symbol)
        if symbol_info is None:
            print(f"Failed to get symbol info for {symbol}")
        
        filling_map = {
            1: self.mt5_instance.ORDER_FILLING_FOK,
            2: self.mt5_instance.ORDER_FILLING_IOC,
            4: self.mt5_instance.ORDER_FILLING_BOC,
            8: self.mt5_instance.ORDER_FILLING_RETURN
        }
        
        return filling_map.get(symbol_info.filling_mode, f"Unknown Filling type")
    
    
    def __GetLogger(self):
        if self.simulator.IS_TESTER:
            return config.tester_logger
        
        return config.simulator_logger
    
    def position_open(self, symbol: str, volume: float, order_type: int, price: float, sl: float=0.0, tp: float=0.0, comment: str="") -> bool:
        
        """
        Open a market position (instant execution).
        
        Executes either a buy or sell order at the current market price. This is for immediate
        position opening, not pending orders.
        
        Args:
            symbol: Trading symbol (e.g., "EURUSD", "GBPUSD")
            volume: Trade volume in lots (e.g., 0.1 for micro lot)
            order_type: Trade direction (either ORDER_TYPE_BUY or ORDER_TYPE_SELL)
            price: Execution price. For market orders, this should be the current:
                - Ask price for BUY orders
                - Bid price for SELL orders
            sl: Stop loss price (set to 0.0 to disable)
            tp: Take profit price (set to 0.0 to disable)
            comment: Optional order comment (max 31 characters, will be truncated automatically)
        
        Returns:
            bool: True if position was opened successfully, False otherwise
        """
        
        request = {
            "action": self.mt5_instance.TRADE_ACTION_DEAL,
            "symbol": symbol,
            "volume": volume,
            "type": order_type,
            "price": price,
            "deviation": self.deviation_points,
            "magic": self.magic_number,
            "comment": comment,
            "type_time": self.mt5_instance.ORDER_TIME_GTC,
            "type_filling":  self.filling_type,
        }
        
        if sl > 0.0:
            request["sl"] = sl
        if tp > 0.0:
            request["tp"] = tp
        
        if self.simulator.order_send(request) is None:
            return False
        
        self.__GetLogger().info(f"Position Opened successfully!")
            
        return True    
    
    def order_open(self, symbol: str, volume: float, order_type: int, price: float, sl: float = 0.0, tp: float = 0.0, type_time: int = mt5.ORDER_TIME_GTC, expiration: datetime = None, comment: str = "") -> bool:
        
        """
        Opens a pending order with full control over order parameters.
        
        Args:
            symbol: Trading symbol (e.g., "EURUSD")
            volume: Order volume in lots
            order_type: Order type (ORDER_TYPE_BUY_LIMIT, ORDER_TYPE_SELL_STOP, etc.)
            price: Activation price for pending order
            sl: Stop loss price (0 to disable)
            tp: Take profit price (0 to disable)
            type_time: Order expiration type (default: ORDER_TIME_GTC). Possible values:
                    - ORDER_TIME_GTC (Good-Til-Canceled)
                    - ORDER_TIME_DAY (Good for current day)
                    - ORDER_TIME_SPECIFIED (expires at specific datetime)
                    - ORDER_TIME_SPECIFIED_DAY (expires at end of specified day)
            expiration: Expiration datetime (required for ORDER_TIME_SPECIFIED types)
            comment: Optional order comment (max 31 characters)
        
        Returns:
            bool: True if order was placed successfully, False otherwise
        """
        
        # Validate expiration for time-specific orders
        if type_time in (self.mt5_instance.ORDER_TIME_SPECIFIED, self.mt5_instance.ORDER_TIME_SPECIFIED_DAY) and expiration is None:
            print(f"Expiration required for order type {type_time}")
            return False
        
        request = {
            "action": self.mt5_instance.TRADE_ACTION_PENDING,
            "symbol": symbol,
            "volume": volume,
            "type": order_type,
            "price": price,
            "sl": sl,
            "tp": tp,
            "deviation": self.deviation_points,
            "magic": self.magic_number,
            "comment": comment[:31],  # MT5 comment max length is 31 chars
            "type_time": type_time,
            "type_filling": self.filling_type,
        }
        
        # Add expiration if required
        if type_time in (self.mt5_instance.ORDER_TIME_SPECIFIED, self.mt5_instance.ORDER_TIME_SPECIFIED_DAY) and expiration is not None:
            
            # Convert to broker's expected format (UTC timestamp in milliseconds)
            
            expiration_utc = expiration.astimezone(timezone.utc) if expiration.tzinfo else expiration.replace(tzinfo=timezone.utc)
            request["expiration"] = int(expiration_utc.timestamp() * 1000)            
            
        # Send order
        
        if self.simulator.order_send(request) is None:
            return False
        
        self.__GetLogger().info(f"Order opened successfully!")
        return True
    
    
    def buy(self, volume: float, symbol: str, price: float, sl: float=0.0, tp: float=0.0, comment: str="") -> bool:
        
        """
        Opens a buy (market) position.
        
        Args:
            volume: Trade volume (lot size)
            symbol: Trading symbol (e.g., "EURUSD")
            price: Execution price
            sl: Stop loss price (optional, default=0.0)
            tp: Take profit price (optional, default=0.0)
            comment: Position comment (optional, default="")
        
        Returns:
            bool: True if order was sent successfully, False otherwise
        """
    
        return self.position_open(symbol=symbol, volume=volume, order_type=self.mt5_instance.ORDER_TYPE_BUY, price=price, sl=sl, tp=tp, comment=comment)

Mit diesem Modul, das an die Bedürfnisse unseres Simulators angepasst wurde, können wir nun auf einfache Weise Positionen und schwebende Aufträge eröffnen. Bevor wir diese Methoden testen, sollten wir die Änderungen an unserer Klasse verstehen und wissen, was zu tun ist, damit ein Simulator erfolgreich läuft.

Im vorigen Artikel haben wir eine Methode namens Start eingeführt, die unsere Simulatorklasseninstanz in einen Strategietestermodus versetzt, der alles und fast alle Daten in der Klasse virtuell macht. 

    def Start(self, IS_TESTER: bool) -> bool: # simulator start
        
        self.IS_TESTER = IS_TESTER

Wenn dieser Modus ausgewählt ist (ist auf „True“ gesetzt), ahmt der Simulator das Verhalten des MetaTrader 5-Strategietesters nach; bei der Einstellung „False“ verhält sich der Simulator anders, da er für alle Informationen direkt auf den MetaTrader 5-Client zurückgreift und die Trades dort eröffnet. Dieser Modus wurde zu Testzwecken in die Klasse integriert, um sicherzustellen, dass die Abläufe im Simulator denen im MetaTrader 5-Client entsprechen.  

Mit den neu eingeführten Änderungen wird diese Methode abgeschafft. Standardmäßig wird die Klasse im Modus Strategietester (Simulator) aufgerufen. Um in den MetaTrader 5-Modus zu gelangen (in der Regel zu Debugging-Zwecken), muss der Nutzer beim Aufruf des endgültigen Skripts das Argument --mt5 übergeben.

(venv) C:\Users\Omega Joctan\OneDrive\Documents\PyMetaTester>python test.py --mt5


Ausführen von Handelsaktionen in einem Simulator

Sie müssen die folgenden Schritte ausführen, um die aktuelle Version des Simulators zu testen (Dateien am Ende dieses Artikels):

Innerhalb von test.py

01: Initialisieren des MetaTrader 5-Terminals

import MetaTrader5 as mt5
from Trade.Trade import CTrade
from datetime import datetime, timedelta
import time
import pytz
from simulator import Simulator, CTrade

if not mt5.initialize(): # Initialize MetaTrader5 instance
    print(f"Failed to Initialize MetaTrader5. Error = {mt5.last_error()}")
    mt5.shutdown()
    quit()

02: Aufrufen der Simulatorinstanz 

Wir rufen die Instanz der Simulatorklasse auf und geben ihr die Instanz der MetaTrader 5-App, den Kontostand, der als Einlage bezeichnet wird, und den Hebelwert.

sim = Simulator(simulator_name="MySimulator", mt5_instance=mt5, deposit=1078.30, leverage="1:500")

Wir verwenden dann eine Simulatorinstanz für die Klasse CTrade.

03: (Optional) Instanziierung der CTrade-Klasse

symbol = "EURUSD"
timeframe = mt5.TIMEFRAME_H1
m_trade = CTrade(simulator=sim, magic_number=112233, filling_type_symbol=symbol, deviation_points=100)

04: Wir weisen dem Simulator Tick-Informationen zu und geben sie zurück

mt5_ticks = mt5.symbol_info_tick(symbol) # tick source
sim.TickUpdate(symbol=symbol, tick=mt5_ticks) # very important

tick_from_sim = sim.symbol_info_tick(symbol=symbol) # we get ticks back from a class

ask = tick_from_sim.ask
bid = tick_from_sim.bid

05: Schließlich, einige Handelsgeschäfte 

symbol_info = sim.symbol_info(symbol=symbol)
lotsize = symbol_info.volume_min

m_trade.buy(
    volume=lotsize,
    symbol=symbol,
    price=ask,
    sl=ask - 100 * symbol_info.point,
    tp=ask + 150 * symbol_info.point,
    comment="Market Buy"
)

m_trade.sell(
    volume=lotsize,
    symbol=symbol,
    price=bid,
    sl=bid + 100 * symbol_info.point,
    tp=bid - 150 * symbol_info.point,
    comment="Market Sell"
)

buy_limit_price = ask - 200 * symbol_info.point

m_trade.buy_limit(
    volume=lotsize,
    symbol=symbol,
    price=buy_limit_price,
    sl=buy_limit_price - 100 * symbol_info.point,
    tp=buy_limit_price + 200 * symbol_info.point,
    comment="Buy Limit"
)

sell_limit_price = bid + 200 * symbol_info.point

m_trade.sell_limit(
    volume=lotsize,
    symbol=symbol,
    price=sell_limit_price,
    sl=sell_limit_price + 100 * symbol_info.point,
    tp=sell_limit_price - 200 * symbol_info.point,
    comment="Sell Limit"
)

buy_stop_price = ask + 150 * symbol_info.point

m_trade.buy_stop(
    volume=lotsize,
    symbol=symbol,
    price=buy_stop_price,
    sl=buy_stop_price - 100 * symbol_info.point,
    tp=buy_stop_price + 300 * symbol_info.point,
    comment="Buy Stop"
)

sell_stop_price = bid - 150 * symbol_info.point

m_trade.sell_stop(
    volume=lotsize,
    symbol=symbol,
    price=sell_stop_price,
    sl=sell_stop_price + 100 * symbol_info.point,
    tp=sell_stop_price - 300 * symbol_info.point,
    comment="Sell Stop"
)

Wir können prüfen, ob diese offenen Positionen und Aufträge in unserem Simulator existieren.

print(f"positions in a simulator = {sim.positions_total()}:\n",sim.positions_get())
print(f"orders in a simulator = {sim.orders_total()}:\n", sim.orders_get())

Ausgänge (Testermodus):

(venv) C:\Users\Omega Joctan\OneDrive\Documents\PyMetaTester>python test.py
2026-01-05 15:09:24,504 | INFO     | tester | [Trade.py:85 - position_open() ] => Position Opened successfully!
2026-01-05 15:09:24,513 | INFO     | tester | [Trade.py:85 - position_open() ] => Position Opened successfully!
2026-01-05 15:09:24,515 | INFO     | tester | [Trade.py:148 - order_open() ] => Order opened successfully!
2026-01-05 15:09:24,515 | INFO     | tester | [Trade.py:148 - order_open() ] => Order opened successfully!
2026-01-05 15:09:24,515 | INFO     | tester | [Trade.py:148 - order_open() ] => Order opened successfully!
2026-01-05 15:09:24,515 | INFO     | tester | [Trade.py:148 - order_open() ] => Order opened successfully!
positions in a simulator = 2:
 (TradePosition(ticket=113127357728313068862, time=1767622158, time_msc=1767622158000, time_update=1767622158, time_update_msc=1767622158000, type=0, magic=112233, identifier=113127357728313068862, reason=3, volume=0.01, price_open=1.16792, sl=1.1669200000000002, tp=1.1694200000000001, price_current=1.16792, swap=0, profit=0, symbol='EURUSD', comment='Market Buy', external_id=''), 
TradePosition(ticket=113127357728890995262, time=1767622158, time_msc=1767622158000, time_update=1767622158, time_update_msc=1767622158000, type=1, magic=112233, identifier=113127357728890995262, reason=3, volume=0.01, price_open=1.16792, sl=1.16892, tp=1.16642, price_current=1.16792, swap=0, profit=0, symbol='EURUSD', comment='Market Sell', external_id=''))
orders in a simulator = 4:
 (TradeOrder(ticket=113127357729019468800, time_setup=1767622158, time_setup_msc=1767622158000, time_done=0, time_done_msc=0, time_expiration=0, type=2, type_time=0, type_filling=1, state=1, magic=112233, position_id=0, position_by_id=0, reason=3, volume_initial=0.01, volume_current=0.01, price_open=1.16592, sl=1.1649200000000002, tp=1.16792, price_current=1.16592, price_stoplimit=0, symbol='EURUSD', comment='Buy Limit', external_id=''), 
TradeOrder(ticket=113127357729019468835, time_setup=1767622158, time_setup_msc=1767622158000, time_done=0, time_done_msc=0, time_expiration=0, type=3, type_time=0, type_filling=1, state=1, magic=112233, position_id=0, position_by_id=0, reason=3, volume_initial=0.01, volume_current=0.01, price_open=1.16992, sl=1.17092, tp=1.16792, price_current=1.16992, price_stoplimit=0, symbol='EURUSD', comment='Sell Limit', external_id=''), 
TradeOrder(ticket=113127357729019468836, time_setup=1767622158, time_setup_msc=1767622158000, time_done=0, time_done_msc=0, time_expiration=0, type=4, type_time=0, type_filling=1, state=1, magic=112233, position_id=0, position_by_id=0, reason=3, volume_initial=0.01, volume_current=0.01, price_open=1.1694200000000001, sl=1.1684200000000002, tp=1.17242, price_current=1.1694200000000001, price_stoplimit=0, symbol='EURUSD', comment='Buy Stop', external_id=''), 
TradeOrder(ticket=113127357729019468803, time_setup=1767622158, time_setup_msc=1767622158000, time_done=0, time_done_msc=0, time_expiration=0, type=5, type_time=0, type_filling=1, state=1, magic=112233, position_id=0, position_by_id=0, reason=3, volume_initial=0.01, volume_current=0.01, price_open=1.16642, sl=1.16742, tp=1.1634200000000001, price_current=1.16642, price_stoplimit=0, symbol='EURUSD', comment='Sell Stop', external_id=''))

Ausgänge (MetaTrader 5 Modus):

(venv) C:\Users\Omega Joctan\OneDrive\Documents\PyMetaTester>python test.py --mt5
2026-01-05 15:09:29,171 | INFO     | simulator | [Trade.py:85 - position_open() ] => Position Opened successfully!
2026-01-05 15:09:30,270 | INFO     | simulator | [Trade.py:85 - position_open() ] => Position Opened successfully!
2026-01-05 15:09:31,110 | INFO     | simulator | [Trade.py:148 - order_open() ] => Order opened successfully!
2026-01-05 15:09:31,711 | INFO     | simulator | [Trade.py:148 - order_open() ] => Order opened successfully!
2026-01-05 15:09:33,000 | INFO     | simulator | [Trade.py:148 - order_open() ] => Order opened successfully!
2026-01-05 15:09:33,952 | INFO     | simulator | [Trade.py:148 - order_open() ] => Order opened successfully!
positions in a simulator = 2:
 (TradePosition(ticket=1393244663, time=1767622166, time_msc=1767622166713, time_update=1767622166, time_update_msc=1767622166713, type=0, magic=112233, identifier=1393244663, reason=3, volume=0.01, price_open=1.16791, sl=1.1669100000000001, tp=1.16941, price_current=1.16791, swap=0.0, profit=0.0, symbol='EURUSD', comment='Market Buy', external_id=''), 
TradePosition(ticket=1393244666, time=1767622167, time_msc=1767622167817, time_update=1767622167, time_update_msc=1767622167817, type=1, magic=112233, identifier=1393244666, reason=3, volume=0.01, price_open=1.16791, sl=1.16891, tp=1.16641, price_current=1.16791, swap=0.0, profit=0.0, symbol='EURUSD', comment='Market Sell', external_id=''))
orders in a simulator = 4:
 (TradeOrder(ticket=1393244672, time_setup=1767622168, time_setup_msc=1767622168661, time_done=0, time_done_msc=0, time_expiration=0, type=2, type_time=0, type_filling=2, state=1, magic=112233, position_id=0, position_by_id=0, reason=3, volume_initial=0.01, volume_current=0.01, price_open=1.16591, sl=1.16491, tp=1.16791, price_current=1.16791, price_stoplimit=0.0, symbol='EURUSD', comment='Buy Limit', external_id=''), 
TradeOrder(ticket=1393244676, time_setup=1767622169, time_setup_msc=1767622169494, time_done=0, time_done_msc=0, time_expiration=0, type=3, type_time=0, type_filling=2, state=1, magic=112233, position_id=0, position_by_id=0, reason=3, volume_initial=0.01, volume_current=0.01, price_open=1.16991, sl=1.1709100000000001, tp=1.16791, price_current=1.16791, price_stoplimit=0.0, symbol='EURUSD', comment='Sell Limit', external_id=''), 
TradeOrder(ticket=1393244679, time_setup=1767622170, time_setup_msc=1767622170093, time_done=0, time_done_msc=0, time_expiration=0, type=4, type_time=0, type_filling=2, state=1, magic=112233, position_id=0, position_by_id=0, reason=3, volume_initial=0.01, volume_current=0.01, price_open=1.16941, sl=1.16841, tp=1.17241, price_current=1.16791, price_stoplimit=0.0, symbol='EURUSD', comment='Buy Stop', external_id=''), 
TradeOrder(ticket=1393244687, time_setup=1767622171, time_setup_msc=1767622171748, time_done=0, time_done_msc=0, time_expiration=0, type=5, type_time=0, type_filling=2, state=1, magic=112233, position_id=0, position_by_id=0, reason=3, volume_initial=0.01, volume_current=0.01, price_open=1.16641, sl=1.16741, tp=1.16341, price_current=1.16791, price_stoplimit=0.0, symbol='EURUSD', comment='Sell Stop', external_id=''))


Verwaltung von Aufträgen und Positionen in einem Simulator

Mit Positionen, die in Arrays oder Containern in einer Simulatorklasse gespeichert sind, können wir Aktionen durchführen, z. B. bestimmte Verhaltensweisen/Zustände erkennen, sie ändern oder sogar schließen.

01: Positionen schließen

Wir eröffnen zwei verschiedene Positionen und schließen eine.

m_trade.buy(volume=lotsize, symbol=symbol, price=ask, comment="buy pos")
m_trade.sell(volume=lotsize, symbol=symbol, price=bid, comment="sell pos")

print(f"positions in a simulator = {sim.positions_total()}:\n",sim.positions_get())

positions = sim.positions_get()
for pos in positions:
    if pos.symbol == symbol and pos.type == sim.mt5_instance.POSITION_TYPE_BUY: # close a buy position
        m_trade.position_close(ticket=pos.ticket, deviation=10) 
        
print("positions remaining: ", sim.positions_get())

Ausgabe:

2026-01-05 15:54:50,114 | INFO     | tester | [Trade.py:85 - position_open() ] => Position Opened successfully!
2026-01-05 15:54:50,114 | INFO     | tester | [Trade.py:85 - position_open() ] => Position Opened successfully!
positions in a simulator = 2:
 (TradePosition(ticket=113127532167305337632, time=1767624887, time_msc=1767624887000, time_update=1767624887, time_update_msc=1767624887000, type=0, magic=112233, identifier=113127532167305337632, reason=3, volume=0.01, price_open=1.16743, sl=0.0, tp=0.0, price_current=1.16743, swap=0, profit=0, symbol='EURUSD', comment='buy pos', external_id=''), 
TradePosition(ticket=113127532167305337651, time=1767624887, time_msc=1767624887000, time_update=1767624887, time_update_msc=1767624887000, type=1, magic=112233, identifier=113127532167305337651, reason=3, volume=0.01, price_open=1.16743, sl=0.0, tp=0.0, price_current=1.16743, swap=0, profit=0, symbol='EURUSD', comment='sell pos', external_id=''))
2026-01-05 15:54:50,114 | INFO     | tester | [Trade.py:397 - position_close() ] => Position 113127532167305337632 closed successfully!
positions remaining:  (TradePosition(ticket=113127532167305337651, time=1767624887, time_msc=1767624887000, time_update=1767624887, time_update_msc=1767624887000, type=1, magic=112233, identifier=113127532167305337651, reason=3, volume=0.01, price_open=1.16743, sl=0.0, tp=0.0, price_current=1.16743, swap=0, profit=0, symbol='EURUSD', comment='sell pos', external_id=''),)

Es wurde ausschließlich eine Kaufposition geschlossen! 

02: Änderungen der Position

Ähnlich wie bei der Schließung von Positionen müssen wir eine auswählen, bevor wir eine Änderungsanfrage senden.

m_trade.buy(volume=lotsize, symbol=symbol, price=ask, comment="buy pos")
m_trade.sell(volume=lotsize, symbol=symbol, price=bid, comment="sell pos")

print(f"positions in a simulator = {sim.positions_total()}:\n",sim.positions_get())

positions = sim.positions_get()
for pos in positions:
    if pos.sl == 0:
        if pos.type == sim.mt5_instance.POSITION_TYPE_BUY:
            m_trade.position_modify(ticket=pos.ticket, sl=pos.price_open - 100 * symbol_info.point, tp=pos.tp) 
        if pos.type == sim.mt5_instance.POSITION_TYPE_SELL:
            m_trade.position_modify(ticket=pos.ticket, sl=pos.price_open + 100 * symbol_info.point, tp=pos.tp) 
        
print("positions after modification\n: ", sim.positions_get())

Ausgabe:

(venv) C:\Users\Omega Joctan\OneDrive\Documents\PyMetaTester>python test.py
2026-01-05 17:19:11,604 | INFO     | tester | [Trade.py:85 - position_open() ] => Position Opened successfully!
2026-01-05 17:19:11,604 | INFO     | tester | [Trade.py:85 - position_open() ] => Position Opened successfully!
positions in a simulator = 2:
 (TradePosition(ticket=113127856102580262436, time=1767629948, time_msc=1767629948000, time_update=1767629948, time_update_msc=1767629948000, type=0, magic=112233, identifier=113127856102580262436, reason=3, volume=0.01, price_open=1.16789, sl=0.0, tp=0.0, price_current=1.16789, swap=0, profit=0, symbol='EURUSD', comment='buy pos', external_id=''), 
TradePosition(ticket=113127856102710534416, time=1767629948, time_msc=1767629948000, time_update=1767629948, time_update_msc=1767629948000, type=1, magic=112233, identifier=113127856102710534416, reason=3, volume=0.01, price_open=1.16789, sl=0.0, tp=0.0, price_current=1.16789, swap=0, profit=0, symbol='EURUSD', comment='sell pos', external_id=''))
2026-01-05 17:19:11,604 | INFO     | tester | [Trade.py:469 - position_modify() ] => Position 113127856102580262436 modified successfully!
2026-01-05 17:19:11,606 | INFO     | tester | [Trade.py:469 - position_modify() ] => Position 113127856102710534416 modified successfully!
positions after modification
:  (TradePosition(ticket=113127856102580262436, time=1767629948, time_msc=1767629948000, time_update=1767629948, time_update_msc=1767629948000, type=0, magic=112233, identifier=113127856102580262436, reason=3, volume=0.01, price_open=1.16789, sl=1.1668900000000002, tp=0.0, price_current=1.16789, swap=0, profit=0, symbol='EURUSD', comment='buy pos', external_id=''), 
TradePosition(ticket=113127856102710534416, time=1767629948, time_msc=1767629948000, time_update=1767629948, time_update_msc=1767629948000, type=1, magic=112233, identifier=113127856102710534416, reason=3, volume=0.01, price_open=1.16789, sl=1.16889, tp=0.0, price_current=1.16789, swap=0, profit=0, symbol='EURUSD', comment='sell pos', external_id=''))

03: Arbeiten mit schwebenden Aufträgen

Nachfolgend erfahren Sie, wie Sie schwebende Aufträge ändern und löschen können.

m_trade.buy_stop(symbol=symbol, volume=symbol_info.volume_min, price=ask+500*symbol_info.point)

for order in sim.orders_get():
    
    print("order curr price: ", order.price_open)
    
    m_trade.order_modify(ticket=order.ticket, price=order.price_open+10*symbol_info.point, sl=order.sl, tp=order.tp)
    
    print("order moved 10 points upward", order.price_open)
    if m_trade.order_delete(ticket=order.ticket) is None:
        continue
    
    print("orders remaining: ", sim.orders_total())

Ausgabe:

(venv) C:\Users\Omega Joctan\OneDrive\Documents\PyMetaTester>python test.py
2026-01-05 17:43:15,677 | INFO     | tester | [Trade.py:148 - order_open() ] => Order opened successfully!
order curr price:  1.17281
2026-01-05 17:43:15,677 | INFO     | tester | [Trade.py:536 - order_modify() ] => Order 113127948523388723206 modified successfully!
order moved 10 points upward 1.17291
2026-01-05 17:43:15,677 | INFO     | tester | [Trade.py:431 - order_delete() ] => Order 113127948523388723206 deleted successfully!
orders remaining:  0 


Was kommt als Nächstes?

Die Möglichkeit, Tick-Daten aus einem bestimmten Zeitraum in der Vergangenheit zu verwenden und damit eine Handelsoperation zu simulieren, bedeutet, dass wir nur noch wenige Schritte von der Fertigstellung unseres nutzerdefinierten Simulators entfernt sind. Jetzt haben wir alles, was wir benötigen, um eine Schleife zu erstellen, die die Simulation durch alle verfügbaren Ticks im angegebenen Zeitbereich laufen lässt. Dies nennen wir Strategietests oder eine vollständige Simulation.

In diesem Artikel haben wir den wichtigsten Aspekt eines Handelssimulators besprochen, nämlich das Senden von Handelsaufträgen und deren Verwaltung. In der nächsten Ausgabe werden wir alles zusammenfügen und unsere allererste Strategietestaktion in Python durchführen. Bleiben Sie dran!

Tschüss.

Teilen Sie Ihre Gedanken und helfen Sie, dieses Projekt auf GitHub zu verbessern: https://github.com/MegaJoctan/PyMetaTester


Tabelle der Anhänge

Dateiname Beschreibung und Verwendung
Trade/Trade.py  Enthält die Klasse CTrade, eine Klasse, die eine einfache Möglichkeit zur Ausführung von Handelsoperationen bietet.
config.py Eine Python-Konfigurationsdatei, in der die nützlichsten Variablen für die Wiederverwendbarkeit im gesamten Projekt definiert sind.
utils.py Eine Python-Datei mit Hilfsprogrammen, die einfache Funktionen zur Bewältigung verschiedener Aufgaben enthält (Helfer).
simulator.py Die Datei beinhaltet eine Klasse namens Simulator. Unsere zentrale Simulatorlogik befindet sich an einem Ort.
test.py Eine Datei zum Testen des gesamten Codes und der Funktionen, die in diesem Beitrag besprochen werden.
error_description.py Es verfügt über Funktionen zur Umwandlung aller MetaTrader 5-Fehlercodes in menschenlesbare Meldungen.
requirements.txt  Enthält alle Python-Abhängigkeiten und deren Versionen, die in diesem Projekt verwendet werden. 


Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/20782

Beigefügte Dateien |
Attachments.zip (23.93 KB)
Larry Williams‘ Geheimnisse des Marktes (Teil 7): Eine empirische Untersuchung zum Konzept des Handelstages der Woche Larry Williams‘ Geheimnisse des Marktes (Teil 7): Eine empirische Untersuchung zum Konzept des Handelstages der Woche
Eine empirische Untersuchung des Konzepts „Trade Day of the Week“ von Larry Williams, die zeigt, wie zeitbasierte Marktverzerrungen mit MQL5 gemessen, getestet und angewendet werden können. In diesem Artikel wird ein praktischer Rahmen für die Analyse von Gewinnquoten und Performance über Handelstage hinweg vorgestellt, um kurzfristige Handelssysteme zu verbessern.
Einführung in MQL5 (Teil 33): Beherrschen der API- und WebRequest-Funktion in MQL5 (VII) Einführung in MQL5 (Teil 33): Beherrschen der API- und WebRequest-Funktion in MQL5 (VII)
Dieser Artikel zeigt, wie man die Google Generative AI API mit MetaTrader 5 unter Verwendung von MQL5 integriert. Sie lernen, wie Sie API-Anfragen strukturieren, Serverantworten verarbeiten, KI-generierte Inhalte extrahieren, Ratenlimits verwalten und die Ergebnisse in einer Textdatei speichern, um einen einfachen Zugriff zu ermöglichen.
Einführung in MQL5 (Teil 34): Beherrschung der API- und WebRequest-Funktion in MQL5 (VIII) Einführung in MQL5 (Teil 34): Beherrschung der API- und WebRequest-Funktion in MQL5 (VIII)
In diesem Artikel erfahren Sie, wie Sie ein interaktives Kontrollpanel in MetaTrader 5 erstellen können. Wir behandeln die Grundlagen des Hinzufügens von Eingabefeldern, Aktionsschaltflächen und Beschriftungen zur Anzeige von Text. Anhand eines projektbasierten Ansatzes werden Sie sehen, wie Sie ein Panel einrichten, in das Nutzer Nachrichten eingeben und schließlich Serverantworten von einer API anzeigen können.
Larry Williams’ Geheimnisse des Marktes (Teil 6): Messung von Volatilitätsausbrüchen anhand der Swings des Marktes Larry Williams’ Geheimnisse des Marktes (Teil 6): Messung von Volatilitätsausbrüchen anhand der Swings des Marktes
Dieser Artikel zeigt, wie ein Expert Advisor für den Ausbruch der Volatilität nach Larry Williams in MQL5 entworfen und implementiert werden kann, wobei die Messung der Swing-Range, die Prognose des Eröffnungsniveaus, die risikobasierte Positionsgrößenbestimmung und das Backtesten anhand realer Marktdaten behandelt werden.