English Русский Español Deutsch Português
preview
RestAPIを統合したMQL5強化学習エージェントの開発(第3回):MQL5で自動手番とテストスクリプトを作成する

RestAPIを統合したMQL5強化学習エージェントの開発(第3回):MQL5で自動手番とテストスクリプトを作成する

MetaTrader 5 | 18 6月 2024, 10:33
208 0
Jonathan Pereira
Jonathan Pereira

はじめに

連載3回目となる今回は、システムにおけるREST APIの実践的な応用について掘り下げていきます。MQL5関数の開発と、FastAPIを介したPythonの三目並べとの統合を見てきました。この新しい記事では、大きな進歩を遂げることができます。対戦の難易度とインタラクティブ性を高めるため、三目並べの手の自動化の実装に重点を置きます。また、統合システムの信頼性と効率を確保するため、MQL5によるテストスクリプトの開発にも特別な注意を払っていきます。

このプロジェクトの複雑さを理解した上で、インストールと実行について明確な指示を出す必要があると思われます。前回この情報を含めなかったことを後悔しています。ご理解に感謝します。そこで、スクリプトの制限に遭遇する可能性のあるWindowsユーザーへの推奨も含め、ステップバイステップのガイドを紹介します。

インストールおよび実装ガイド

前提条件

  • Python 3.6以上
  • MetaTrader 5がインストールされている
  • Windowsでは、スクリプトが許可されていることを確認する:必要に応じて、PowerShellのSet-ExecutionPolicy RemoteSignedを管理者として実行し、スクリプトの実行を許可する

インストールと実装の手順

  1. プロジェクトをダウンロードして解凍する:記事のプロジェクトをダウンロードした後、ファイルを目的のフォルダに解凍します。
  2. Expertsフォルダにコピーする:解凍したフォルダをMetaTrader端末のExpertsフォルダに移動します。
  3. 端末でフォルダを開く:
    • Windowsの場合は、スタートメニューからコマンドプロンプトまたはPowerShellを検索してプログラムを開き、cd folder_pathコマンドを使用して対応するプロジェクトフォルダに移動します。
    • MacOSまたはLinuxでは、Terminalを開き、cd folder_pathコマンドを実行します。
  4. 仮想環境を作成する:端末でプロジェクトフォルダを開き、python -m venv envを実行して仮想環境を作成します。
  5. 仮想環境をアクティブにする:Windowsでは、envScriptsactivateコマンドを実行します。MacOSまたはLinuxでは、sourceenv/bin/activateコマンドを実行します。
  6. 依存関係をインストールする:仮想環境を有効にしたら、pip install -r requirements.txtを実行します。

プロジェクトの実装

  • 三目並べAPIを実行するには、プロジェクトフォルダで端末を開き、python AppTicTacToe.pyを実行します。
  • MetaTraderで実行するには、[ツール]から[MetaQuotes言語エディタ](MetaEditor)を開き、MetaEditor内で[ツール] > [オプション] > [コンパイラ]に進みます。仮想環境のscriptsフォルダへのパスを[External compile location]に貼り付け、コンパイルボタンをクリックするか、チャート上にスクリプトをドラッグします。
  • Swagger UIにアクセスします。ブラウザでlocalhost:8000/docsにアクセスし、Swagger UIからAPIを操作します。

現在の作業の主な目標は2つあります。1つ目は、Pythonで三目並べを改良し、インテリジェントな意思決定アルゴリズムを使用して自律的に動きを実行するようにすることです。2つ目は、MQL5のユニットテストの開発と実装で、MQL5のコードとREST API間の相互作用の信頼性を確認し、保証することです。

この記事は3部構成になっています。

  1. 三目並べにおける自動手番の開発:自動手番のロジックを追加するために対戦を修正するプロセスについて、使用したプログラミング方法や遭遇した困難を含めて記述しています。
  2. MQL5言語でのテストスクリプトの作成:MQL5とREST API間の相互作用をテストする方法に重点を置いて、MQL5でのユニットテストの開発プロセスを見ていきます。
  3. 実践テストと統合テスト:テストと結果の評価を含め、実施された改良の統合を確認します。
