コンテンツにスキップ

【移植#53】Delete→Undoをやめる — 選択中オブジェクトを安全に読む

EURUSDのH1チャートで4オブジェクト中2個の選択、トレンドラインの2アンカー、イベント更新とタイマー補完の検証合格を表示したMT5実画面

この記事の3行まとめ

  • 旧MT4の「一度削除してUndoする」判定を使わず、MT5のOBJPROP_SELECTEDを読みます
  • 毎ティック全走査せず、チャートイベントと低頻度タイマーを併用し、走査件数に上限を設けます
  • 専用MT5では選択数を2→1→2と変え、名前・種類・2アンカー・注文無変更まで独立照合しました

今回の原典は「選択中か判定できるか」

2010年2月21日の記事は、読者から寄せられた質問への回答です。

元記事「選択中のオブジェクトを判定する?」を読む

質問者が作りたかったのは、チャート上で選択しているオブジェクトへ処理を行うMT4スクリプトでした。しかし、当時の標準関数では選択状態を直接取得できない、というのが原典の出発点です。

そこで示されたのが、次の発想でした。

  1. 選択中のオブジェクトはDeleteキーで消せる
  2. 削除前後のオブジェクト一覧を比べる
  3. 消えた名前を選択中だったと判断する
  4. Ctrl+Zで削除を戻す

制約を逆手に取った面白い試みですが、現在の道具として採用するには危険です。

  • 判定するために利用者のオブジェクトを一度破壊する
  • Undoが別の操作へ作用する可能性がある
  • 削除と復元の間に見た目と状態が変わる
  • アクティブウィンドウやキー入力に依存する
  • 復元後の選択状態や重なり順まで同じとは限らない

今回の移植では、原典の目的だけを残します。

選択中のオブジェクトを見つけるために、そのオブジェクトを変更しない。


MT5ではOBJPROP_SELECTEDを直接読める

MT5のオブジェクト整数プロパティには、現在の選択状態を表すOBJPROP_SELECTEDがあります。

判定の中心は次の呼び出しです。

MQL
long selected = 0;
if(ObjectGetInteger(0, name, OBJPROP_SELECTED, 0, selected) &&
   selected != 0)
{
   // name は現在選択中
}

ここで使うObjectGetInteger()は、プロパティを戻り値だけで受け取る形ではなく、成功・失敗をboolで受け取るオーバーロードにしました。

取得失敗と「未選択の0」を混同せず、失敗件数を診断パネルへ表示するためです。

似た名前のOBJPROP_SELECTABLEは別の意味です。

プロパティ 意味
OBJPROP_SELECTABLE 利用者がそのオブジェクトを選択できるか
OBJPROP_SELECTED 現在そのオブジェクトが選択されているか

「選択可能」だから「いま選択中」とは限りません。今回数えるのは後者だけです。


一覧を走査し、自分の表示部品は除外する

チャート上の件数はObjectsTotal()、各オブジェクト名はObjectName()で取得できます。

配布版は、メインチャートとサブウィンドウを含む全オブジェクトを対象にします。

MQL
const int total = ObjectsTotal(0, -1, -1);

for(int index = 0;
    index < total && scanned < InpMaxObjects;
    ++index)
{
   const string name = ObjectName(0, index, -1, -1);

   if(StringFind(name, "MQL53_INSPECTOR_") == 0)
      continue;  // 診断パネル自身は数えない

   // OBJPROP_SELECTED を確認
}

インジケーター自身も、背景と文字をチャートオブジェクトとして描きます。それを走査対象に含めると、パネルを追加しただけで件数が増えます。

そこで、配布版が作る表示部品には固有の接頭辞を付け、走査から除外しています。表示部品はOBJPROP_SELECTABLE=falseにして、利用者が誤って選択することも防ぎました。

選択中が複数ある場合、パネルへ詳しく表示する「最初の1個」は、オブジェクト名の辞書順で決めます。端末内部の一覧順に依存させないためです。


選択数だけでなく種類とアンカーも読む

パネルは次の値を表示します。

  • 走査したオブジェクト数
  • 選択中の数
  • 辞書順で最初の選択オブジェクト名
  • オブジェクト種類
  • 第1アンカーの時刻と価格
  • 第2アンカーの時刻と価格
  • 更新理由
  • 走査回数とチャートイベント回数
  • 上限打切りと読取りエラー

種類はOBJPROP_TYPE、アンカーはOBJPROP_TIMEとOBJPROP_PRICEのmodifier 0・1から読みます。

画像では、辞書順で最初の選択オブジェクトがトレンドラインなので、2点の時刻と価格が表示されています。

水平線のように、同じ形で2つの時刻・価格アンカーを持たない種類もあります。その場合は、存在する値だけを表示し、取得できない第2アンカーを0として捏造しません。

このインジケーターがオブジェクトへ行うのは読取りだけです。色、位置、名前、選択状態、重なり順は変更しません。


毎ティック全走査をしない

ObjectsTotal()とObjectName()は同期呼び出しです。公式リファレンスにも、対象チャートで先にキューへ入ったコマンドの完了を待つため、オブジェクトが多いと時間がかかると記載されています。

そのため、OnCalculate()が呼ばれるたびに全オブジェクトを走査する構成にはしていません。

更新経路は2つです。

1. チャートイベント

OnChartEvent()で、次のイベントを受けたときに再走査します。

  • オブジェクトのクリック
  • 作成
  • 削除
  • プロパティ変更
  • ドラッグ
  • チャート変更

