前回の記事で`IndicatorCreate()`を使い、

移動平均線のハンドルを自分で取得する方法を解説しました。

ですがこのコードをそのままインジケーターとして動かしても、

チャート上に線が1本も表示されないことに気づいた人もいるはずです。

これはバグではありません。

`IndicatorCreate()`(や`iMA()`・`iRSI()`)が

返すハンドルは、

あくまで値を計算するための「裏方」

の参照番号であり、それをチャートに表示するかどうかは完全に別の話だからです。

値を`CopyBuffer()`で読み取って

`Comment()`表示するだけなら前回の記事のままで十分ですが、

EAやスクリプトから「参考になるインジケーターを自動でチャートに追加してあげる」

ような、見た目そのものを操作したい場面では

`ChartIndicatorAdd()`が必要になります。

この記事では、`ChartIndicatorAdd()`で

インジケーターを実際にチャートへ追加する方法と、

あわせて見落としやすい「画面から消すこと」

と「計算リソースを解放すること」

は別作業だという点を、実際に動くスクリプトで解説します。

設計思想: 「計算用ハンドル」と「表示」は別のレイヤー

MQL5では、インジケーターの値を計算する処理

(`IndicatorCreate()`・`iMA()`など)と、

その計算結果をチャート上に視覚的に表示する処理

(`ChartIndicatorAdd()`)が、

はっきり分離された設計になっています。

MQL4の`iCustom()`のように「呼び出せば計算もできるし、

手動でチャートにドラッグすれば表示もできる」

という一体感のある感覚でMQL5を使っていると、

「ハンドルは取れているのにチャートに何も出ない」

状態で戸惑いやすくなります。

値を使うだけ(EAの判断材料にするだけ)なら

ハンドルとCopyBufferだけで完結しますが、

画面上にも表示したいときだけ、

もう一段階`ChartIndicatorAdd()`が必要になる、

と覚えてください。

ChartIndicatorAdd関数の書式

bool ChartIndicatorAdd(
   long chart_id,          //チャートID(自分のチャートならChartID()または0)
   int  sub_window,        //追加先のサブウィンドウ番号(0はメイン画面)
   int  indicator_handle   //IndicatorCreateやiMA等で取得したハンドル
   );

戻り値は追加の成否(bool)です。

`sub_window`に0を指定すればメインチャート画面にオーバーレイ表示され、

存在しない番号(1、2…)を指定すると、

その番号のサブウィンドウが自動的に新しく作られてそこに表示されます。

画面から消すChartIndicatorDeleteは「ハンドル」ではなく「表示名」で指定する

追加したインジケーターを画面から取り除きたいとき、

うっかり`ChartIndicatorAdd()`と同じ感覚で

ハンドルを渡そうとしてコンパイルエラーになる人がいます。

bool ChartIndicatorDelete(
   long   chart_id,               //チャートID
   int    sub_window,             //サブウィンドウ番号
   string indicator_shortname     //ハンドルではなく「表示名」の文字列
   );

`ChartIndicatorDelete()`の第3引数はハンドルではなく、

インジケーターの「表示名(ショートネーム)」

という文字列です。

標準インジケーターの表示名は`iMA(20)`のように自動生成されるため、

あらかじめ決め打ちで文字列を書くのは避け、

`ChartIndicatorName()`で実際の表示名を取得してから渡すのが安全です。

int    total = ChartIndicatorsTotal(chart_id, sub_window);      //そのサブウィンドウの表示数
string name  = ChartIndicatorName(chart_id, sub_window, total-1); //最後に追加したものの表示名

ChartIndicatorDelete(chart_id, sub_window, name);

落とし穴: 画面から消しても計算用ハンドルは解放されない

`ChartIndicatorDelete()`は、

あくまで「画面表示を消す」処理であり、

`IndicatorCreate()`で確保した計算用のハンドル自体はまだ生きたままです。

前回の記事で解説した`IndicatorRelease()`は、

このハンドルを別途解放するための処理であり、

`ChartIndicatorDelete()`を呼んだからといって

自動的には呼ばれません。

「表示を消しただけで満足してIndicatorReleaseを呼び忘れる」

と、前回の記事で扱ったハンドルリークがそのまま蓄積していきます。

**表示を消す(ChartIndicatorDelete)と、

計算リソースを解放する(IndicatorRelease)は、

常にセットで行う**と覚えてください。

実際に動くコード全体

メイン画面(サブウィンドウ0)に移動平均線を、

新しいサブウィンドウ(1)にRSIを追加し、

5秒後に画面から消してハンドルも解放する、

実際にMetaEditorでコンパイルできるスクリプトです。

