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

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

MQL5におけるDatabaseBind関数は、あらかじめ用意したSQLクエリ(準備済みステートメント)内の「?」というプレースホルダーに対して、具体的な値を紐付ける(バインドする)ための関数です。

実務レベルのEA開発において、トレード履歴や独自のシグナルログをSQLiteデータベースに保存する際、多くの開発者は「文字列結合(StringConcatenate)」でSQL文を組み立てようとします。しかし、この手法はデータ内にシングルクォーテーションが含まれる場合のバグ(SQLインジェクションに近い挙動)や、型変換による精度の欠如を招きがちです。

DatabaseBindを活用することで、データ型を安全に保持したまま高速にデータを流し込むことが可能になります。特に大量のバックテスト結果やティックデータをDBに記録する際、クエリをコンパイル(準備)した状態で値だけを入れ替えて実行できるため、処理パフォーマンスを劇的に向上させることができます。

2. 構文と戻り値

DatabaseBindは、データの型に合わせて複数のオーバーロードが用意されています。

bool DatabaseBind(
   int       request,      // DatabasePrepareで取得したリクエストハンドル
   int       index,        // プレースホルダーのインデックス (0から開始)
   T         value         // バインドする値 (int, long, double, string, boolなど)
);

パラメーター

  • request: DatabasePrepare関数が返したリクエストハンドルを指定します。
  • index: SQL文中の「?」の位置を0から始まる番号で指定します。例えば、1番目の「?」は 0、2番目は 1 となります。
  • value: 実際にデータベースへ保存したい値を指定します。

戻り値

  • 成功した場合は true、失敗した場合は false を返します。エラーの詳細は GetLastError() で確認できます。

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

以下の例では、トレードの結果(マジックナンバー、利益、コメント)をデータベースの「TRADE_LOG」テーブルに保存する実装を示します。

// データベースに取引記録を保存する関数
void SaveTradeLog(long magic, double profit, string comment)
{
   string dbPath = "MyEA_Data.sqlite";

   // 1. データベースを開く
   int dbHandle = DatabaseOpen(dbPath, DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE | DATABASE_OPEN_COMMON);

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

   // テーブル作成(存在しない場合のみ)
   DatabaseExecute(dbHandle, "CREATE TABLE IF NOT EXISTS TRADE_LOG (Magic INTEGER, Profit REAL, Comment TEXT);");

   // 2. SQL文の準備(プレースホルダーを使用)
   string sql = "INSERT INTO TRADE_LOG (Magic, Profit, Comment) VALUES (?, ?, ?);";
   int request = DatabasePrepare(dbHandle, sql);

   if(request == INVALID_HANDLE)
   {
      Print("クエリ準備失敗: ", GetLastError());
      DatabaseClose(dbHandle);
      return;
   }

   // 3. DatabaseBindで値を安全に紐付け
   DatabaseBind(request, 0, magic);    // 1番目の?にマジックナンバー
   DatabaseBind(request, 1, profit);   // 2番目の?に利益
   DatabaseBind(request, 2, comment);  // 3番目の?にコメント

   // 4. クエリの実行
   if(!DatabaseRead(request) && GetLastError() != ERR_DATABASE_DONE)
   {
      Print("実行エラー: ", GetLastError());
   }
   else
   {
      Print("データ保存成功!");
   }

   // 5. ハンドルの解放とクローズ
   DatabaseFinalize(request);
   DatabaseClose(dbHandle);
}

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

  1. インデックスは「0」から始まる:
    SQL系のAPIによっては1から始まるものもありますが、MQL5のDatabaseBind0始まりです。1番目の「?」に対して index = 1 を指定すると、エラーになるか意図しない挙動になります。

  2. DatabaseFinalizeの忘れ:
    DatabasePrepareで作成したリクエストハンドルは、使い終わったら必ず DatabaseFinalize で解放してください。これを忘れると、データベースがロックされたままになり、次の書き込みができなくなる「Database is locked」エラーの主因となります。

  3. 浮動小数点数の精度:
    double型をバインドする場合、SQLite内部ではREAL型として扱われます。厳密な損益計算をDB側で行う場合は、丸め誤差に注意が必要です。

  4. トランザクションの検討:
    ループ内で大量に DatabaseBind と実行を繰り返す場合は、必ず DatabaseExecute(dbHandle, "BEGIN TRANSACTION")COMMIT で囲んでください。これを行わないと、1レコードごとにディスクへの書き込みが発生し、処理が極端に遅くなります。

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

アルゴリズムトレーダーとして、データベース操作やコードの最適化にこだわるのであれば、それ以上に「実行環境」のネットワーク遅延(レイテンシ)に目を向けるべきです。どれほど洗練されたMQL5プログラムを記述しても、自宅のPCや一般的な光回線からの注文は、物理的な距離とルーティングの多さにより、証券会社のサーバーに届くまでに数十ミリ秒から数百ミリ秒のロスが発生します。

この遅延は、ボラティリティが高い局面での「スリッページ」を招き、期待期待値を著しく低下させます。特に1分足以下のロジックやスキャルピングを行う場合、自宅PCでの運用はネットワークの不安定さによる切断リスクも含め、致命的な損失を生む原因となります。極限まで約定スピードを高め、優位性を確保するためには、証券会社のデータセンターに近い場所に位置する自動売買専用のVPS(仮想専用サーバー)の導入が必須です。プロのクオンツ環境において、低遅延なインフラはロジックと同等、あるいはそれ以上に重要な要素であることを忘れないでください。

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

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

コメント

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