利用者がオブジェクトをクリックまたはドラッグしたときは、次のタイマーを待たずに更新できます。

2. タイマー補完

プログラムからOBJPROP_SELECTEDを書き換えた場合など、期待するチャートイベントが必ず発生するとは限りません。

そこで初期値1秒のタイマーでも再走査します。

MQL
void OnTimer()
{
   ScanSelectedObjects("timer fallback");
   UpdateBuffers();
   DrawPanel();
}

さらに、1回の走査はInpMaxObjectsで打ち切ります。初期値は500です。上限へ達したときは「全件を見た」ように扱わず、truncated YESを表示します。


12本の診断バッファを独立照合した

画面の文字だけを目視して合格にはしていません。

配布インジケーターは、次の12本を計算用バッファとして公開します。

# 診断値
1 走査完了
2 走査した利用者オブジェクト数
3 選択中の数
4 最初の選択オブジェクト種類
5 第1アンカー時刻
6 第1アンカー価格
7 第2アンカー時刻
8 第2アンカー価格
9 走査回数
10 対象チャートイベント回数
11 上限打切り
12 読取りエラー数

専用の検証EAは、次の4オブジェクトを作りました。

名前 種類 最終状態
MQL53_FIXTURE_A_TREND トレンドライン 選択中
MQL53_FIXTURE_B_RECTANGLE 長方形 選択中
MQL53_FIXTURE_C_HLINE 水平線 未選択
MQL53_FIXTURE_D_VLINE 垂直線 未選択

検証手順は次のとおりです。

  1. 3個を作り、トレンドラインと長方形の2個を選択する
  2. 診断バッファが「3個中2個」を返すことを確認する
  3. 長方形を未選択へ変え、垂直線を追加する
  4. イベント回数と走査回数が進み、「4個中1個」になることを確認する
  5. 長方形を再選択する
  6. タイマー走査後に「4個中2個」へ戻ることを確認する
  7. 最初の名前、種類、2アンカーを、検証EAが保持する期待値と比較する
  8. 注文・ポジション件数が変わっていないことを確認して画面を保存する

実行条件

項目 値
MT5 Build 6061
チャート EURUSD・H1
走査上限 500
タイマー 1秒
検証オブジェクト 4
売買操作 なし

検証結果

確認項目 結果
配布インジケーターのコンパイル 0エラー・0警告
独立検証EAのコンパイル 0エラー・0警告
選択状態の遷移 2 → 1 → 2
最終選択数 2 / 4
最初の種類 OBJ_TREND
第1・第2アンカー 期待値と一致
イベント経路 更新を確認
タイマー補完 更新を確認
上限打切り なし
読取りエラー 0
注文・ポジション変更 なし
実画面キャプチャー 成功

ダウンロードと使い方

53_Selected_Object_Inspector_v1_00.mq5 をダウンロード

  1. ファイルをMT5のカスタムインジケーターフォルダーへ保存します
  2. MetaEditorでコンパイルします
  3. 確認したいチャートへ適用します
  4. トレンドラインなどをダブルクリックして選択します
  5. パネルのSELECTED、名前、種類、アンカーを確認します

主な入力値は次のとおりです。

パラメーター 初期値 役割
InpMaxObjects 500 1回に確認する利用者オブジェクト数の上限
InpRefreshSeconds 1 イベントを補完する再走査間隔
InpShowPanel true 診断パネルの表示

オブジェクトが非常に多いチャートでは、まず走査上限を小さくして反応を確認してください。上限打切りが表示された場合だけ、必要に応じて増やします。


使えないこと・注意点

  • 選択状態はチャートごとのUI状態です。別チャートの選択をまとめて取得するものではありません。
  • 表示部品を含め、対象チャートにオブジェクトが多いほど同期走査に時間がかかります。
  • 走査上限へ達した場合、その先にある選択オブジェクトは数えられません。
  • オブジェクト種類によって、利用できる時刻・価格アンカーの数は異なります。
  • 名前の辞書順で最初の1個だけを詳細表示します。選択中すべてのアンカー一覧を出力するものではありません。
  • OBJPROP_SELECTED=trueは、売買判断や利用者の承認を意味しません。
  • このインジケーターは注文、変更、取消、ポジション管理を行いません。
  • 実機検証は選択状態の読取りと更新経路を確認したもので、投資成果を検証したバックテストではありません。

ロジック評価の結論

原典のDelete→Undo方式には、「取得できない状態でも、周辺の挙動を組み合わせれば推定できる」という工夫がありました。

ただし、選択を知るために利用者のオブジェクトを消す方法は、現在の実装へ持ち込むべきではありません。

MT5ではOBJPROP_SELECTEDを直接読めます。移植で必要なのは、関数を置き換えるだけでなく、次の境界を決めることでした。

  • インジケーター自身の表示部品を除外する
  • 取得失敗と未選択を区別する
  • 複数選択時の表示順を固定する
  • イベントをタイマーで補完する
  • 同期走査の回数と件数を制限する
  • 利用者オブジェクトを一切変更しない

今回の実測では、4個のうち2個を選択した状態を正しく取得し、1個への変更と2個への復帰も追従できました。

古い工夫を尊重しつつ、破壊的な手順は捨てる。今回の移植で残すべきなのは、「選択したものだけに処理したい」という目的です。

あわせて読みたい記事


前後の記事

前: 【移植#52】HST直書きをやめる — 日足CSVをMT5カスタムシンボルへ安全に取り込む

次: 【移植#54】キー送信をやめる — 複数チャートの時間足をMQL5で一括変更

— SPONSORED —