この記事の背景にある考え方は、三目並べの自動手番をPythonで実装することで、より完全で効率的なテストのための強固な基礎を築くというものです。対戦が自律的に反応するため、REST APIとの実際のやりとりをシミュレートするMQL5スクリプトを使用して、徹底的な確認をおこなうことができます。このアプローチは、対戦がさまざまなシナリオで期待通りに動作することを保証するだけでなく、MQL5コードとAPI間の接続の信頼性も確認します。

したがって、三目並べと相互作用するエージェントをMQL5で開発することが次の論理的ステップとなります。このエージェントは実際のユーザーを模倣し、対戦中に手を打ち、アクションに反応することで、現実に近いテスト環境を作り出すことができます。この戦略により、対戦とAPIの機能をテストするだけでなく、自動化された対戦の意思決定アルゴリズムを研究(改善)し、より複雑で魅力的な対戦体験を提供することができます。

MQL5の三目並べ自動手番とユニットテストの組み合わせは、対戦のあらゆる改良が厳密なテストを通じてテストされ、改良される、強固な開発サイクルを生み出します。継続的な開発とテストのプロセスにより、信頼性が高く効率的な統合システムの構築が保証され、対戦体験の向上だけでなく、将来的な統合や統合が必要なシステムの開発のための貴重な洞察を提供することができます。


 


自動手番の開発

このセクションでは、既存の対戦コードの構造とロジックを理解することに焦点を当てます。この対戦にはグラフィカルインターフェイスはなく、複雑な意思決定アルゴリズムは自動手番の実装には使用されないので、私たちの目標はこのプロセスを単純化し、最適化することです。

最初のステップは、対戦がどのように手動手番を処理するかを分析することで、手番を駆動するロジックと、それがどのように勝ち負け(または引き分け)の状態を決定するかを見ることです。このプロセスを理解することは、既存の対戦メカニクスを壊すことなく自動手番機能を統合するために必要です。

将来的には、自動手番はよりシンプルな方法で実装されるでしょう。複雑なアルゴリズムの代わりに、より直接的なアプローチを選択することもできます。例えば、自動手番のために、空いているマスをランダムに選択するといった方法です。シンプルではありますが、このアプローチで十分にプレイヤーを真似ることができ、対戦をダイナミックにすることができるはずです。

古い対戦コードをよく見て、その仕組みを理解しましょう。これは、対戦を複雑にすることなく自動手番を追加する方法を計画できるようにするために必要なことです。目標は、すでに対戦にあるシンプルなスタイルを守りながら、すべてをユーザーフレンドリーにすることです。

対戦の初期化

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

ここでは、対戦は空のボード(self.board)で初期化され、変数self.player_turnはプレイヤーの順番を示します。ミニマルなデザインは、既存のコードを複雑にすることなく、自動化された対戦ロジックを統合するための理想的な出発点となります。

ボードの表示

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

print_boardメソッドは、格子の表示を処理します。対戦状態の表示方法は、特に自動手番の導入後、使いやすさと理解を維持するために非常に重要です。


勝者を確認します。

 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 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.")

make_moveメソッドはプレイヤーの手番を担当します。自動手番を実現するには、手動と自動を切り替えるためにこのメソッドを修正する必要があります。


自動手番の実行

三目並べに自動手番を導入することは、新しい挑戦であり、楽しいものです。machine_moveという新しいメソッドを使用して実装してみましょう。まず、勝つ方法を見つけるか、相手の勝利を阻止しようとします。もしそのような機会がなければ、格子の空いているマスをランダムに選択します。

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



available_movesメソッドも用意します。この方法は、格子を表示し、すべての空のマスを表示することができるので、非常に重要です。こうすることで、コンピュータが空のマスにしか手を打たないことを保証します。

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


こうした変化のおかげで、三目並べはより面白くなりました。シンプルであることに変わりはありませんが、自動手番のおかげで驚きと戦略の要素が加わりました。これが対戦をさらにエキサイティングなものにしています。

Pythonの三目並べに自動手番を組み込むことで、複雑さとインタラクティブ性が加わり、MQL5エージェントの実装のような将来の高度な実装への道が開かれます。この開発段階が終了すると、対戦はプレイヤーにとってより複雑になるだけでなく、他の外部エージェントとのより複雑な相互作用の準備が整うということです。

