【超入門】MQL5 EA講座 第128回「EA開発用のテンプレートを作る-その4-」【EAの作り方】

MQL5でEA作ろう講座
スポンサーリンク
スポンサーリンク

前回まで行った作業

今回がEA開発用テンプレートの解説シリーズの最終回となります。

今回も作業に取り掛かる前に前回までのおさらいをしておきます。

第125回でおこなった作業については

・「必要なインクルードファイルを読み込み、インスタンスを設定する

・「必要なinput変数を設定する

・「必要なグローバル変数を設定する

セクションをそれぞれご確認ください。

第126回でおこなった作業については

OnInit関数に必要な記述を行う」セクション

及び

OnTick関数に必要な設定を行う」セクションをご覧ください。

そして前回は、

OnTick関数内の記述のうち、価格情報をアップデートする記述や注文関連の記述を追加しました。

詳しくは前回記事の、

・「OnTick関数の中その2:価格情報をアップデートする記述」セクション

・「OnTick関数の中その3:注文関連の記述」セクションをご覧ください。

注文関連の記述については多岐にわたる為、資金管理に関する記述、買い注文に関する記述、売り注文に関する記述、とさらに細かくセクションを分けてその実装過程を解説していきました。

詳しくは前回記事の、

・「資金管理に関する記述」セクション

・「買い注文に関する記述」セクション

・「売り注文に関する記述」セクションをご覧ください。

前回時点でのテンプレートファイル記述は以下のようになっています↓

//+------------------------------------------------------------------+
//|                                    Template For EA Creation.mqh  |
//|                                                MQL5ssei          |
//|                                  https://mqlinvestmentlab.com/   |
//+------------------------------------------------------------------+
#property copyright "MQL5ssei"
#property link      "https://mqlinvestmentlab.com/"


// 注文関連
#include <OriginalTrade.mqh> // 注文関連機能を提供するファイルをインクルード
OriginalCTrade Trade; // 取引のための主要インスタンス
OriginalCPositions Positions; // ポジション管理のためのインスタンス

// 価格情報
#include <OriginalPrice.mqh> // 価格データ取得のためのファイルをインクルード
OriginalCBars Price; // 価格情報を管理するインスタンス

// 資金管理
#include <OriginalMoneyManagement.mqh> // 資金管理機能を提供するファイルをインクルード

// トレーリングストップ
#include <OriginalTrailingStop.mqh> // トレーリングストップ機能を提供するファイルをインクルード
OriginalCTrailing Trailing; // トレーリングストップを管理するインスタンス

// インジケータ
#include <OriginalIndicators.mqh> // インジケータを扱うためのファイルをインクルード

// タイマー
#include <OriginalTimer.mqh> // タイマー機能を提供するファイルをインクルード
CTradeSession Session; // トレーディングセッションを管理するインスタンス
OriginalCNewBar NewBar; // 新しいバーが生成されたかを検出するインスタンス


//+------------------------------------------------------------------+
//| Input変数                                                         |
//+------------------------------------------------------------------+
input group "基本取引設定"  // 基本取引設定のグループ化
input ulong Slippage = 3;              // スリッページ設定
input ulong MagicNumber = 123;         // EA識別番号
input bool TradeOnNewBar = true;       // 新しいバーでの取引を行うかどうか

input group "資金管理"                  // 資金管理設定のグループ化
input bool UseMoneyManagement = true;  // マネーマネジメントを使用するかどうか
input double RiskPercent = 2;          // リスク許容度(%)
input double FixedVolume = 0.1;        // 固定取引量

input group "ストップロスとテイクプロフィット"  // ストップロスとテイクプロフィット設定のグループ化
input int StopLoss = 0;                // ストップロスの設定値
input int TakeProfit = 0;              // テイクプロフィットの設定値

input group "トレーリングストップ"            // トレーリングストップ設定のグループ化
input bool UseTrailingStop = false;    // トレーリングストップを使用するかどうか
input int TrailingStop = 0;            // トレーリングストップの距離
input int MinimumProfit = 0;           // トレーリングを開始する最小利益
input int Step = 0;                    // トレーリングのステップ

input group "ブレイクイーブン"              // ブレイクイーブン設定のグループ化
input bool UseBreakEven = false;       // ブレイクイーブンを使用するかどうか
input int BreakEvenProfit = 0;         // ブレイクイーブンに設定する利益
input int LockProfit = 0;              // 利益を確保するためのロック量

