English Русский 中文 Español 日本語 Português
preview
Entwicklung eines MQL5 RL-Agenten mit Integration von RestAPI (Teil 3): Erstellen von automatischen Bewegungen und Testskripten in MQL5

Entwicklung eines MQL5 RL-Agenten mit Integration von RestAPI (Teil 3): Erstellen von automatischen Bewegungen und Testskripten in MQL5

MetaTrader 5Beispiele | 17 Juni 2024, 14:31
91 0
Jonathan Pereira
Jonathan Pereira

Einführung

In diesem Artikel, dem dritten in einer Reihe, werden wir uns mit der praktischen Anwendung von REST-APIs in Systemen befassen. Wir haben die Entwicklung von MQL5-Funktionen und ihre Integration mit dem Python-Tic-Tac-Toe-Spiel über FastAPI gesehen. In diesem neuen Artikel können wir erhebliche Fortschritte erzielen. Wir werden uns darauf konzentrieren, automatische Tic-Tac-Toe-Bewegungen zu implementieren, um den Schwierigkeitsgrad und die Interaktivität des Spiels zu erhöhen. Wir werden auch der Entwicklung von Testskripten in MQL5 besondere Aufmerksamkeit widmen, um die Zuverlässigkeit und Effizienz unseres integrierten Systems zu gewährleisten.

In Anbetracht der Komplexität dieses Projekts erscheint es notwendig, klare Anweisungen für die Installation und Ausführung zu geben. Ich bedaure, dass ich diese Informationen nicht in die vorherigen Teile aufgenommen habe, und danke Ihnen für Ihr Verständnis. Daher stelle ich eine Schritt-für-Schritt-Anleitung vor, die auch Empfehlungen für Windows-Nutzer enthält, die auf Einschränkungen bei der Skripterstellung stoßen könnten.

Installations- und Implementierungshandbuch

Voraussetzungen:

  • Python 3.6 oder höher.
  • MetaTrader 5 ist auf Ihrem Computer installiert.
  • Stellen Sie unter Windows sicher, dass Skripting erlaubt ist. Führen Sie gegebenenfalls Set-ExecutionPolicy RemoteSigned in PowerShell als Administrator aus, damit das Skript ausgeführt werden kann.

Installations- und Implementierungsschritte:

  1. Laden Sie das Projekt herunter und entpacken Sie es. Nachdem Sie das Artikelprojekt heruntergeladen haben, entpacken Sie die Dateien in den gewünschten Ordner.
  2. Kopieren Sie sie in den Ordner Experts. Verschieben Sie den extrahierten Ordner in den Ordner Experts in Ihrem MetaTrader-Terminal.
  3. Öffnen Sie den Ordner im Terminal.
    • Unter Windows können Sie dies tun, indem Sie im Startmenü nach Command Prompt oder PowerShell suchen, das Programm öffnen und den Befehl cd folder_path verwenden, um zum entsprechenden Projektordner zu navigieren.
    • Unter MacOS oder Linux öffnen Sie Terminal und führen den Befehl cd folder_path aus.
  4. Erstellen Sie eine virtuelle Umgebung. Öffnen Sie den Projektordner im Terminal und führen Sie python -m venv env aus, um eine virtuelle Umgebung zu erstellen.
  5. Aktivieren Sie die virtuelle Umgebung. Unter Windows führen Sie den Befehl env\Scripts\activate aus. Unter MacOS oder Linux führen Sie den Befehl source env/bin/activate aus.
  6. Abhängigkeiten installieren. Nachdem Sie die virtuelle Umgebung aktiviert haben, führen Sie pip install -r requirements.txt aus.

Durchführung des Projekts:

  • Um die API des Tic-Tac-Toe-Spiels auszuführen, öffnen Sie das Terminal im Projektordner und führen Sie python AppTicTacToe.py aus.
  • Zur Ausführung in MetaTrader öffnen Sie MetaEditor und gehen Sie in das Menü Extras > Optionen > Compiler. Fügen Sie den Pfad zum Ordner „scripts“ der virtuellen Umgebung in das Feld „External compile location“ ein, klicken Sie auf die Schaltfläche zum Kompilieren und/oder ziehen Sie das Skript auf das Chart.
  • Zugriff auf Swagger UI. Gehen Sie im Browser zu localhost:8000/docs, um mit der API über die Swagger-Nutzeroberfläche zu interagieren.

Mit unserer derzeitigen Arbeit verfolgen wir zwei Hauptziele. Erstens soll das Tic-Tac-Toe-Spiel in Python so verbessert werden, dass es die Züge mithilfe intelligenter Entscheidungsalgorithmen selbständig ausführt. Zweitens, die Entwicklung und Implementierung von Unit-Tests in MQL5, die die Zuverlässigkeit der Interaktion zwischen dem MQL5-Code und der REST-API prüfen und sicherstellen.

Dieser Artikel besteht aus 3 Teilen:

  1. Entwicklung automatischer Züge im Tic-Tac-Toe-Spiel, die den Prozess der Modifizierung des Spiels beschreibt, um die Logik der automatischen Züge hinzuzufügen, einschließlich der verwendeten Programmiermethoden und der aufgetretenen Schwierigkeiten.
  2. Erstellen von Testskripten in der MQL5-Sprache, wobei wir den Prozess der Entwicklung von Unit-Tests in MQL5 sehen werden, mit einem Schwerpunkt auf Möglichkeiten, die Interaktion zwischen MQL5 und der REST-API zu testen.
  3. Praktische und Integrationstests, bei denen wir die Integration der umgesetzten Verbesserungen sehen werden, einschließlich Tests und Bewertung der Ergebnisse.