自動手番のロジックは最初は単純ですが、対戦の基礎を固めます。自律的に手を打ち、対戦の状況に動的に対応する能力を持つこのシステムは、リアルな対戦相手をシミュレートするのに適しています。これは、特にMQL5エージェントの将来の実装に備えて、様々なシナリオにおける対戦の有効性と安定性をテストするために必要です。

このエージェントの実装を計画する場合、実際の対戦環境をシミュレートするためには自動手番を持つ三目並べがすでに装備されています。エージェントは、2人の人間のプレイヤー間の対戦に近いスクリプトで、対戦と対話し、手を打ち、自律的なアクションに反応することができます。このような相互作用により、対戦やAPIの機能性だけでなく、使用されている意思決定アルゴリズムの有効性も評価することができ、改善や修正への道が開かれます。

さらに、MQL5エージェントの存在は、より高度で現実的なテスト環境を提供します。これにより、さまざまな対戦シナリオをシミュレートし、さまざまな条件下でのシステムの反応をテストし、対戦の安定性と信頼性を確保することができます。

以下は完全な対戦コードです。

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

Pythonで三目並べの自動手番を実装したので、この機能をFastAPIと統合する必要があります。このステップは、対戦とバックエンド間の効率的でシームレスなインタラクションを確保するために必要であり、MQL5エージェントの実装など、将来の統合への道を開きます。

APIが自動手番をサポートするためには、コードにいくつかの重要な変更を加える必要があります。それでは、この統合に必要な手順を詳しく見ていきましょう。


FastAPIを自動手番に適応させる手順
  1. 順番の管理の改善:APIは人間の手番かマシンの手番かを正しく判断しなければなりません。各プレイヤーの動きの後、APIはマシンの手番かどうかを確認し、マシンの手番であれば自動手番ロジックを起動しなければなりません。

  2. machine_moveロジックとの統合:三目並べのコードにあるmachine_move関数は、自動的な移動に必要です。したがって、各プレイヤーの動きの後、APIはこのメソッドを呼び出してマシンの反応を決定しなければなりません。

  3. 対戦状態情報の一貫した更新:人間であれマシンであれ、各手番の後、APIは格子の状態を正確に反映するように更新されなければなりません。これにより、プレイヤーは常に対戦の進行に関する最新かつ正確な情報を受け取ることができます。

  4. 対戦結果の処理:APIは対戦の終了(勝ちか引き分けか)を判断し、それに従って報告できるようにします。APIは対戦の勝者について明確な情報を提供するか、勝利が不可能な場合は引き分けを宣言することが非常に重要です。

  5. 明確で意味のある回答:APIは、現在の対戦の状態、マシンが打った手、対戦の結果(もしあれば)など、必要なデータをすべてレスポンスとして提供しなければなりません。これらすべてが、スムーズで有益なユーザー体験を保証します。


APIでの実装例

これらの変更に対応するため、APIのplay関数を変更します。

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


三目並べにおける自動手番の機能をPythonで実装し、この機能をサポートするためにFastAPIを適応させ、本格的な対話型対戦システムを作成します。APIが自動手番を処理するように設定されたので、プレイヤーはよりダイナミックな方法で対戦と対話することができます。プレイヤーが手を打つと、APIはマシンの手番かどうかを確認し、マシンの手番であれば自動手番ロジックを起動します。これにより、人間が人工知能と対戦して勝利を目指す、継続的なインタラクティブな対戦体験が生まれます。

さらに、APIは、更新された盤面、マシンが打った手、勝つか引き分けるかといった対戦の結果など、現在の対戦の状態に関する詳細な情報を提供します。これにより、プレイヤーの体験はより魅力的で有益なものになります。


MQL5でテストスクリプトを作成する

前回の記事では、MQL5でHTTPリクエストを作成して管理する方法を学びました。では、この知識を応用して、ロバストなユニットテストを開発しましょう。MQL5コードとREST API間の実際の相互作用シナリオをシミュレートするために各テスト関数を設計し、相互作用のすべての側面がテストおよび検証されるようにします。

このテストは、対戦の初期化から、正しい手と正しくない手の実行、勝利条件の確認まで、すべてをカバーしています。これはまた、将来の拡張や統合のための強固な基盤を作成することにもなります。

