【MQL5】DatabaseExecute関数の使い方と自動売買実装コード

1. DatabaseExecute関数の概要と実務での活用法

MQL5のDatabaseExecute関数は、SQLiteデータベースに対して「結果を返さないSQLコマンド」を実行するための関数です。具体的には、テーブルの作成(CREATE)、データの挿入(INSERT)、更新(UPDATE)、削除(DELETE)といった操作に使用します。

実務レベルのEA開発において、なぜ標準のCSV出力やグローバル変数ではなくデータベースを使うのでしょうか?それは、データの堅牢性と検索性にあります。数万件に及ぶ取引履歴や、複雑なストラテジーのパラメータ管理をCSVで行うと、ファイル破損のリスクや読み込み速度の低下に悩まされます。

しかし、初心者が最初につまずくのは「SQL構文のミス」と「データベースのロック」です。MQL5の中で文字列としてSQLを組み立てる際、クォーテーションの扱いやカンマの有無でエラーが出やすく、デバッグに時間を取られがちです。この関数を使いこなすことで、EAに「高度な記憶装置」を持たせることが可能になります。

2. 構文と戻り値

DatabaseExecuteの構文は非常にシンプルです。

bool DatabaseExecute(
   int           database,      // DatabaseOpenで取得したデータベースハンドル
   const string  sql            // 実行するSQLリクエスト
);

パラメーター

  • database: DatabaseOpen()関数によって返された有効なハンドルを指定します。
  • sql: 実行したいSQL文を文字列で記述します。

戻り値

  • 成功した場合は true、失敗した場合は false を返します。
  • 失敗の理由を知るには、GetLastError()を呼び出すことで詳細なエラーコードを確認できます。

3. 具体的な使い方・実践サンプルコード

以下は、EAの初期化時に取引ログ保存用のテーブルを作成し、実際にデータを挿入する実用的なサンプルコードです。

//+------------------------------------------------------------------+
//| データベース操作のサンプルEA                                           |
//+------------------------------------------------------------------+
#property strict

int db_handle = INVALID_HANDLE;

int OnInit()
{
   // データベースファイルを開く(存在しない場合は作成される)
   db_handle = DatabaseOpen("TradeLog.sqlite", DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE | DATABASE_OPEN_COMMON);

   if(db_handle == INVALID_HANDLE)
   {
      Print("データベースのオープンに失敗。エラーコード: ", GetLastError());
      return(INIT_FAILED);
   }

   // 1. テーブルの作成 (CREATE TABLE)
   // 取引時刻、シンボル、ロット数を保存するテーブル
   string sql_create = "CREATE TABLE IF NOT EXISTS TRADE_HISTORY("
                       "ID INTEGER PRIMARY KEY AUTOINCREMENT,"
                       "TIME TEXT NOT NULL,"
                       "SYMBOL TEXT NOT NULL,"
                       "LOT REAL NOT NULL);";

   if(!DatabaseExecute(db_handle, sql_create))
   {
      Print("テーブル作成失敗: ", GetLastError());
      DatabaseClose(db_handle);
      return(INIT_FAILED);
   }

   Print("データベースの準備が完了しました。");
   return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason)
{
   // ハンドルを閉じる(忘れるとファイルがロックされる原因になる)
   if(db_handle != INVALID_HANDLE)
   {
      DatabaseClose(db_handle);
   }
}

void OnTick()
{
   // 何らかの条件でデータを挿入する例(デモ用に一度だけ実行)
   static bool inserted = false;
   if(!inserted)
   {
      string time_now = TimeToString(TimeCurrent(), TIME_DATE | TIME_SECONDS);
      string symbol = _Symbol;
      double lot = 0.1;

      // 2. データの挿入 (INSERT)
      // 文字列データはシングルクォーテーションで囲む必要があることに注意
      string sql_insert = StringFormat(
         "INSERT INTO TRADE_HISTORY (TIME, SYMBOL, LOT) VALUES ('%s', '%s', %f);",
         time_now, symbol, lot
      );

      if(DatabaseExecute(db_handle, sql_insert))
      {
         Print("データ挿入成功: ", sql_insert);
         inserted = true;
      }
      else
      {
         Print("データ挿入失敗: ", GetLastError());
      }
   }
}

4. 使用上の注意点とよくあるエラー

  1. SELECT文には使えない:
    DatabaseExecuteは「実行するだけ」の関数です。テーブルからデータを取得したい(SELECT文を使いたい)場合は、DatabasePrepareDatabaseReadを使用する必要があります。

  2. 文字列のクォーテーション:
    SQL文の中で文字列(TEXT型)を扱う場合、必ずシングルクォーテーション ' で囲む必要があります。これを忘れると「No such column」などの構文エラーが発生します。StringFormat関数を使ってSQLを組み立てると、ミスを防ぎやすくなります。

  3. トランザクションの未利用による速度低下:
    ループ内でDatabaseExecuteを数百回繰り返すと、動作が極端に重くなります。大量のデータを一気に処理する場合は、DatabaseExecute(db_handle, "BEGIN TRANSACTION");COMMIT; で囲むようにしましょう。

  4. ハンドルの閉じ忘れ:
    DatabaseCloseを忘れると、MT5を終了するまでデータベースファイルがロックされ、外部ツール(SQLite Browser等)で中身を確認できなくなることがあります。

5. 【重要】自動売買における約定スピードと環境の罠

どれほど完璧なデータベース管理やアルゴリズムを構築しても、実行環境が「自宅のPC」であれば、プロのクオンツの世界ではスタートラインにすら立てていません。FXトレードは0.1秒、あるいは数ミリ秒の差で利益が損失に変わる世界です。自宅のインターネット回線は、プロバイダの混雑やパケットロスといった不安定要素に満ちており、注文ボタンが押されてから業者のサーバーに届くまでの「レイテンシ(遅延)」が致命的なスリッページを引き起こします。

システムトレードで安定した利益を追求するなら、取引サーバーの物理的距離が近いデータセンター内に設置された「専用のVPS」の利用は絶対条件です。ネットワーク遅延を極限まで排除した環境を整えて初めて、あなたが書いたDatabaseExecuteによるログ管理やロジックが本来の価値を発揮します。インフラへの投資を惜しむことは、エンジニアとして最も避けるべき「技術的負債」であることを忘れないでください。

💡 この記事の内容を実運用で活かすには?

この記事の内容を実運用で活かすには、正しい環境が必要です。
特にVPSを使わないと、このロジックは再現できません。

コメント

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