Die Idee hinter diesem Artikel ist, dass wir durch die Implementierung von automatischen Zügen in Tic-Tac-Toe in Python eine solide Grundlage für umfassendere und effizientere Tests schaffen. Da das Spiel autonom reagiert, können wir eine gründliche Überprüfung mit MQL5-Skripten durchführen, die eine reale Interaktion mit der REST-API simulieren. Dieser Ansatz stellt nicht nur sicher, dass das Spiel in verschiedenen Szenarien wie erwartet funktioniert, sondern überprüft auch die Zuverlässigkeit der Verbindung zwischen dem MQL5-Code und der API.

Daher ist die Entwicklung eines Agenten in MQL5, der mit dem Tic-Tac-Toe-Spiel interagiert, der nächste logische Schritt. Dieser Agent kann einen echten Nutzer imitieren, indem er Bewegungen ausführt und auf Aktionen im Spiel reagiert, wodurch eine realitätsnahe Testumgebung geschaffen wird. Diese Strategie ermöglicht es uns nicht nur, die Funktionalität des Spiels und der API zu testen, sondern auch die Entscheidungsalgorithmen in automatisierten Spielen zu untersuchen (und zu verbessern), um ein komplexeres und fesselndes Spielerlebnis zu schaffen.

Die Kombination aus automatischem Tic-Tac-Toe-Spiel und Unit-Tests in MQL5 schafft einen robusten Entwicklungszyklus, bei dem jede Verbesserung im Spiel durch strenge Tests geprüft und verfeinert wird. Ein kontinuierlicher Entwicklungs- und Testprozess stellt sicher, dass ein zuverlässiges und effizientes integriertes System entsteht, das nicht nur ein verbessertes Spielerlebnis bietet, sondern auch wertvolle Erkenntnisse für zukünftige Integrationen und Entwicklungen von Systemen, die integriert werden müssen.


 


Entwicklung von automatischen Bewegungen

In diesem Abschnitt geht es darum, die Struktur und Logik des vorhandenen Spielcodes zu verstehen. Da das Spiel keine grafische Oberfläche hat und keine komplexen Entscheidungsalgorithmen verwendet werden, um automatische Züge zu implementieren, wird unser Ziel sein, diesen Prozess zu vereinfachen und zu optimieren.

Der erste Schritt besteht darin, zu analysieren, wie das Spiel mit manuellen Zügen umgeht, indem man die Logik untersucht, die die Züge steuert und wie sie den Status bestimmt: Sieg, Niederlage oder Unentschieden. Das Verständnis dieses Prozesses ist notwendig, um die automatische Bewegungsfunktion zu integrieren, ohne die bestehende Spielmechanik zu zerstören.

In Zukunft werden automatische Züge auf einfachere Art und Weise durchgeführt. Anstelle eines komplexen Algorithmus können wir auch einen direkteren Ansatz wählen, z. B. die zufällige Auswahl einer freien Position auf dem Spielfeld für das automatische Spiel. Trotz seiner Einfachheit sollte dieser Ansatz ausreichen, um einen Gegner zu imitieren und das Spiel dynamisch zu gestalten.

Schauen wir uns den alten Spielcode genauer an und versuchen wir zu verstehen, wie er funktioniert. Dies ist notwendig, um zu planen, wie automatische Züge hinzugefügt werden können, ohne das Spiel zu verkomplizieren. Unser Ziel ist es, alles nutzerfreundlich zu gestalten und dabei den einfachen Stil beizubehalten, der bereits im Spiel vorhanden ist.

Initialisierung des Spiels:

def __init__(self):
    self.board = [[' ' for _ in range(3)] for _ in range(3)]
    self.player_turn = True

Hier wird das Spiel mit einem leeren Brett (self.board) initialisiert, während die Variable self.player_turn den Zug des Spielers angibt. Das minimalistische Design bietet einen idealen Ausgangspunkt für die Integration von automatisierter Spiellogik, ohne den bestehenden Code zu verkomplizieren.

Anzeige der Spielbretts:

def print_board(self):
    for row in self.board:
        print("|".join(row))
        print("-" * 5)

Die Methode print_board sorgt für die Anzeige des Spielbretts. Die Art und Weise, wie der Spielstand dargestellt wird, ist entscheidend für die Nutzerfreundlichkeit und das Verständnis des Spiels, insbesondere nach der Einführung der automatischen Züge.


Überprüfung des Gewinners:

 def check_winner(self):
     for i in range(3):
         if self.board[i][0] == self.board[i][1] == self.board[i][2] != ' ':
             return self.board[i][0]
         if self.board[0][i] == self.board[1][i] == self.board[2][i] != ' ':
             return self.board[0][i]
     if self.board[0][0] == self.board[1][1] == self.board[2][2] != ' ':
         return self.board[0][0]
     if self.board[0][2] == self.board[1][1] == self.board[2][0] != ' ':
         return self.board[0][2]
     return None

Diese Methode ist notwendig, um den Sieger nach jedem Spiel zu ermitteln. Seine Logik wird bei der Überprüfung des Spielabschlusses benötigt, sowohl bei manuellen als auch bei automatischen Spielen.

Ausführen von Bewegungen:

 def make_move(self, row, col):
     if self.board[row][col] == ' ':
         if self.player_turn:
             self.board[row][col] = 'X'
         else:
             self.board[row][col] = 'O'
         self.player_turn = not self.player_turn
     else:
         print("Invalid move. Try again.")