このトピックの最後には、読者はMQL5でユニットテストがどのように構造化され実装されているか、そしてシステム開発にとってユニットテストがいかに重要であるかを明確に理解できるでしょう。


テスト構成


MQL5のテストコードは3つの主要ファイルで構成されています。

  1. Tests.mqhにはテスト関数が含まれている
  2. Request.mqhはHTTPリクエストを処理する
  3. Tests.mq5はテストを実行するメインスクリプト


Tests.mqh

  • Assert():特定の条件が真であるかどうかを確認するために使用されます。予想される検査結果を確認することは非常に重要です。

    コードと説明

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

    Assertに渡された条件が偽の場合、エラーメッセージが表示されます。これにより、テストの失敗を素早く特定することができます。

    • TestGameInitialisation():新しい対戦の初期化を確認し、APIが正しく応答することを確認します。
    コードと説明
    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.
    

      この関数は、対戦を開始するためにGETリクエストをおこない、レスポンスコードが200(OK)かどうか、レスポンスにgame_idが返されているかどうかを確認します。


      • TestPlayerMove():プレイヤーが正しい手を打つ機能をテストします。

        コードと説明

        // 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");
          }
        

        対戦が始まると、この関数は有効な手を打ち、APIがその手を正しく処理したかどうかを確認し、コード200とレスポンスボディにある手の情報を返します。


        • TestInvalidPlayerMove():API レスポンスに無効な手がないか確認します。

          コードと説明

          // 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");
            }
          
            この関数は、無効な手(例えば、既に占拠されたマスに打たれた手)の実行を試み、APIがエラーコード400 (Bad Request)を返すかどうかを確認します。

            • TestPlayerWin():プレイヤーの勝利につながる一連の動きをシミュレートします。

              コードと説明
              // 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");
              
                }
              

                この関数は、プレイヤーが勝利するための一連の動きを実行し、APIが勝利条件を正しく認識しているかどうかも確認します。

                • RunTests():定義されたすべてのテストを実行する集約関数
                コードと説明
                  // Function to run all tests
                  void RunTests()
                    {
                     TestGameInitialization();
                     TestPlayerMove();
                     TestInvalidPlayerMove();
                     TestPlayerWin();
                    }
                  

                  この関数は、先に説明したすべてのテスト関数を呼び出すだけで、すべてのテストを一度に実行します。

                  前回は、MQL5コードとREST APIをやり取りするためのRequestsライブラリを作成しました。この記事では、このライブラリを引き続き使用しますが、機能性と効率性を向上させるためにいくつかの重要な改良を加えました。


                  リクエストライブラリの変更点

                  リクエストライブラリの新しい実装には、主に柔軟性とエラー診断を目的とした改良が加えられています。主な変更点を見てみましょう。

                  1. デバッグオプションを有効にします。

                    • 古い実装:以前のバージョンでは、ライブラリはデバッグオプションを提供していませんでした。デバッグ出力はすべて手動でおこなわなければなりませんでした。
                    • SendGetRequest関数とSendPostRequest関数の実装にデバッグオプションが追加されました。このオプションを有効にすると、デバッグデータを出力できるようになり、問題のトレースやトラブルシューティングが容易になります。

                  2. エラー処理の改善:

                    • 古い実装:エラー処理はよりシンプルで、エラーコードを返すか、コンソールに直接プリントするだけでした。
                    • 新しいバージョンでは、より洗練されたエラーハンドリングが提供され、HTTP通信の問題をより適切に診断できるようになりました。


                  なぜこのような変更がなされたのでしょうか。リクエストライブラリの変更は、その必要性によって動機づけられました。

                  • デバッグの改善:デバッグのオンオフを切り替えることで、開発者はAPIレスポンスを素早く分析し、問題を特定できるため、開発とメンテナンスが容易になります。
                  • エラー管理の改善:信頼性の高いシステムには、より効率的なエラー処理が不可欠であり、特にさまざまな問題が発生する可能性のあるネットワーク通信に関してはなおさらです。


                  変更の影響

                  これらの改善により、Requestsライブラリはより多機能で信頼性の高いものとなりました。デバッグ機能により、開発者はアプリケーションの開発とテストにおいて、より迅速なフィードバックを得ることができます。さらに、より効率的なエラー処理は、APIインタラクションの問題を特定し解決するのに役立ち、よりスムーズで信頼性の高い統合を実現します。


                  変更点1:デバッグオプションを有効にする

                  SendGetRequestおよび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);
                  }
                  
                  

                  デバッグ機能を備えた新しい実装は次の通りです。

                  // 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;
                  }
                  
                  

                  新しい実装にはデバッグオプションが含まれています。もしtrueなら、この関数は結果を表示し、デバッグを容易にします。


                  変更点2:エラー処理の改善

                  SendGetRequest および 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);
                     }
                  }
                  
                  

                  エラー処理を改善した新しい実装は次の通りです。

                  // 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;
                     }
                  }
                  
                  

                  新しい実装では、HTTPエラー処理がより正確になり、デバッグモードが有効な場合にのみデータを出力することができるようになりました。


                  統合テストと実用テスト

                  このプロジェクトの重要な段階では、Pythonの三目並べで、Requestsライブラリで実装された改良と自動着手の統合を実証します。さらに、システム全体の効率と信頼性を評価するための実用テストを実施します。このステップは、システムのすべての部分がスムーズかつ確実に機能するために必要なものです。

                  統合の段階

                  テストを始める前に、新しい機能がどのように統合されたかを理解することが重要です。以下は、スムーズな統合を確実にするためにおこなわれた主なステップの説明です。

                  1. リクエストライブラリの更新:まず、Requestsライブラリをアップデートし、私たちが実装した改良を加えました。これにより、より効果的なコミュニケーションが可能になりました。

                  2. 三目並べの適応:重要なステップは、Pythonの三目並べに自動手番を統合したことです。対戦コードを変更し、マシンの手番を認識し、自動手番のロジックを作動させるようにしました。

                  3. API統合の実装:次に、FastAPIを自動手番に対応させました。これには、種版管理の改善、machine_move関数との統合、対戦状態の常時更新、対戦結果の適切な処理などが含まれます。

                  テストの実施

                  統合が完了したところで、すべてが期待通りに動くことを確認するための実践的なテストをおこないます。実装のさまざまな側面をカバーする一連のテストがあります。

                  1. 新規対戦作成テスト(Swaggerとテストスクリプト):新規対戦作成がスムーズに実行されることを確認するため、Swaggerインターフェイスと自動テストスクリプトの両方を使用して実行します。どちらの方法でも機能を利用できることを確認します。

                  2. プレイヤー手番テスト(Swaggerとテストスクリプト):プレイヤーが有効な手を打つ機能は、Swaggerとテストスクリプトの両方を使用してテストされます。

                  3. プレイヤー無効手番テスト:APIが無効手を正しく処理することを確認するために、すでに占拠されているマスに手を打とうとするテストを実行し、APIが適切なエラーコードを返すかどうかを確認します。

                  4. プレイヤー勝利テスト: APIが勝利条件を正しく認識していることを確認するために、プレイヤーが勝利する一連の動きをシミュレートします。

                  結果と評価

                  これらのテストを実施した後、統合システムの完全性を確認する信頼できる結果が得られると期待しています。自動化された対戦が期待通りに機能するか、エラーが期待通りに処理されるか、APIが明確で有益な応答を提供するか、などを評価します。

                  これらの実用的なテストは、私たちのシステムが実際の相互運用性や将来の拡張に対応できることを確認するために必要なものです。統合に成功し、徹底的なテストをおこなうことで、信頼性が高く効率的な対戦システムの構築に近づくのです。




                  上の画像では、私たちのアプリケーションで重要な役割を果たす、新しい対戦を作成する機能をテストするプロセスを見ることができます。テストは包括的かつ一貫して実施され、Swaggerインターフェイスと自動テストスクリプトの2つの異なる方法で機能のアクセシビリティに対応します。

                  APIドキュメンテーションおよびテストツールであるSwaggerを使用すると、開発者はAPIを視覚的かつ効果的に操作できます。画像では、新しい対戦の作成が開始され、Swaggerインターフェイスを通して直接テストされ、機能が簡単にアクセスでき、期待通りに動作することが確認できます。

                  さらに、テストプロセスでは自動テストスクリプトを使用し、機能の一貫性と信頼性を確保するための厳密な確認をおこないます。これは、どのようなテスト手法を用いても、システムの品質を維持するという私たちのコミットメントを示すものです。


                  結論

                  この記事ではREST APIの連載を続け、MQL5関数の開発と統合された、Pythonによる三目並べの自動手番の実装とテストに焦点を当てました。主なタスクは、自律型対戦を使用して三目並べを改良することと、REST APIとのインタラクションをテストするためにMQL5でユニットテストを開発することでした。これらの機能を統合することは、対戦そのものを向上させるだけでなく、より完全で効果的なテストの基礎を作成することにもなります。

                  自動手番を開発する際、新しい機能を効果的かつ調和的に実装するためには、対戦の既存のロジックを理解することが非常に重要でした。選択された戦略は、対戦をより複雑でダイナミックなものにすることを可能にする、シンプルだが効果的なアプローチを想定しています。さらに、実際のユーザーをシミュレートするMQL5エージェントを実装するための環境を準備することは、より現実的なテストに向けた重要なステップです。

                  テストに関しては、MQL5でテストスクリプトを作成し実装することで、MQL5コードとREST API間の信頼性の高い接続を確保しました。テストには、対戦の初期化、正しい手と正しくない手の実行、勝利条件の確認などが含まれます。これは、システムの信頼性と安定性を確保するために必要なことです。

                  デバッグオプションの有効化や、より洗練されたエラー処理など、リクエストライブラリに実装された改良により、インタラクションの効率が大幅に改善され、問題の診断も容易になりました。

                  最後に、統合と実践テストの段階で、三目並べとリクエストライブラリの両方において、実装された改良の有効性を確認しました。Swaggerインターフェイスと自動テストスクリプトを使用したテストにより、システム全体の機能性と信頼性が確認されました。

                  この記事では、MQL5における自動手番とユニットテストの組み合わせが、どのように堅牢な開発サイクルを生み出し、信頼性が高く効率的な統合システムを提供することで、より充実した対戦体験と、将来の統合や開発のための貴重な洞察を提供できるかを示しました。



                  MetaQuotes Ltdによりポルトガル語から翻訳されました。
                  元の記事: https://www.mql5.com/pt/articles/13813

                  添付されたファイル |
                  Parte_03.zip (65.15 KB)
                  多通貨エキスパートアドバイザーの開発(第1回):複数取引戦略の連携 多通貨エキスパートアドバイザーの開発(第1回):複数取引戦略の連携
                  取引戦略にはさまざまなものがあります。リスクを分散し、取引結果の安定性を高めるためには、複数の戦略を並行して適用することが有効かもしれません。ただし、それぞれのストラテジーが個別のエキスパートアドバイザー(EA)として実装されている場合、1つの取引口座でそれらの作業を管理することは非常に難しくなります。この問題を解決するのに合理的なのは、1つのEAで異なる取引戦略の運用を実装することです。
                  母集団最適化アルゴリズム:2進数遺伝的アルゴリズム(BGA)(第2回) 母集団最適化アルゴリズム:2進数遺伝的アルゴリズム(BGA)(第2回)
                  この記事では、自然界の生物の遺伝物質で起こる自然なプロセスをモデル化した2進数遺伝的アルゴリズム(binary genetic algorithm:BGA)を見ていきます。
                  多銘柄多期間指標のDRAW_ARROW描画タイプ 多銘柄多期間指標のDRAW_ARROW描画タイプ
                  この記事では、多銘柄多期間矢印指標の描画について見ていきます。また、現在のチャートの銘柄/期間と一致しない銘柄/期間で計算された矢印指標のデータを示す矢印を正しく表示するためのクラスメソッドを改善します。
                  リプレイシステムの開発(第38回):道を切り開く(II) リプレイシステムの開発(第38回):道を切り開く(II)
                  MQL5プログラマーを自認する人の多くは、この記事で概説するような基本的な知識を持っていません。MQL5は多くの人によって限定的なツールだと考えてられていますが、実際の理由は、そのような人たちが必要な知識を持っていないということです。知らないことがあっても恥じることはありません。聞かなかったことを恥じるべきです。MetaTrader 5で指標の複製を強制的に無効にするだけでは、指標とEA間の双方向通信を確保することはできません。まだこれにはほど遠いものの、チャート上でこの指標が重複していないという事実は、私たちに自信を与えてくれます。