input group "トレードタイマー"              // トレードタイマー設定のグループ化
input bool UseTimer = false;           // トレードタイマーを使用するかどうか
input int StartHour = 0;               // トレード開始時間(時)
input int StartMinute = 0;             // トレード開始時間(分)
input int EndHour = 0;                 // トレード終了時間(時)
input int EndMinute = 0;               // トレード終了時間(分)
input bool UseLocalTime = false;       //ローカル時間を使うかどうか

//+------------------------------------------------------------------+
//| グローバル変数                                                       |
//+------------------------------------------------------------------+

ulong glBuyTicket, glSellTicket;//ポジション番号を格納する変数

//+------------------------------------------------------------------+
//| OnInit関数                                                        |
//+------------------------------------------------------------------+

int OnInit()
  {
// フィルポリシーを取得する
   ENUM_ORDER_TYPE_FILLING filltype = FillPolicy();

// EAのマジックナンバーを設定する
   Trade.SetMagicNumber(MagicNumber);

// 注文実行時の許容スリッページを設定する
   Trade.SetDeviation(Slippage);

// 取得したフィルポリシーを設定する
   Trade.SetFillType(filltype);

// 初期化が完了したことを示す(0 = 成功)
   return(0);
  }

//+------------------------------------------------------------------+
//| OnTick関数                                                        |
//+------------------------------------------------------------------+