Die Methode make_move ist für die Spielerzüge zuständig. Die Einführung automatischer Züge erfordert eine Änderung dieser Methode, um zwischen manuellen und automatischen Zügen zu wechseln.


Ausführen von automatischen Bewegungen:

Automatische Spielzüge in Tic-Tac-Toe zu implementieren ist eine neue Herausforderung und macht Spaß. Versuchen wir, dies mit einer neuen Methode namens machine_move zu realisieren. Zuerst wird er versuchen, einen Weg zu finden, um zu gewinnen oder den anderen Spieler am Gewinnen zu hindern. Wenn es keine solche Möglichkeit gibt, wählt es zufällig eine leere Position auf dem Spielbrett.

def machine_move(self):
    for i in range(3):
        for j in range(3):
            if self.board[i][j] == ' ':
                # First try to find a winning move for 'O'
                self.board[i][j] = 'O'
                if self.check_winner() == 'O':
                    return (i, j)  # Return position for win
                self.board[i][j] = ' '

                # Then try to block a winning move for 'X'
                self.board[i][j] = 'X'
                if self.check_winner() == 'X':
                    self.board[i][j] = 'O'  # Block the win of the player
                    return (i, j)
                self.board[i][j] = ' '

    # If there are no winning moves, randomly choose a free position
    available_moves = self.available_moves()
    if available_moves:
        move = random.choice(available_moves)
        self.board[move["row"]][move["col"]] = 'O'
        return (move["row"], move["col"])



Wir werden auch eine Methode available_moves haben. Diese Methode ist sehr wichtig, weil sie es Ihnen ermöglicht, das Spielbrett zu sehen und alle leeren Positionen anzuzeigen. Auf diese Weise wird sichergestellt, dass der Computer nur Züge in leere Zellen macht.

def available_moves(self):
    moves = []
    for i in range(3):
        for j in range(3):
            if self.board[i][j] == ' ':
                moves.append({"row": i, "col": j})
    return moves


Dank dieser Änderungen wird das Tic-Tac-Toe interessanter. Es ist nach wie vor einfach, bietet aber dank der automatischen Züge ein zusätzliches Element der Überraschung und Strategie. Und das macht das Spiel noch spannender.

Die Integration automatischer Züge in ein Python-Tic-Tac-Toe-Spiel erhöht die Komplexität und Interaktivität und ebnet den Weg für zukünftige fortgeschrittene Implementierungen wie die Implementierung eines MQL5-Agenten. Die Idee ist, dass das Spiel am Ende dieser Entwicklungsphase nicht nur für den Spieler komplexer wird, sondern auch für komplexere Interaktionen mit anderen externen Agenten bereit sein wird.

Obwohl die Logik der automatischen Züge zunächst einfach ist, bildet sie eine solide Grundlage für das Spiel. Mit der Fähigkeit, autonome Züge auszuführen und dynamisch auf den Spielkontext zu reagieren, ist das System geeignet, einen realistischen Gegner zu simulieren. Dies ist notwendig, um die Wirksamkeit und Stabilität des Spiels in verschiedenen Szenarien zu testen, insbesondere in Vorbereitung auf die zukünftige Implementierung des MQL5-Agenten.

Bei der Planung der Implementierung dieses Agenten wird Tic-Tac-Toe mit automatischen Zügen bereits so ausgestattet sein, dass eine reale Spielumgebung simuliert wird. Der Agent wird in der Lage sein, mit dem Spiel zu interagieren, Züge zu machen und auf automatische Aktionen zu reagieren, und zwar mit einem Skript, das einem Spiel zwischen zwei menschlichen Spielern ähnelt. Eine solche Interaktion wird es uns ermöglichen, nicht nur die Funktionalität des Spiels und der API zu bewerten, sondern auch die Wirksamkeit der verwendeten Entscheidungsalgorithmen, was den Weg für Verbesserungen und Änderungen ebnen wird.

Darüber hinaus bietet die Anwesenheit eines MQL5-Agenten eine fortschrittlichere und realistischere Testumgebung. So können wir verschiedene Spielszenarien simulieren, die Reaktion des Systems unter verschiedenen Bedingungen testen und die Stabilität und Zuverlässigkeit des Spiels sicherstellen.

Nachstehend finden Sie den vollständigen Spielcode:

class TicTacToe:

    def __init__(self):
        self.board = [[' ' for _ in range(3)] for _ in range(3)]
        self.player_turn = True

    def print_board(self):
        for row in self.board:
            print("|".join(row))
            print("-" * 5)
        print(f"Player {self.player_turn}'s turn")

    def check_winner(self):
        for i in range(3):
            if self.board[i][0] == self.board[i][1] == self.board[i][2] != ' ':
                return self.board[i][0]
            if self.board[0][i] == self.board[1][i] == self.board[2][i] != ' ':
                return self.board[0][i]
        if self.board[0][0] == self.board[1][1] == self.board[2][2] != ' ':
            return self.board[0][0]
        if self.board[0][2] == self.board[1][1] == self.board[2][0] != ' ':
            return self.board[0][2]
        return None

    def machine_move(self):
        for i in range(3):
            for j in range(3):
                if self.board[i][j] == ' ':
                    self.board[i][j] = 'O'
                    if self.check_winner() == 'O':
                        return (i, j)
                    self.board[i][j] = ' '
                    self.board[i][j] = 'X'
                    if self.check_winner() == 'X':
                        self.board[i][j] = 'O'
                        return (i, j)
                    self.board[i][j] = ' '

        for i in range(3):
            for j in range(3):
                if self.board[i][j] == ' ':
                    self.board[i][j] = 'O'
                    return (i, j)


    def available_moves(self):
        moves = []
        for i in range(3):
            for j in range(3):
                if self.board[i][j] == ' ':
                    moves.append({"row": i, "col": j})
        return moves

