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

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

MQL5におけるDatabasePrepare関数は、SQLiteデータベースに対して実行したいSQL文を「プリコンパイル(事前準備)」するための非常に重要な関数です。

実務レベルの開発において、この関数は単にSQLを実行するだけでなく、「効率化」と「安全性の確保」という2つの大きな役割を担います。

  • 効率化: 同じ構造のSQL(例:データの挿入や検索)を繰り返し実行する場合、毎回SQL文を解析するのは無駄です。DatabasePrepareで一度「型」を作っておけば、後はデータだけを入れ替えて高速に処理できます。
  • 安全性の確保: 変数を直接SQL文字列に組み込むのではなく、この関数を介して「パラメータ(プレースホルダ)」として扱うことで、バグや予期せぬデータ形式によるエラーを未然に防ぐことができます。

初心者の方は、DatabaseExecute(一発実行)で済ませてしまいがちですが、バックテスト結果の保存や大量の取引ログの解析など、パフォーマンスが求められる場面では、このDatabasePrepareを使いこなせるかどうかが、プロのクオンツエンジニアへの第一歩となります。


2. 構文と戻り値

DatabasePrepare関数の基本的な書き方は以下の通りです。

bool DatabasePrepare(
   int           database,      // DatabaseOpenで取得したハンドル
   const string  sql,           // 実行したいSQL文
   int&          request        // 作成されたリクエストハンドルを格納する変数
);

パラメーター解説

  1. database: DatabaseOpen()関数によって取得された、データベースの接続ハンドルを指定します。
  2. sql: 実行したいSQLクエリを入力します。値を動的に変えたい場合は ? などのプレースホルダを使用します。
  3. request: 参照渡し(&)の引数です。成功すると、そのSQL文を指し示す「リクエストハンドル」がこの変数に代入されます。

戻り値

  • 成功した場合は true を、失敗した場合は false を返します。
  • 失敗の理由は GetLastError() を呼び出すことで確認可能です。

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

以下のサンプルは、取引履歴をデータベースに記録する際、事前にクエリを準備して実行する実用的なコードです。

// データベースハンドルの宣言
int db_handle = INVALID_HANDLE;

void OnStart()
{
   // 1. データベースを開く(または作成)
   db_handle = DatabaseOpen("TradeHistory.sqlite", DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE);

   if(db_handle == INVALID_HANDLE) {
      Print("DBオープン失敗: ", GetLastError());
      return;
   }

   // 2. テーブルの作成(これはDatabaseExecuteでOK)
   DatabaseExecute(db_handle, "CREATE TABLE IF NOT EXISTS TRADES (ID INTEGER, SYMBOL TEXT, PROFIT REAL);");

   // 3. DatabasePrepareによる準備
   // 値を "?" にすることで、後からデータを流し込めるようにする
   string sql = "INSERT INTO TRADES (ID, SYMBOL, PROFIT) VALUES (?, ?, ?);";
   int request_handle = INVALID_HANDLE;

   if(DatabasePrepare(db_handle, sql, request_handle)) {
      Print("SQL準備完了。データの流し込みを開始します。");

      // サンプルデータの挿入
      long   ticket = 1234567;
      string symbol = _Symbol;
      double profit = 150.50;

      // 4. パラメータのバインド(?の部分に値をセット)
      DatabaseBind(request_handle, 0, ticket);
      DatabaseBind(request_handle, 1, symbol);
      DatabaseBind(request_handle, 2, profit);

      // 5. SQLの実行(ステップ実行)
      if(DatabaseStep(request_handle)) {
         Print("データの保存に成功しました。");
      } else {
         Print("データの保存に失敗: ", GetLastError());
      }

      // 6. リクエストハンドルの解放(必ず必要!)
      DatabaseFinalize(request_handle);
   } else {
      Print("DatabasePrepare失敗: ", GetLastError());
   }

   // 7. データベースを閉じる
   DatabaseClose(db_handle);
}

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

  1. DatabaseFinalizeの忘れ:
    DatabasePrepareで作成したハンドルは、使い終わったら必ず DatabaseFinalize() で解放してください。これを忘れると、データベースがロックされたままになったり、メモリリークの原因になります。
  2. SQL文の末尾のセミコロン:
    SQLiteでは単一の文にセミコロンは必須ではありませんが、複数の文を一度に準備することはできません。DatabasePrepareは一つのSQL文に対して一度呼び出す必要があります。
  3. インデックスの開始番号:
    DatabaseBind() でプレースホルダに値を割り当てる際、インデックスは「0」から始まります。SQL内の1番目の ? は 0、2番目は 1 となる点に注意してください。
  4. テーブルロックのエラー:
    他のプロセス(例えばSQLiteのGUI閲覧ソフトなど)でDBファイルを開いていると、Prepareは成功しても実行時に失敗することがあります。

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

アルゴリズムトレードにおいて、データベースの最適化と同じかそれ以上に重要なのが「物理的な実行環境」です。自宅のPCや一般的な光回線でEAを稼働させている場合、目に見えないネットワーク遅延(レイテンシ)が常に発生しています。どれほど優れたロジックをDatabasePrepareで高速化しても、証券会社のサーバーまでのパケット往復に数十ミリ秒かかってしまえば、その間に価格が動き、不利な価格での約定(スリッページ)を招きます。

特にボラティリティが高い局面では、わずか数ミリ秒の遅れが利益を削り、年間では致命的な損失差となって現れます。プロの現場でVPS(仮想専用サーバー)が必須とされるのは、物理的な距離を縮めてレイテンシを極限まで抑えるためです。約定スピードを追求することは、トレード手法を磨くことと同義の技術的投資であることを忘れないでください。

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

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

コメント

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