void OnTick()
  {
// 新しいバーがあるかどうかの初期状態をtrueと仮定し、バーシフトを0に設定
   bool newBar = true;
   int barShift = 0;

// 新しいバーでの取引を行う場合の処理
   if(TradeOnNewBar == true)
     {
      // 現在のシンボルと期間で新しいバーが始まったかどうかをチェック
      newBar = NewBar.CheckNewBar(_Symbol,_Period);

      // 新しいバーが確認された場合、バーシフトを1に設定
      barShift = 1;
     }

// トレードタイマーの設定
   bool timerOn = true;
// トレードタイマーを使用する場合の処理
   if(UseTimer == true)
     {
      // 指定された時間内で取引が許可されているかどうかをチェック
      timerOn = Session.DailySessionTimer(StartHour, StartMinute, EndHour, EndMinute, UseLocalTime);
     }

//価格情報をアップデートする
   Price.Update(_Symbol,_Period);

// 注文関連の記述
// 新しいバーかつトレードタイマーが許可している時のみ注文処理を行う
   if(newBar == true && timerOn == true)
     {
      // ロット調整
      double tradeSize;
      //資金管理モードONなら設定したリスクに応じたロットを算出
      if(UseMoneyManagement == true)
         tradeSize = MoneyRiskManagement(_Symbol,FixedVolume,RiskPercent,StopLoss);

      //固定ロットを指定
      else
         tradeSize = VerifyVolume(_Symbol,FixedVolume);

      // 買い注文について
      if(Positions.GetBuyPosCount(MagicNumber) == 0)//買いポジションがなければ
        {
         //買い注文を出す
         glBuyTicket = Trade.Buy(_Symbol,tradeSize);

         if(glBuyTicket > 0)
           {
            // 開いたポジションの価格を取得
            double openPrice = PositionOpenPrice(glBuyTicket);
            double buyStop = CalBuySL(_Symbol, StopLoss, openPrice); // ストップロス計算
            if(buyStop > 0)
                buyStop =ModifyLowerStopLevel(_Symbol, buyStop); // ストップロスの設定が有効かチェック

            double buyProfit = CalBuyTP(_Symbol, TakeProfit, openPrice); // テイクプロフィット計算
            if(buyProfit > 0)
               buyProfit =ModifyUpperStopLevel(_Symbol, buyProfit); // テイクプロフィットの設定が有効かチェック

            // ストップロスとテイクプロフィットを設定
            if(buyStop > 0 || buyProfit > 0)
               Trade.SetSLTP(glBuyTicket, buyStop, buyProfit);
            glSellTicket = 0; // 売りチケットリセット
           }



        }//買い注文記述の終わり

      // 売り注文について
      if(Positions.GetSellPosCount(MagicNumber) == 0)// 売りポジションがなければ
        {
         //新規売り注文を出す
         glSellTicket = Trade.Sell(_Symbol,tradeSize);

         if(glSellTicket > 0)
           {
            // 開いたポジションの価格を取得
            double openPrice = PositionOpenPrice(glSellTicket);

            double sellStop = CalSellSL(_Symbol,StopLoss,openPrice);// ストップロス計算
            if(sellStop > 0)
               sellStop = ModifyUpperStopLevel(_Symbol,sellStop); // ストップロスの設定が有効かチェック

            double sellProfit = CalSellTP(_Symbol,TakeProfit,openPrice);// テイクプロフィット計算
            if(sellProfit > 0)
               sellProfit = ModifyLowerStopLevel(_Symbol,sellProfit); // テイクプロフィットの設定が有効かチェック

            // ストップロスとテイクプロフィットを設定
            if(sellStop > 0 || sellProfit > 0)
               Trade.SetSLTP(glSellTicket,sellStop,sellProfit);
            glBuyTicket = 0;//買いチケットリセット
           }
        }//売り注文の記述終わり

     }// 注文関連の記述終わり

インクルードファイルとしてテンプレートに取り込んでいるファイルは以下の通りです

今回は、最後に残っている作業である、

ポジション番号取得に関する記述

トレーリングストップに関する記述

ブレイクイーブンストップに関する記述

を行っていきたいと思います。

OnTick関数の中その4:ポジション番号取得に関する記述

前回の「売り注文記述3:ストップロスとテイクプロフィットを設定する」セクションで解説した記述の下に、ポジション番号に関する記述を追加していきます。

// ポジション番号を取得
   ulong tickets[]; // ポジション番号を格納するための配列
   Positions.GetTotalPosNum(MagicNumber, tickets); // 全ポジションのポジション番号を取得
   int numTickets = ArraySize(tickets); // 取得したポジションの数を格納

ulong型配列「tickets」はポジション番号を格納する為の配列です。

「Positions」インスタンスから、「OriginalCPositionsクラスメンバ関数であるGetTotalPosNum関数を呼び出します。

GetTotalPosNum関数は、買いと売り両方のポジション番号を取得する関数です。

GetTotalPosNum関数についての詳細は講座記事第82回の「GetTotalPosNum関数について」セクションをご覧ください。

int型ローカル変数「numTickets」を宣言し、ArraySize関数戻り値代入します。

ArraySize関数引数には配列「tickets」を記述します。これで「numTickets」には現在のポジション数が格納された事になります。この情報はこの後解説する、トレーリングストップブレイクイーブンストップに関する記述内で用いる事になります。

OnTick関数の中その5:トレーリングストップに関する記述

続いてトレーリングストップに関する記述を実装していきます。

  // トレーリングストップ処理
   if(UseTrailingStop == true && numTickets > 0) // トレーリングストップが有効かつオープンポジションが存在する場合
     {
      for(int i = 0; i < numTickets; i++) // オープンポジションの数だけループ
        {
         // 各ポジションに対してトレーリングストップ処理を適用
         Trailing.TrailingStop(tickets[i], TrailingStop, MinimumProfit, Step);
        }
     }//トレーリングストップ記述の終わり

input変数「UseTrailingStop」はトレーリングストップを利用するかどうかのフラグとして用いる変数です。ローカル変数「numTickets」は先程の「OnTick関数の中その4:ポジション番号取得に関する記述」セクションで書いたように、現在のポジション数が格納されています。よって、

if(UseTrailingStop == true && numTickets > 0)

は「もしトレーリングストップモードがONで、現在ポジションが1つでもあるなら」と読み解けます。

もしif文の条件を満たしているのであれば、for文を使ってオープンポジションの数だけ繰り返し処理を行います。

for文の中ではOriginalCTrailingクラスメンバ関数であるTrailingStop関数を呼び出し、各ポジションに対してトレーリングストップを設定します。

TrailingStop関数の第1引数にはポジション番号が、第2引数にはトレーリングストップポイントが、第3引数にはミニマムプロフィットが、第4引数にはステップ幅が記述されるよう設計されています。

それぞれ、

第1引数ポジション番号を格納している配列「tickets」(のi番目)

第2引数トレーリングストップポイントを設定するinput変数「TrailingStop」

第3引数ミニマムプロフィットを設定するinput変数「MinimumProfit」

第4引数ステップ幅を設定するinput変数「Step」

を記述しておきます。

トレーリングストップに関する記述は以上になります。

TrailingStop関数についての詳細は↓

MQL5 EA講座 第96回「トレーリングストップクラスを作る1」

をご覧ください。

OnTick関数の中その6:ブレイクイーブンストップに関する記述

最後にブレイクイーブンストップに関する記述を施していきます。

// ブレイクイーブン処理 
   if(UseBreakEven == true && numTickets > 0) // ブレイクイーブンが有効かつオープンポジションが存在する場合
     {
      for(int i = 0; i < numTickets; i++) // オープンポジションの数だけループ
        {
         // 各ポジションに対してブレイクイーブン処理を適用
         Trailing.BreakEven(tickets[i], BreakEvenProfit, LockProfit);
        }
     } // ブレイクイーブン処理の終わり



  }//全体記述の終わり