Nachdem wir nun automatische Züge in das Tic-Tac-Toe-Spiel in Python implementiert haben, müssen wir diese Funktionalität in unsere FastAPI integrieren. Dieser Schritt ist notwendig, um eine effiziente und nahtlose Interaktion zwischen dem Spiel und dem Backend zu gewährleisten, was den Weg für zukünftige Integrationen, wie die Implementierung eines MQL5-Agenten, ebnet.

Damit die API automatische Züge unterstützen kann, müssen wir einige wichtige Änderungen am Code vornehmen. Lassen Sie uns nun die für diese Integration erforderlichen Schritte im Detail durchgehen.


Schritte zur Anpassung von FastAPI an automatische Züge
  1. Verbessertes Zug-Management: Die API sollte korrekt ermitteln, wer an der Reihe ist, der Spieler oder die Maschine. Nach jedem Zug eines Spielers muss die API prüfen, ob die Maschine an der Reihe ist, und, falls ja, die automatische Zuglogik aktivieren.

  2. Integration mit der Logik von machine_move: Die Funktion machine_move im Code des Spiels Tic-Tac-Toe ist für automatische Züge erforderlich. Daher muss die API nach jedem Zug des Spielers diese Methode aufrufen, um die Reaktion der Maschine zu ermitteln.

  3. Konsistente Aktualisierung der Spielstatusinformationen: Nach jedem Zug, egal ob Spieler oder Maschine, muss die API aktualisiert werden, um den Zustand des Spielbretts genau wiederzugeben. Dadurch wird sichergestellt, dass der Spieler immer aktuelle und korrekte Informationen über den Spielverlauf erhält.

  4. Behandlung von Spielergebnissen: Die API sollte in der Lage sein, das Ende des Spiels (Sieg oder Unentschieden) zu ermitteln und entsprechend zu melden. Es ist sehr wichtig, dass die API klare Informationen über den Gewinner des Spiels liefert oder ein Unentschieden erklärt, wenn ein Sieg nicht möglich ist.

  5. Klare und aussagekräftige Antworten: Die API muss als Antwort alle erforderlichen Daten liefern, z. B. den aktuellen Zustand des Spielfelds, die von der Maschine ausgeführten Züge und das etwaige Ergebnis des Spiels. All dies gewährleistet eine reibungslose und informative Nutzererfahrung.


Beispiel für die Implementierung in API

Um diese Änderungen zu berücksichtigen, werden wir die Abspielfunktion in der API ändern:

@app.post("/play/{game_id}/")
def play(game_id: int, move: PlayerMove):
    game = games.get(game_id)
    if not game:
        raise HTTPException(status_code=404, detail="Game not found")

    board = game.board
    if board[move.row][move.col] == ' ':
        board[move.row][move.col] = 'X'
    else:
        raise HTTPException(status_code=400, detail="Invalid move")

    player_move = {"row": move.row, "col": move.col, "symbol": 'X'}

    winner = game.check_winner()
    machine_move_result = None

    if not winner:
        game.player_turn = not game.player_turn
        if not game.player_turn:
            move_result = game.machine_move()
            if move_result:
                row, col = move_result
                machine_move_result = {"row": row, "col": col, "symbol": 'O'}
                winner = game.check_winner()
            game.player_turn = not game.player_turn

    return {
        "board": board,
        "player_move": player_move,
        "machine_move": machine_move_result,
        "winner": winner,
        "available_moves": game.available_moves()
    }


Wir implementieren die Funktionalität der automatischen Züge im Tic-Tac-Toe-Spiel in Python und passen FastAPI an, um diese Funktionalität zu unterstützen, um ein vollwertiges interaktives Spielsystem zu schaffen. Da die API nun so konfiguriert ist, dass sie automatische Züge verarbeiten kann, können die Spieler auf dynamischere Weise mit dem Spiel interagieren. Wenn ein Spieler einen Zug macht, prüft die API, ob die Maschine an der Reihe ist, und wenn ja, aktiviert sie die automatische Zuglogik. So entsteht ein kontinuierliches interaktives Spielerlebnis, bei dem ein Mensch gegen eine künstliche Intelligenz um den Sieg kämpft.

Darüber hinaus liefert die API detaillierte Informationen über den aktuellen Stand des Spiels, einschließlich des aktualisierten Spielbretts, der von der Maschine ausgeführten Züge und des Spielergebnisses, d. h. ob einer der Spieler gewinnt oder unentschieden spielt. Dies macht die Erfahrung des Spielers fesselnder und informativer.


Erstellen von Testskripten in MQL5

Im vorherigen Artikel haben wir gelernt, wie man HTTP-Anfragen in MQL5 erstellt und verwaltet. Jetzt werden wir dieses Wissen anwenden, um robuste Unit-Tests zu entwickeln. Wir entwerfen jede Testfunktion, um reale Interaktionsszenarien zwischen MQL5-Code und der REST-API zu simulieren und so sicherzustellen, dass alle Aspekte der Interaktion getestet und überprüft werden.

Die Tests decken alles ab, von der Initialisierung des Spiels über richtige und falsche Züge bis hin zur Überprüfung der Bedingungen für den Sieg. Damit wird auch eine solide Grundlage für künftige Erweiterungen und Integrationen geschaffen.