//+------------------------------------------------------------------+
//|                                  ChartIndicatorAddDemo.mq5        |
//|                     Copyright 2026, FX-EA System Project Creator |
//|                        https://creator.fx-ea-system-project.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, FX-EA System Project Creator"
#property link      "https://creator.fx-ea-system-project.com/"
#property version   "1.00"
#property script_show_inputs

input int MAPeriod  = 20;//メイン画面に追加する移動平均線の期間
input int RSIPeriod = 14;//サブウィンドウに追加するRSIの期間

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   long chartId = ChartID();

   //メイン画面(sub_window=0)に移動平均線を追加
   int maHandle = iMA(_Symbol, PERIOD_CURRENT, MAPeriod, 0, MODE_SMA, PRICE_CLOSE);
   if(maHandle == INVALID_HANDLE)
     {
      Print("iMAのハンドル取得に失敗しました。エラーコード=", GetLastError());
      return;
     }
   if(!ChartIndicatorAdd(chartId, 0, maHandle))
     {
      Print("ChartIndicatorAdd(MA)に失敗しました。エラーコード=", GetLastError());
      IndicatorRelease(maHandle);
      return;
     }

   //新しいサブウィンドウ(1)にRSIを追加(存在しない番号を指定すると自動生成される)
   int rsiHandle = iRSI(_Symbol, PERIOD_CURRENT, RSIPeriod, PRICE_CLOSE);
   if(rsiHandle == INVALID_HANDLE)
     {
      Print("iRSIのハンドル取得に失敗しました。エラーコード=", GetLastError());
      ChartRedraw(chartId);
      return;
     }
   if(!ChartIndicatorAdd(chartId, 1, rsiHandle))
     {
      Print("ChartIndicatorAdd(RSI)に失敗しました。エラーコード=", GetLastError());
      IndicatorRelease(rsiHandle);
      return;
     }

   ChartRedraw(chartId);
   Print("移動平均線とRSIを追加しました。5秒後に画面から外します。");
   Sleep(5000);

   //画面から外す: ChartIndicatorDeleteは「ハンドル」ではなく「表示名」で指定する
   string maName = ChartIndicatorName(chartId, 0, ChartIndicatorsTotal(chartId, 0)-1);
   ChartIndicatorDelete(chartId, 0, maName);

   string rsiName = ChartIndicatorName(chartId, 1, ChartIndicatorsTotal(chartId, 1)-1);
   ChartIndicatorDelete(chartId, 1, rsiName);

   //表示を外しただけでは計算用ハンドルはまだ生きているため、IndicatorReleaseを別途呼ぶ
   IndicatorRelease(maHandle);
   IndicatorRelease(rsiHandle);

   ChartRedraw(chartId);
  }
//+------------------------------------------------------------------+
Copy

MetaEditorで実際に動かす手順

  1. MetaTrader 5の「ツール」→「MetaQuotes Language Editor」でMetaEditorを開く
  2. 「ファイル」→「新規作成」→「スクリプト」を選び、名前を「ChartIndicatorAddDemo」にして作成する
  3. テンプレートの中身をすべて削除し、上のコードを丸ごと貼り付ける
  4. F7キー(またはコンパイルボタン)でコンパイルし、「0 error(s), 0 warning(s)」になることを確認する
  5. チャートへドラッグ&ドロップして実行し、一瞬だけ移動平均線とRSIのサブウィンドウが表示され、5秒後に消えることを確認する

コピペして動かないときに確認すること

  • `ChartIndicatorAdd()`が`false`を返す場合、直前の`iMA()`・`iRSI()`が`INVALID_HANDLE`を返していないか(ハンドル自体が無効なら追加も失敗する)
  • `ChartIndicatorDelete()`にハンドル(int)をそのまま渡していないか。第3引数は表示名の文字列(string)であり、型が合わずコンパイルエラーになる
  • サブウィンドウに何も表示されない場合、`ChartRedraw()`を呼び忘れていないか(即座に再描画されない環境がある)
  • 2回目の実行でサブウィンドウの数が増え続ける場合、`IndicatorRelease()`の呼び忘れによるハンドルリークが起きている可能性がある(前回記事の症状と同じ原因)

まとめ

MQL5では、インジケーターの値を計算する処理と、

その結果をチャート上に見た目として表示する処理がはっきり分離されています。

`IndicatorCreate()`や`iMA()`で

取れるのはあくまで計算用のハンドルであり、

実際にチャートへ表示したい場合は

`ChartIndicatorAdd()`をもう一段階呼ぶ必要があります。

取り除くときも、画面表示を消す`ChartIndicatorDelete()`と、

計算リソースを解放する`IndicatorRelease()`は

別作業だという点を忘れずに、

必ずセットで実行してください。

関連記事