input変数「UseBreakEven」はブレイクイーブンストップを利用するかどうかのフラグとして用いる変数です。ローカル変数「numTickets」には現在のポジション数が格納されています。

if(UseBreakEven == true && numTickets > 0)

「もしブレイクイーブンストップモードがONで、現在ポジションが1つでもあるなら」と読み解けます。

もしif文の条件を満たしているのであれば、for文を使ってオープンポジションの数だけ繰り返し処理を行います。

for文の中ではOriginalCTrailingクラスメンバ関数であるBreakEven関数を呼び出し、各ポジションに対してブレイクイーブンストップを設定します。

BreakEven関数の第1引数にはポジション番号が、第2引数にはブレイクイーブンストップポイントが、第3引数には「約定値に追加するポイント」が記述されるよう設計されています。

それぞれ、

第1引数ポジション番号を格納している配列「tickets」(のi番目)

BreakEven関数についての詳細は↓

MQL5 EA講座 第100回「ブレイクイーブンストップ関数を作る」

をご覧ください。

ブレイクイーブンストップに関する記述は以上になります。

そして、ブレイクイーブンストップに関する記述の終了と同時にEA開発用テンプレート全体の記述も以上となります。

まとめ

今回はEA開発用テンプレート作りの最終回という事で、OnTick関数内の記述である、ポジション番号取得に関する記述、トレーリングストップに関する記述、ブレイクイーブンストップに関する記述について解説を行いました。

詳細は当記事の↓

OnTick関数の中その4:ポジション番号取得に関する記述

OnTick関数の中その5:トレーリングストップに関する記述

OnTick関数の中その6:ブレイクイーブンストップに関する記述

セクションをご覧ください。

これで講座記事4回分を使って解説してきたEA開発用テンプレートの完成です。

完成したEA開発用テンプレートの全体記述は以下の通りです。

//+------------------------------------------------------------------+
//|                                    Template For EA Creation.mqh  |
//|                                                MQL5ssei          |
//|                                  https://mqlinvestmentlab.com/   |
//+------------------------------------------------------------------+
#property copyright "MQL5ssei"
#property link      "https://mqlinvestmentlab.com/"


// 注文関連
#include <OriginalTrade.mqh> // 注文関連機能を提供するファイルをインクルード
OriginalCTrade Trade; // 取引のための主要インスタンス
OriginalCPositions Positions; // ポジション管理のためのインスタンス

// 価格情報
#include <OriginalPrice.mqh> // 価格データ取得のためのファイルをインクルード
OriginalCBars Price; // 価格情報を管理するインスタンス

// 資金管理
#include <OriginalMoneyManagement.mqh> // 資金管理機能を提供するファイルをインクルード

// トレーリングストップ
#include <OriginalTrailingStop.mqh> // トレーリングストップ機能を提供するファイルをインクルード
OriginalCTrailing Trailing; // トレーリングストップを管理するインスタンス

// インジケータ
#include <OriginalIndicators.mqh> // インジケータを扱うためのファイルをインクルード

// タイマー
#include <OriginalTimer.mqh> // タイマー機能を提供するファイルをインクルード
CTradeSession Session; // トレーディングセッションを管理するインスタンス
OriginalCNewBar NewBar; // 新しいバーが生成されたかを検出するインスタンス