Am Ende dieses Themas wird der Leser ein klares Verständnis davon haben, wie Unit-Tests in MQL5 strukturiert und implementiert sind und wie wichtig sie für die Systementwicklung sind.


Test Struktur


Der Testcode in MQL5 ist in drei Hauptdateien organisiert:

  1. Tests.mqh enthält Testfunktionen.
  2. Request.mqh bearbeitet HTTP-Anfragen.
  3. Tests.mq5 ist das Hauptskript, das die Tests ausführt.


Tests.mqh

  • Assert(): Diese Funktion wird verwendet, um zu prüfen, ob eine bestimmte Bedingung erfüllt ist. Es ist sehr wichtig, die erwarteten Testergebnisse zu bestätigen.

    Code und Erklärung:

    void Assert(bool condition, const string message) {
      if(!condition) {
        Print("Test error: ", message);
      }
    }
    

    Wenn die an Assert übergebene Bedingung falsch ist, wird eine Fehlermeldung ausgegeben. Auf diese Weise können Sie Fehler bei den Tests schnell erkennen.

    • TestGameInitialisation(): Überprüft die Initialisierung des neuen Spiels und stellt sicher, dass die API korrekt antwortet.
    Code und Erklärung:
    void TestGameInitialization() {
      string url = "http://localhost:8000/start-game/";
      string response;
      int result = Request("GET", response, url);
      Assert(result == 200, "Game initialization failed");
      Assert(StringLen(response) > 0, "game_id missing in game initialization response");
    }This function makes a GET request to start a game and checks if the response code is 200 (OK) and if 
    game_id is returned in the response.
    

      Diese Funktion stellt eine GET-Anfrage zum Starten eines Spiels und prüft, ob der Antwortcode 200 (OK) lautet und ob die game_id in der Antwort zurückgegeben wird.


      • TestPlayerMove(): Testet die Funktionalität des Spielers, der den richtigen Zug macht.

        Code und Erklärung:

        // Test function to check player's move
        void TestPlayerMove()
          {
           string url = "http://localhost:8000/start-game/";
           string response;
           int result = -1;
           int game_id = -1;
        
           Request("GET", response, url);
        
           js.Deserialize(response);
           game_id = js["game_id"].ToStr();
        
        // Make a valid player move
           url = StringFormat("http://localhost:8000/play/%d/", game_id);
           string payload = "{\"row\": 0, \"col\": 0}";
           result = Request("POST", response, url, payload);
        
        // Check if the HTTP response code is 200 (OK)
           Assert(result == 200, "Player move failed");
        
        // Check if the response contains information about the player's move
        // (you can adjust this based on the actual response structure)
           Assert(StringFind(response, "player_move") != -1, "Player move response incomplete");
          }
        

        Sobald das Spiel beginnt, führt diese Funktion einen gültigen Zug aus und prüft, ob die API den Zug korrekt verarbeitet, wobei der Code 200 und die Zuginformationen im Antwortkörper zurückgegeben werden.


        • TestInvalidPlayerMove(): Überprüft die API-Antwort auf ungültige Bewegungen.

          Code und Erklärung:

          // Test function to check an invalid player move
          void TestInvalidPlayerMove()
            {
             string url = "http://localhost:8000/start-game/";
             string response;
             int result = -1;
             int game_id = -1;
          
             Request("GET", response, url);
          
             js.Deserialize(response);
             game_id = js["game_id"].ToStr();
          
          // Make an invalid player move (e.g., on an occupied position)
             url = StringFormat("http://localhost:8000/play/%d/", game_id);
             string payload = "{\"row\": 0, \"col\": 0}";
             Request("POST", response, url, payload);
          
          //repeat
             payload = "{\"row\": 0, \"col\": 0}";
             result = Request("POST", response, url, payload);
          
          // Check if the HTTP response code is 400 (Bad Request)
             Assert(result == 400, "Invalid player move not handled correctly");
            }
          
            Diese Funktion versucht, einen ungültigen Zug auszuführen (z. B. in einer bereits besetzten Stellung zu spielen) und überprüft, ob die API den Fehlercode 400 (Bad Request) zurückgibt.

            • TestPlayerWin(): Simuliert die Abfolge der Züge, die zu einem Sieg des Spielers führen.

              Code und Erklärung:
              // Test function to check player's victory
              void TestPlayerWin()
                {
                 string url = "http://localhost:8000/start-game/";
                 string response;
                 int result = -1;
                 int game_id = -1;
              
                 Request("GET", response, url);
              
                 js.Deserialize(response);
                 game_id = js["game_id"].ToStr();
              
              // Make moves for player X to win
                 url = StringFormat("http://localhost:8000/play/%d/", game_id);
              
                 string payload = "{\"row\": 0, \"col\": 0}";
                 result = Request("POST", response, url, payload);
                 Assert(result == 200, "Player X move 1 failed");
              
                 payload = "{\"row\": 0, \"col\": 2}";
                 result = Request("POST", response, url, payload);
                 Assert(result == 200, "Player X move 2 failed");
              
                 payload = "{\"row\": 2, \"col\": 2}";
                 result = Request("POST", response, url, payload);
                 Assert(result == 200, "Player X move 3 failed");
              
                 payload = "{\"row\": 1, \"col\": 2}";
                 result = Request("POST", response, url, payload);
                 Assert(result == 200, "Player X move 4 failed");
              
              // Check if the response contains information about the winner
                 js.Deserialize(response); // Deserialize the updated game state
              
              // Check if the HTTP response code is 200 (OK) after move 5
                 Assert(result == 200 && js["winner"].ToStr() == "X", "Player X victory failed");
              
                }
              

                Diese Funktion führt eine Reihe von Zügen aus, die zum Gewinn des Spielers führen, und prüft außerdem, ob die API die Gewinnbedingung korrekt erkennt.

                • RunTests(): Eine Aggregatorfunktion, die alle definierten Tests durchführt.
                Code und Erklärung:
                  // Function to run all tests
                  void RunTests()
                    {
                     TestGameInitialization();
                     TestPlayerMove();
                     TestInvalidPlayerMove();
                     TestPlayerWin();
                    }
                  

                  Diese Funktion ruft einfach alle zuvor beschriebenen Testfunktionen auf und führt alle Tests auf einmal durch.

                  Zuvor haben wir die Requests-Bibliothek für die Interaktion zwischen MQL5-Code und der REST-API erstellt. In diesem Artikel werden wir diese Bibliothek weiter verwenden, allerdings mit einigen wesentlichen Verbesserungen, um ihre Funktionalität und Effizienz zu steigern.


                  Änderungen in der Anforderungsbibliothek

                  Die neue Implementierung der Requests-Bibliothek weist Verbesserungen auf, die hauptsächlich auf Flexibilität und Fehlerdiagnose abzielen. Schauen wir uns die wichtigsten Änderungen an:

                  1. Aktivieren Sie die Debug-Option:

                    • Alte Implementierung: In früheren Versionen bot die Bibliothek keine Debugging-Option als solche. Jede Debug-Ausgabe musste manuell implementiert werden.
                    • Die Implementierungen der Funktionen SendGetRequest und SendPostRequest enthalten jetzt eine Debug-Option. Wenn diese Option aktiviert ist, können Sie Debug-Daten ausdrucken, was die Verfolgung und Behebung von Problemen erleichtert.

                  2. Verbesserte Fehlerbehandlung:

                    • Alte Implementierung: Die Fehlerbehandlung war einfacher; es wurde nur ein Fehlercode zurückgegeben oder direkt auf der Konsole ausgegeben.
                    • Die neue Version bietet eine ausgefeiltere Fehlerbehandlung, mit der Sie HTTP-Kommunikationsprobleme besser diagnostizieren können.


                  Warum werden diese Änderungen vorgenommen? Die Änderungen an der Anforderungsbibliothek wurden durch die Notwendigkeit motiviert,:

                  • Verbesserte Fehlersuche: Die Möglichkeit, das Debugging ein- oder auszuschalten, erleichtert die Entwicklung und Wartung, da die Entwickler die API-Antworten schnell analysieren und Probleme erkennen können.
                  • Verbesserung des Fehlermanagements: Eine effizientere Fehlerbehandlung ist für zuverlässige Systeme unerlässlich, insbesondere bei der Netzkommunikation, wo eine Vielzahl von Problemen auftreten kann.


                  Auswirkungen der Änderungen

                  Diese Verbesserungen machen die Requests-Bibliothek noch vielseitiger und zuverlässiger. Dank der Debugging-Funktionen können Entwickler bei der Entwicklung und beim Testen ihrer Anwendungen schneller Feedback erhalten. Darüber hinaus trägt eine effizientere Fehlerbehandlung dazu bei, Probleme bei der API-Interaktion zu erkennen und zu beheben, was zu einer reibungsloseren und zuverlässigeren Integration führt.


                  Änderung 1: Aktivieren der Debug-Option

                  Frühere Implementierungen von SendGetRequest und SendPostRequest:

                  // Example of old SendGetRequest implementation
                  int SendGetRequest(const string url, const string query_param, string &out, string headers = "", const int timeout = 5000) {
                     // ... code ...
                     out = CharArrayToString(result, start_index, WHOLE_ARRAY, CP_UTF8);
                     return (0);
                  }
                  
                  

                  Neue Implementierung mit Debugging:

                  // Example of new SendGetRequest implementation
                  int SendGetRequest(const string url, const string query_param, string &out, string headers = "", const int timeout = 5000, bool debug=false) {
                     // ... code ...
                     out = CharArrayToString(result, start_index, WHOLE_ARRAY, CP_UTF8);
                     if(debug) {
                         Print(out);
                     }
                     return res;
                  }
                  
                  

                  Die neue Implementierung enthält eine Debug-Option. Wenn sie wahr ist, gibt die Funktion das Ergebnis aus, was die Fehlersuche erleichtert.


                  Änderung 2: Verbesserung der Fehlerbehandlung

                  Frühere Implementierungen von SendGetRequest und SendPostRequest:

                  // Example of old implementation of SendGetRequest
                  int SendGetRequest(const string url, const string query_param, string &out, string headers = "", const int timeout = 5000) {
                     // ... code ...
                     if(res == -1) {
                         return (_LastError);
                     } else {
                         // HTTP error handling
                         out = CharArrayToString(result, 0, WHOLE_ARRAY, CP_UTF8);
                         Print(out);
                         return (ERR_HTTP_ERROR_FIRST + res);
                     }
                  }
                  
                  

                  Neue Implementierung mit verbesserter Fehlerbehandlung:

                  // Example if new implementation of SendGetRequest
                  int SendGetRequest(const string url, const string query_param, string &out, string headers = "", const int timeout = 5000, bool debug=false) {
                     // ... code ...
                     if(res == -1) {
                         return (_LastError);
                     } else {
                         // HTTP error handling
                         out = CharArrayToString(result, 0, WHOLE_ARRAY, CP_UTF8);
                         if(debug) {
                             Print(out);
                         }
                         return res;
                     }
                  }
                  
                  

                  In der neuen Implementierung ist die HTTP-Fehlerbehandlung genauer geworden, da Daten nur dann ausgegeben werden können, wenn der Debugging-Modus aktiviert ist.


                  Integration und praktische Tests

                  In dieser entscheidenden Phase unseres Projekts werden wir die Integration der in der Requests-Bibliothek implementierten Verbesserungen und automatischen Züge in einem Tic-Tac-Toe-Spiel in Python demonstrieren. Darüber hinaus werden wir praktische Tests durchführen, um die Effizienz und Zuverlässigkeit des Gesamtsystems zu bewerten. Dieser Schritt ist notwendig, um sicherzustellen, dass alle Teile unseres Systems reibungslos und zuverlässig funktionieren.

                  Phasen der Integration

                  Bevor wir mit dem Testen beginnen, ist es wichtig zu verstehen, wie die neue Funktionalität integriert wurde. Im Folgenden werden die wichtigsten Schritte zur Gewährleistung einer reibungslosen Integration beschrieben:

                  1. Aktualisierung der Anforderungsbibliothek: Zunächst haben wir die Anforderungsbibliothek aktualisiert, um die von uns eingeführten Verbesserungen zu berücksichtigen. Dies ermöglichte eine effektivere Kommunikation.

                  2. Tic-Tac-Toe-Anpassung: Ein wichtiger Schritt war die Integration von automatischen Zügen in das Tic-Tac-Toe-Spiel in Python. Wir haben den Spielcode so geändert, dass er den Zug der Maschine erkennt und die Logik der automatischen Züge aktiviert.

                  3. Implementierung der API-Integration: Als Nächstes haben wir FastAPI angepasst, um automatische Züge zu unterstützen. Dazu gehören eine verbesserte Zugkontrolle, die Integration mit der Funktion machine_move, die ständige Aktualisierung des Spielstatus und die korrekte Verarbeitung von Spielergebnissen.

                  Durchführung der Tests

                  Nun, da die Integration abgeschlossen ist, ist es an der Zeit, einige praktische Tests durchzuführen, um sicherzustellen, dass alles wie erwartet funktioniert. Wir haben eine Reihe von Tests, die verschiedene Aspekte unserer Implementierung abdecken:

                  1. Test zur Erstellung eines neuen Spiels (Swagger und Testskript): Um sicherzustellen, dass die Erstellung eines neuen Spiels reibungslos abläuft, werden wir diesen Test sowohl über die Swagger-Schnittstelle als auch über unser automatisiertes Testskript durchführen. Dadurch wird sichergestellt, dass die Funktionalität in beiden Richtungen verfügbar ist.

                  2. Test der Spielerbewegungen (Swagger und Testskript): Die Funktionalität eines Spielers, der einen gültigen Zug ausführt, wird sowohl mit Swagger als auch mit einem Testskript getestet.

                  3. Test für ungültige Züge des Spielers: Um sicherzustellen, dass die API mit ungültigen Zügen korrekt umgeht, führen wir einen Test durch, bei dem wir versuchen, einen Zug auf eine bereits besetzte Position auszuführen, und prüfen, ob die API den entsprechenden Fehlercode zurückgibt.

                  4. Test zum Gewinnen eines Spielers: Wir simulieren eine Folge von Zügen, sodass ein Spieler gewinnt, um sicherzustellen, dass die API die Gewinnbedingung korrekt erkennt.

                  Ergebnisse und Bewertung

                  Wir erwarten, dass wir nach der Durchführung dieser Tests zuverlässige Ergebnisse erhalten, die die Integrität unseres integrierten Systems bestätigen. Wir werden bewerten, ob die automatisierten Spiele wie erwartet funktionieren, ob Fehler wie erwartet behandelt werden und ob die API klare und informative Antworten liefert.

                  Diese praktischen Tests sind notwendig, um sicherzustellen, dass unser System für die Interoperabilität in der Praxis und künftige Erweiterungen bereit ist. Mit erfolgreicher Integration und gründlichen Tests sind wir der Schaffung eines zuverlässigen und effizienten Spielsystems näher gekommen.




                  In den obigen Bildern sehen wir den Prozess des Testens der Funktionalität der Erstellung eines neuen Spiels, das in unserer Anwendung eine wichtige Rolle spielt. Der Test wird umfassend und konsistent durchgeführt, wobei die Zugänglichkeit der Funktion auf zwei verschiedene Arten geprüft wird: über die Swagger-Schnittstelle und unser automatisiertes Testskript.

                  Swagger, ein API-Dokumentations- und Testwerkzeug, ermöglicht es Entwicklern, visuell und effektiv mit der API zu interagieren. In der Abbildung ist zu sehen, wie die Erstellung eines neuen Spiels direkt über die Swagger-Schnittstelle gestartet und getestet wird, um sicherzustellen, dass die Funktionalität leicht zugänglich ist und wie erwartet funktioniert.

                  Darüber hinaus umfasst der Testprozess auch die Verwendung eines automatisierten Testskripts, das strenge Prüfungen durchführt, um die Konsistenz und Zuverlässigkeit der Funktionalität zu gewährleisten. Dies zeigt unser Engagement für die Aufrechterhaltung der Systemqualität, unabhängig von der verwendeten Prüfmethode.


                  Schlussfolgerung

                  Dieser Artikel, der die Serie über REST-APIs fortsetzt, konzentrierte sich auf die Implementierung und das Testen von automatischen Zügen im Tic-Tac-Toe-Spiel in Python, integriert mit der Entwicklung von MQL5-Funktionen. Die Hauptaufgabe ging in zwei Richtungen: die Verbesserung des Tic-Tac-Toe-Spiels durch ein autonomes Spiel und die Entwicklung von Unit-Tests in MQL5, um die Interaktion mit der REST-API zu testen. Die Integration dieser Funktionen verbessert nicht nur das Spiel selbst, sondern schafft auch die Grundlage für vollständigere und effektivere Tests.

                  Bei der Entwicklung automatischer Spielzüge war es sehr wichtig, die bestehende Logik des Spiels zu verstehen, um neue Funktionen effektiv und harmonisch zu implementieren. Die gewählte Strategie geht von einem einfachen, aber effektiven Ansatz aus, der es Ihnen ermöglicht, das Spiel komplexer und dynamischer zu gestalten. Darüber hinaus ist die Vorbereitung der Umgebung für die Implementierung eines MQL5-Agenten, der einen echten Nutzer simuliert, ein wichtiger Schritt zu realistischeren Tests.

                  Was die Tests betrifft, so wurde durch die Erstellung und Implementierung von Testskripten in MQL5 eine zuverlässige Verbindung zwischen dem MQL5-Code und der REST-API sichergestellt. Die Tests umfassten die Initialisierung des Spiels, die Durchführung von richtigen und falschen Zügen und die Überprüfung der Gewinnbedingungen: Dies ist notwendig, um die Zuverlässigkeit und Stabilität des Systems zu gewährleisten.

                  Die in der Requests-Bibliothek implementierten Verbesserungen, wie die Aktivierung einer Debugging-Option und eine ausgefeiltere Fehlerbehandlung, haben die Effizienz der Interaktion erheblich verbessert und auch die Diagnose von Problemen erleichtert.

                  Schließlich bestätigte die Integrations- und praktische Testphase die Wirksamkeit der implementierten Verbesserungen sowohl beim Tic-Tac-Toe-Spiel als auch bei der Anforderungsbibliothek. Die Tests mit der Swagger-Schnittstelle und den automatisierten Testskripten bestätigten die Funktionalität und Zuverlässigkeit des gesamten Systems.

                  Dieser Artikel zeigt, wie die Kombination aus automatisiertem Spiel und Unit-Tests in MQL5 einen robusten Entwicklungszyklus schafft, der ein zuverlässiges und effizientes integriertes System bereitstellt, das in der Lage ist, ein verbessertes Spielerlebnis und wertvolle Erkenntnisse für zukünftige Integrationen und Entwicklungen zu liefern.



                  Übersetzt aus dem Portugiesischen von MetaQuotes Ltd.
                  Originalartikel: https://www.mql5.com/pt/articles/13813

                  Beigefügte Dateien |
                  Parte_03.zip (65.15 KB)
                  Entwicklung eines Replay Systems (Teil 38): Den Weg ebnen (II) Entwicklung eines Replay Systems (Teil 38): Den Weg ebnen (II)
                  Viele Menschen, die sich für MQL5-Programmierer halten, verfügen nicht über die Grundkenntnisse, die ich in diesem Artikel erläutern werde. Viele Menschen halten MQL5 für ein begrenztes Werkzeug, aber der eigentliche Grund ist, dass sie nicht über die erforderlichen Kenntnisse verfügen. Wenn Sie also etwas nicht wissen, brauchen Sie sich dafür nicht zu schämen. Es ist besser, sich dafür zu schämen, nicht zu fragen. MetaTrader 5 einfach dazu zu zwingen, die Indikatorduplikation zu deaktivieren, gewährleistet in keiner Weise eine Zwei-Wege-Kommunikation zwischen dem Indikator und dem Expert Advisor. Davon sind wir noch weit entfernt, aber die Tatsache, dass sich der Indikator auf dem Chart nicht dupliziert, stimmt uns zuversichtlich.
                  Trailing-Stopp im Handel Trailing-Stopp im Handel
                  In diesem Artikel befassen wir uns mit der Verwendung eines Trailing-Stops beim Handel. Wir werden bewerten, wie nützlich und wirksam das ist und wie es genutzt werden kann. Die Effizienz eines Trailing-Stopps hängt weitgehend von der Preisvolatilität und der Wahl des Stop-Loss-Niveaus ab. Für die Festlegung eines Stop-Loss können verschiedene Ansätze verwendet werden.
                  Algorithmen zur Optimierung mit Populationen: Evolution sozialer Gruppen (ESG) Algorithmen zur Optimierung mit Populationen: Evolution sozialer Gruppen (ESG)
                  Wir werden das Prinzip des Aufbaus von Algorithmen mit mehreren Populationen besprechen. Als Beispiel für diese Art von Algorithmus werden wir uns den neuen nutzerdefinierten Algorithmus - Evolution of Social Groups (ESG) - ansehen. Wir werden die grundlegenden Konzepte, die Mechanismen der Populationsinteraktion und die Vorteile dieses Algorithmus analysieren und seine Leistung bei Optimierungsproblemen untersuchen.
                  DRAW_ARROW Zeichnungstyp in Multi-Symbol-Multi-Perioden-Indikatoren DRAW_ARROW Zeichnungstyp in Multi-Symbol-Multi-Perioden-Indikatoren
                  In diesem Artikel werden wir uns mit Multi-Symbol-Multi-Perioden-Indikatoren beschäftigen, die Pfeile zeichnen. Wir werden auch die Klassenmethoden für die korrekte Anzeige von Pfeilen verbessern, die Daten von Pfeilindikatoren anzeigen, die auf einem Symbol/einer Periode berechnet wurden, das/die nicht mit dem Symbol/der Periode des aktuellen Charts übereinstimmt.