//+------------------------------------------------------------------+
//| Input変数                                                         |
//+------------------------------------------------------------------+
input group "基本取引設定"  // 基本取引設定のグループ化
input ulong Slippage = 3;              // スリッページ設定
input ulong MagicNumber = 123;         // EA識別番号
input bool TradeOnNewBar = true;       // 新しいバーでの取引を行うかどうか

input group "資金管理"                  // 資金管理設定のグループ化
input bool UseMoneyManagement = true;  // マネーマネジメントを使用するかどうか
input double RiskPercent = 2;          // リスク許容度(%)
input double FixedVolume = 0.1;        // 固定取引量

input group "ストップロスとテイクプロフィット"  // ストップロスとテイクプロフィット設定のグループ化
input int StopLoss = 0;                // ストップロスの設定値
input int TakeProfit = 0;              // テイクプロフィットの設定値

input group "トレーリングストップ"            // トレーリングストップ設定のグループ化
input bool UseTrailingStop = false;    // トレーリングストップを使用するかどうか
input int TrailingStop = 0;            // トレーリングストップの距離
input int MinimumProfit = 0;           // トレーリングを開始する最小利益
input int Step = 0;                    // トレーリングのステップ

input group "ブレイクイーブン"              // ブレイクイーブン設定のグループ化
input bool UseBreakEven = false;       // ブレイクイーブンを使用するかどうか
input int BreakEvenProfit = 0;         // ブレイクイーブンに設定する利益
input int LockProfit = 0;              // 利益を確保するためのロック量

input group "トレードタイマー"              // トレードタイマー設定のグループ化
input bool UseTimer = false;           // トレードタイマーを使用するかどうか
input int StartHour = 0;               // トレード開始時間(時)
input int StartMinute = 0;             // トレード開始時間(分)
input int EndHour = 0;                 // トレード終了時間(時)
input int EndMinute = 0;               // トレード終了時間(分)
input bool UseLocalTime = false;       //ローカル時間を使うかどうか

//+------------------------------------------------------------------+
//| グローバル変数                                                       |
//+------------------------------------------------------------------+

ulong glBuyTicket, glSellTicket;//ポジション番号を格納する変数

//+------------------------------------------------------------------+
//| OnInit関数                                                        |
//+------------------------------------------------------------------+

int OnInit()
  {
// フィルポリシーを取得する
   ENUM_ORDER_TYPE_FILLING filltype = FillPolicy();

// EAのマジックナンバーを設定する
   Trade.SetMagicNumber(MagicNumber);

// 注文実行時の許容スリッページを設定する
   Trade.SetDeviation(Slippage);

// 取得したフィルポリシーを設定する
   Trade.SetFillType(filltype);

// 初期化が完了したことを示す(0 = 成功)
   return(0);
  }

//+------------------------------------------------------------------+
//| OnTick関数                                                        |
//+------------------------------------------------------------------+

void OnTick()
  {
// 新しいバーがあるかどうかの初期状態をtrueと仮定し、バーシフトを0に設定
   bool newBar = true;
   int barShift = 0;

// 新しいバーでの取引を行う場合の処理
   if(TradeOnNewBar == true)
     {
      // 現在のシンボルと期間で新しいバーが始まったかどうかをチェック
      newBar = NewBar.CheckNewBar(_Symbol,_Period);

      // 新しいバーが確認された場合、バーシフトを1に設定
      barShift = 1;
     }

// トレードタイマーの設定
   bool timerOn = true;
// トレードタイマーを使用する場合の処理
   if(UseTimer == true)
     {
      // 指定された時間内で取引が許可されているかどうかをチェック
      timerOn = Session.DailySessionTimer(StartHour, StartMinute, EndHour, EndMinute, UseLocalTime);
     }

//価格情報をアップデートする
   Price.Update(_Symbol,_Period);

// 注文関連の記述
// 新しいバーかつトレードタイマーが許可している時のみ注文処理を行う
   if(newBar == true && timerOn == true)
     {
      // ロット調整
      double tradeSize;
      //資金管理モードONなら設定したリスクに応じたロットを算出
      if(UseMoneyManagement == true)
         tradeSize = MoneyRiskManagement(_Symbol,FixedVolume,RiskPercent,StopLoss);

      //固定ロットを指定
      else
         tradeSize = VerifyVolume(_Symbol,FixedVolume);

      // 買い注文について
      if(Positions.GetBuyPosCount(MagicNumber) == 0)//買いポジションがなければ
        {
         //買い注文を出す
         glBuyTicket = Trade.Buy(_Symbol,tradeSize);

         if(glBuyTicket > 0)
           {
            // 開いたポジションの価格を取得
            double openPrice = PositionOpenPrice(glBuyTicket);
            double buyStop = CalBuySL(_Symbol, StopLoss, openPrice); // ストップロス計算
            if(buyStop > 0)
                buyStop =ModifyLowerStopLevel(_Symbol, buyStop); // ストップロスの設定が有効かチェック

            double buyProfit = CalBuyTP(_Symbol, TakeProfit, openPrice); // テイクプロフィット計算
            if(buyProfit > 0)
               buyProfit =ModifyUpperStopLevel(_Symbol, buyProfit); // テイクプロフィットの設定が有効かチェック

            // ストップロスとテイクプロフィットを設定
            if(buyStop > 0 || buyProfit > 0)
               Trade.SetSLTP(glBuyTicket, buyStop, buyProfit);
            glSellTicket = 0; // 売りチケットリセット
           }



        }//買い注文記述の終わり

      // 売り注文について
      if(Positions.GetSellPosCount(MagicNumber) == 0)// 売りポジションがなければ
        {
         //新規売り注文を出す
         glSellTicket = Trade.Sell(_Symbol,tradeSize);

         if(glSellTicket > 0)
           {
            // 開いたポジションの価格を取得
            double openPrice = PositionOpenPrice(glSellTicket);

            double sellStop = CalSellSL(_Symbol,StopLoss,openPrice);// ストップロス計算
            if(sellStop > 0)
               sellStop = ModifyUpperStopLevel(_Symbol,sellStop); // ストップロスの設定が有効かチェック

            double sellProfit = CalSellTP(_Symbol,TakeProfit,openPrice);// テイクプロフィット計算
            if(sellProfit > 0)
               sellProfit = ModifyLowerStopLevel(_Symbol,sellProfit); // テイクプロフィットの設定が有効かチェック

            // ストップロスとテイクプロフィットを設定
            if(sellStop > 0 || sellProfit > 0)
               Trade.SetSLTP(glSellTicket,sellStop,sellProfit);
            glBuyTicket = 0;//買いチケットリセット
           }
        }//売り注文の記述終わり

     }// 注文関連の記述終わり

// ポジション番号を取得
   ulong tickets[]; // チケット番号を格納するための配列
   Positions.GetTotalPosNum(MagicNumber, tickets); // 全ポジションのポジション番号を取得
   int numTickets = ArraySize(tickets); // 取得したポジションの数を格納
   
   // トレーリングストップ処理
   if(UseTrailingStop == true && numTickets > 0) // トレーリングストップが有効かつオープンポジションが存在する場合
     {
      for(int i = 0; i < numTickets; i++) // オープンポジションの数だけループ
        {
         // 各ポジションに対してトレーリングストップ処理を適用
         Trailing.TrailingStop(tickets[i], TrailingStop, MinimumProfit, Step);
        }
     }//トレーリングストップ記述の終わり

// ブレイクイーブン処理 
   if(UseBreakEven == true && numTickets > 0) // ブレイクイーブンが有効かつオープンポジションが存在する場合
     {
      for(int i = 0; i < numTickets; i++) // オープンポジションの数だけループ
        {
         // 各ポジションに対してブレイクイーブン処理を適用
         Trailing.BreakEven(tickets[i], BreakEvenProfit, LockProfit);
        }
     } // ブレイクイーブン処理の終わり



  }//全体記述の終わり

//+------------------------------------------------------------------+

このEA開発用テンプレートを使えば、色んなタイプのEAを通常よりもかなり簡単に、しかも時間を短縮させて完成させる事ができるようになります。

次回以降は、このEA開発用テンプレートを使って実際にEAを開発していくところをお届けしたいと思います。そしてそのEA完成をもって、長きにわたる「MQL5でEAを作ろう講座」を一旦完結させたいと思っています。

あとわずかにはなりますが、もうしばらくお付き合いいただければと思います。

今回は以上となります。最後までお読みいただきありがとうございました。

MQL5 EA講座 第127回「EA開発用のテンプレートを作る-その3-」

第129回「EA開発用テンプレートを利用して実際にEAを作る-MAクロス編-」

タイトルとURLをコピーしました