1. MQL5 CopyBufferとは何か
MQL5のCopyBufferは、インジケータの計算結果を配列へコピーするための関数です。
MetaTrader 5では、インジケータの値は直接取得するのではなく、インジケータハンドル(indicator handle)を作成し、そのハンドルを使ってCopyBufferで値を取得するという仕組みになっています。
この構造は、MQL4のプログラムを書いたことがある人ほど最初に戸惑いやすい部分です。
MQL4では次のように、インジケータ関数を呼ぶだけで値を取得できました。
double ma = iMA(NULL,0,20,0,MODE_SMA,PRICE_CLOSE,0);
しかしMQL5では、次のような2段階の処理が必要になります。
- インジケータのハンドルを作る
- CopyBufferで計算結果を取得する
つまり、MQL5では以下の流れが基本になります。
インジケータ作成
↓
インジケータハンドル取得
↓
CopyBufferで値取得
↓
EAロジックで使用
この設計は、MetaTrader 5がマルチスレッド処理やパフォーマンス最適化を前提に作られているためです。
インジケータ計算とEA処理を分離することで、複数インジケータの効率的な計算が可能になります。
そのため、MQL5でEAやインジケータを開発する場合、CopyBufferはほぼ必ず使う関数になります。
代表的な利用例は次のとおりです。
- 移動平均(iMA)の値取得
- RSI(iRSI)の値取得
- MACDなどの複数バッファインジケータ取得
- カスタムインジケータ(iCustom)の値取得
- EAロジックのシグナル判定
例えば、移動平均をEAの売買判断に使う場合も、次のような処理になります。
iMAでインジケータハンドル作成
↓
CopyBufferで最新値を取得
↓
価格と比較
↓
売買判断
つまりCopyBufferは、EAがインジケータの計算結果を読むための入口と言えます。
1.1 CopyBufferを理解するための重要ポイント
CopyBufferを理解するには、次の3つの概念をセットで理解する必要があります。
① インジケータハンドル
インジケータの実体を指すIDのようなものです。
iMAやiRSIなどの関数は、値ではなくハンドルを返します。
② インジケータバッファ
インジケータが計算した結果を保存しているメモリ領域です。
例えばMACDでは以下のように複数のバッファがあります。
- メインライン
- シグナルライン
- ヒストグラム
CopyBufferでは、このバッファ番号を指定して値を取得します。
③ 配列
CopyBufferは取得した値を配列(array)へコピーします。
EAはその配列を使ってロジックを実行します。
初心者がつまずきやすいポイント
CopyBufferは非常に基本的な関数ですが、初心者がよく混乱する点があります。
① MQL4の感覚で値を直接取得しようとする
MQL5では次のような書き方はできません。
double ma = iMA(...);
iMAはハンドルを返す関数です。
② CopyBufferを呼ぶだけで値が取得できると思う
実際には次の条件が必要です。
- インジケータハンドルが有効
- 十分なバー数が存在
- インジケータ計算が完了
これらが満たされないと、CopyBufferは失敗する可能性があります。
③ 未確定足の扱い
CopyBufferで取得した最新バーの値は、
まだ確定していない可能性があります。
EAロジックでは以下のどちらかを明確に決める必要があります。
- 未確定足を使う
- 確定足のみ使う
実運用EAでは、確定足のみ使用する設計が一般的です。
CopyBufferは単なる関数ですが、MetaTrader 5のインジケータ処理を理解するうえで最も重要な関数の一つです。
2. CopyBufferの基本構文
CopyBufferを正しく使うためには、まず関数の構文(シグネチャ)と各パラメータの意味を理解する必要があります。
この関数は、インジケータが内部で計算した値を配列(array)へコピーするための関数です。
CopyBufferは単純な関数に見えますが、引数の意味を誤解すると以下のようなトラブルが発生します。
- 値が取得できない
- array out of range エラー
- 想定と違うバーの値を取得してしまう
そのため、まずは基本構文を正確に理解することが重要です。
2.1 CopyBufferの関数構文
CopyBufferの基本構文は次の通りです。
int CopyBuffer(
int indicator_handle,
int buffer_num,
int start_pos,
int count,
double buffer[]
);
この関数は、指定したインジケータのバッファからデータを取得し、指定した配列へコピーします。
戻り値はコピーされた要素数です。
処理に失敗した場合は -1 が返されます。
2.2 各パラメータの意味
CopyBufferには5つの引数があります。
それぞれの意味を正確に理解することが重要です。
indicator_handle
対象となるインジケータハンドルです。
これは通常、次のような関数で取得します。
int handle = iMA(Symbol(), PERIOD_CURRENT, 20, 0, MODE_SMA, PRICE_CLOSE);
この handle をCopyBufferへ渡すことで、インジケータの計算結果を取得できます。
注意点
- ハンドルが
INVALID_HANDLEの場合はCopyBufferは失敗する
buffer_num
取得するインジケータバッファ番号です。
例としてMACDを考えると、次のようなバッファがあります。
| バッファ番号 | 内容 |
|---|---|
| 0 | メインライン |
| 1 | シグナルライン |
| 2 | ヒストグラム |
したがって、メインラインを取得する場合は次のようになります。
CopyBuffer(handle,0,0,1,buffer);
よくある失敗
- バッファ番号を間違える
- カスタムインジケータの仕様を確認していない
start_pos
取得開始位置(バー番号)です。
基本的なルールは次の通りです。
| 値 | 意味 |
|---|---|
| 0 | 最新バー |
| 1 | 1本前 |
| 2 | 2本前 |
つまり、次のコードは「最新バー」を取得します。
CopyBuffer(handle,0,0,1,buffer);
count
取得するデータ数(バー数)です。
例
CopyBuffer(handle,0,0,10,buffer);
この場合、最新バーから10本分のデータを取得します。
注意点
配列サイズが不足していると、次のエラーが発生することがあります。
array out of range
buffer[]
取得したデータを格納する配列です。
通常は次のように定義します。
double buffer[];
ArraySetAsSeries(buffer,true);
ArraySetAsSeries を使用すると、配列のインデックスが時系列(最新が0)になります。
2.3 CopyBufferの戻り値
CopyBufferの戻り値は、実際にコピーされたデータ数です。
例
int copied = CopyBuffer(handle,0,0,5,buffer);
戻り値の意味
| 戻り値 | 意味 |
|---|---|
| 5 | 5個のデータ取得成功 |
| -1 | 取得失敗 |
重要なポイント
CopyBufferを使う場合、戻り値をチェックすることが推奨されます。
例
if(copied <= 0)
{
Print("CopyBuffer failed");
}
これを行わないと、エラー状態でもEAが動き続ける可能性があります。
よくある失敗
初心者がよくやるミスを整理しておきます。
① 配列サイズ不足
CopyBuffer(handle,0,0,10,buffer);
なのに
double buffer[5];
これではエラーになります。
② ハンドル未生成
次のような状態です。
int handle;
CopyBuffer(handle,0,0,1,buffer);
ハンドルは必ずOnInitなどで作成する必要があります。
③ start_posの意味を逆に理解
MetaTraderでは
0 = 最新バー
です。
これを間違えると、EAロジックが崩れます。
3. CopyBufferの基本的な使い方(最小コード例)
CopyBufferの使い方を理解するためには、実際のコードの流れを見るのが最も早い方法です。
MQL5では、インジケータ値を取得する基本手順は次の3ステップになります。
- インジケータハンドルを作成する
- CopyBufferでデータを取得する
- 配列から値を読み取る
この流れは、移動平均・RSI・MACD・カスタムインジケータなど、ほぼすべてのインジケータで共通です。
ここでは、最も基本的な例として移動平均(iMA)とCopyBufferを使ったコードを解説します。
3.1 iMAとCopyBufferの基本例
まず、移動平均のインジケータハンドルを作成します。
この処理は通常 OnInit() で行います。
int ma_handle;
int OnInit()
{
ma_handle = iMA(Symbol(), PERIOD_CURRENT, 20, 0, MODE_SMA, PRICE_CLOSE);
if(ma_handle == INVALID_HANDLE)
{
Print("Failed to create iMA handle");
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
このコードでは、20期間の単純移動平均(SMA)を作成しています。
次に、CopyBufferで移動平均の値を取得します。
これは通常 OnTick() などで行います。
void OnTick()
{
double ma_buffer[];
ArraySetAsSeries(ma_buffer,true);
int copied = CopyBuffer(ma_handle,0,0,1,ma_buffer);
if(copied <= 0)
{
Print("CopyBuffer failed");
return;
}
double ma = ma_buffer[0];
Print("MA value: ", ma);
}
このコードの処理は次の通りです。
iMAハンドル作成
↓
CopyBufferで1本分取得
↓
配列に保存
↓
ma_buffer[0]を使用
この流れが、MQL5でのインジケータ値取得の基本パターンになります。
3.2 配列からインジケータ値を読む
CopyBufferで取得したデータは、配列に格納されます。
例えば、次のコードの場合です。
CopyBuffer(ma_handle,0,0,3,ma_buffer);
この場合、取得されるデータは次のようになります。
| 配列インデックス | 内容 |
|---|---|
| 0 | 最新バー |
| 1 | 1本前 |
| 2 | 2本前 |
したがって、次のような使い方が可能です。
double current_ma = ma_buffer[0];
double prev_ma = ma_buffer[1];
このように複数バーを取得することで、トレンド判定やクロス判定が可能になります。
例えば移動平均クロスの判定は次のようになります。
if(ma_buffer[0] > ma_buffer[1])
{
Print("Uptrend");
}
初心者がつまずきやすいポイント
CopyBufferの使い方では、特に次の点で混乱することが多いです。
① 配列の向きを理解していない
MetaTraderの配列は、設定によって順序が変わります。
ArraySetAsSeries(buffer,true);
この設定を行うと、配列は次の順序になります。
buffer[0] = 最新バー
buffer[1] = 1本前
buffer[2] = 2本前
これを設定しない場合、配列順序が逆になることがあります。
EA開発ではseries配列にするのが一般的です。
② CopyBufferを毎Tickで大量取得してしまう
例えば次のようなコードです。
CopyBuffer(handle,0,0,1000,buffer);
これを毎Tick実行すると、EAのパフォーマンスが低下する可能性があります。
実運用EAでは、次のようにすることが多いです。
- 必要な本数だけ取得する
- 新バー発生時のみ取得する
③ インジケータ計算がまだ終わっていない
CopyBufferを呼んでも、まだインジケータが計算されていない場合があります。
この場合
- 戻り値が -1
- 値が0
になることがあります。
そのため実務では次のチェックを行うこともあります。
BarsCalculated(ma_handle)
CopyBufferは単純な関数ですが、配列・バー番号・ハンドルの関係を理解することが重要です。
4. CopyBufferのインデックスとバーの関係
CopyBufferを使う際に最も混乱しやすいのが、バー番号(start_pos)と配列インデックスの関係です。
この仕組みを誤解すると、EAロジックが意図と違うデータを参照してしまう可能性があります。
特に初心者が混乱しやすいポイントは次の3つです。
- start_posの意味
- 配列インデックスの順序
- 未確定足(forming bar)の扱い
ここを正確に理解することで、CopyBufferのトラブルの多くを防ぐことができます。
4.1 start_posの意味
CopyBufferの start_pos は、どのバーからデータ取得を開始するかを指定するパラメータです。
基本ルールは次の通りです。
| 値 | 意味 |
|---|---|
| 0 | 最新バー |
| 1 | 1本前 |
| 2 | 2本前 |
| n | n本前 |
つまり、次のコードは最新バーからデータ取得を意味します。
CopyBuffer(handle,0,0,1,buffer);
この場合
start_pos = 0
count = 1
なので、最新バー1本のみ取得になります。
一方、次のコードの場合です。
CopyBuffer(handle,0,1,1,buffer);
これは1本前のバーのみ取得します。
4.2 countの意味
count は、取得するバー数(データ数)です。
例
CopyBuffer(handle,0,0,5,buffer);
このコードは、最新バーから5本分のデータを取得します。
取得結果は次のように配列へ格納されます。
| 配列 | 内容 |
|---|---|
| buffer[0] | 最新バー |
| buffer[1] | 1本前 |
| buffer[2] | 2本前 |
| buffer[3] | 3本前 |
| buffer[4] | 4本前 |
ただし、これは次の設定をしている場合です。
ArraySetAsSeries(buffer,true);
この設定をしない場合、配列順序が逆になる可能性があります。
そのためEA開発では、Series配列を使うのが一般的です。
4.3 最新値を取得する方法
最もよく使われるパターンは、最新値のみ取得する方法です。
double buffer[];
ArraySetAsSeries(buffer,true);
CopyBuffer(handle,0,0,1,buffer);
double value = buffer[0];
このコードは次の処理になります。
最新バー取得
↓
buffer[0]へ格納
↓
EAロジックで使用
この書き方は、次のようなEAロジックでよく使われます。
- 移動平均クロス
- RSI判定
- トレンド判定
4.4 未確定足(forming bar)の問題
CopyBufferを使う場合、重要なのが未確定足の扱いです。
start_pos = 0 で取得する値は、まだ確定していない可能性があります。
つまり次の状態です。
現在形成中のバー
このバーは、次のような特徴があります。
- 値がリアルタイムで変化する
- ローソク足がまだ確定していない
- インジケータ値も変動する
そのためEAロジックでは、次の2つの設計方針があります。
① 未確定足を使用する
メリット
- エントリーが早い
デメリット
- ダマシが増える可能性
② 確定足のみ使用する
その場合は、次のようにします。
CopyBuffer(handle,0,1,1,buffer);
つまり
1本前のバーを使用
この方法は、システムトレードでは一般的な設計です。
初心者がよくやるミス
CopyBuffer関連のトラブルの多くは、次のようなミスが原因です。
① start_posの意味を逆に理解する
次のように誤解するケースです。
0 = 最も古いバー
これは誤りです。
MetaTraderでは
0 = 最新バー
です。
② 配列順序を理解していない
次の設定をしていない場合です。
ArraySetAsSeries(buffer,true);
これを忘れると、配列順序が逆になります。
③ 未確定足を意図せず使っている
例えば
CopyBuffer(handle,0,0,1,buffer);
この場合、EAは形成中のバーを使っている可能性があります。
CopyBufferはシンプルな関数ですが、バー番号と配列インデックスの理解が非常に重要です。
5. CopyBufferのよくあるエラー
CopyBufferはMetaTrader 5のEA開発で頻繁に使われる関数ですが、エラーや想定外の挙動が発生しやすい部分でもあります。
実際に検索されるトラブルの多くは、CopyBufferの使い方やインジケータの初期化タイミングに関係しています。
ここでは、特に多い以下の問題を解説します。
array out of range- CopyBufferが -1 を返す
- インジケータ値が 0 になる
- 未確定足による誤判定
これらを理解しておくことで、EA開発時のトラブルを大幅に減らすことができます。
5.1 array out of range
MetaTraderで最もよく発生するエラーの一つが
array out of range
です。
これは、配列サイズより多いデータをCopyBufferで取得しようとした場合に発生します。
例えば次のコードです。
double buffer[3];
CopyBuffer(handle,0,0,5,buffer);
この場合
取得要求 = 5
配列サイズ = 3
となるため、エラーになります。
対策
CopyBufferで取得するデータ数に合わせて、配列サイズを確保します。
double buffer[];
ArrayResize(buffer,5);
ArraySetAsSeries(buffer,true);
CopyBuffer(handle,0,0,5,buffer);
実務では、動的配列(ArrayResize)を使う方法が安全です。
5.2 CopyBufferが-1を返す
CopyBufferの戻り値が -1 の場合、データ取得に失敗しています。
例
int copied = CopyBuffer(handle,0,0,1,buffer);
if(copied < 0)
{
Print("CopyBuffer failed");
}
主な原因は次の通りです。
原因1:インジケータハンドルが無効
例えば次のような場合です。
int handle = iMA(...);
このハンドルが
INVALID_HANDLE
だった場合、CopyBufferは必ず失敗します。
対策
if(handle == INVALID_HANDLE)
{
Print("Indicator handle error");
}
原因2:インジケータ計算がまだ終わっていない
インジケータは、必要なバー数が揃わないと計算できない場合があります。
例えば
- 移動平均200期間
- チャートに100バーしかない
この場合、値が取得できません。
対策としてよく使われるのが
BarsCalculated(handle)
です。
例
if(BarsCalculated(handle) <= 0)
{
return;
}
原因3:履歴データがまだロードされていない
MetaTraderでは、チャートを開いた直後は過去データが完全にロードされていないことがあります。
その状態でCopyBufferを呼ぶと
- -1
- 0
が返ることがあります。
この問題は
- EA起動直後
- VPS再起動直後
に発生しやすいです。
5.3 値が0になる
CopyBufferが成功しているのに、値が0になる場合があります。
主な原因は次の通りです。
インジケータがまだ計算されていない
特に以下のインジケータで起きやすいです。
- 長期移動平均
- ATR
- カスタムインジケータ
例えば
MA200
の場合、最低200バー必要になります。
インジケータバッファ番号が間違っている
例えばMACDの場合
| buffer | 内容 |
|---|---|
| 0 | メイン |
| 1 | シグナル |
| 2 | ヒストグラム |
これを間違えると、想定外の値になります。
5.4 未確定足による誤判定
CopyBufferを使ったEAでは、未確定足が原因のトラブルもよく発生します。
例えば次のコードです。
CopyBuffer(handle,0,0,1,buffer);
この場合
buffer[0] = 現在形成中のバー
になります。
未確定足は
- 値が変動する
- ロジックが不安定
という特徴があります。
対策
EAでは次のようにするケースが多いです。
CopyBuffer(handle,0,1,1,buffer);
つまり
確定したバーのみ使用
です。
これはシステムトレードでは一般的な安全設計です。
エラー対策まとめ
CopyBufferトラブルの多くは、次のポイントを確認することで防げます。
- インジケータハンドルが有効か
- 配列サイズが十分か
- インジケータ計算が完了しているか
- バッファ番号が正しいか
- 未確定足を意図せず使っていないか
これらをチェックすることで、CopyBuffer関連の問題の大半は解決できます。
6. CopyBufferをEAで使うときの注意点
CopyBufferはインジケータの値を取得するための基本関数ですが、EA(Expert Advisor)で実運用する場合は設計上の注意点があります。
単純に値を取得するだけのコードでも、書き方によっては次の問題が発生する可能性があります。
- EAの処理が重くなる
- トレード判断が不安定になる
- VPS環境で動作が遅延する
そのため、CopyBufferは単なる関数としてではなく、EAのアーキテクチャ(設計)の一部として扱う必要があります。
ここでは、実運用EAでよく問題になるポイントを解説します。
6.1 毎TickでCopyBufferを呼ぶ問題
EAは通常、ティック(価格更新)ごとにOnTick()が実行されます。
そのため次のようなコードを書くと、CopyBufferが非常に高頻度で実行されることになります。
void OnTick()
{
double buffer[];
ArraySetAsSeries(buffer,true);
CopyBuffer(handle,0,0,1,buffer);
}
この書き方は小規模なEAでは問題ない場合もありますが、次の条件では負荷が増える可能性があります。
- 複数インジケータを使用
- 複数通貨ペア
- VPS環境
- 高頻度ティック
結果として
- CPU使用率増加
- EA遅延
- スリッページ増加
につながる可能性があります。
対策:新バー判定を使う
EAでは、新しいバーが形成されたタイミングのみCopyBufferを呼ぶ設計がよく使われます。
例
datetime last_bar = 0;
void OnTick()
{
datetime current_bar = iTime(Symbol(),PERIOD_CURRENT,0);
if(current_bar != last_bar)
{
last_bar = current_bar;
double buffer[];
ArraySetAsSeries(buffer,true);
CopyBuffer(handle,0,0,2,buffer);
}
}
この方法により
毎Tick処理
↓
毎バー処理
へ最適化できます。
6.2 ハンドルを毎回作る問題
初心者がよくやるミスの一つが、インジケータハンドルを毎Tick生成してしまうことです。
悪い例
void OnTick()
{
int handle = iMA(Symbol(),PERIOD_CURRENT,20,0,MODE_SMA,PRICE_CLOSE);
}
このコードは、毎ティックでインジケータを再生成しています。
これは
- CPU負荷増加
- EAのパフォーマンス低下
の原因になります。
正しい設計
インジケータハンドルはOnInit()で作成するのが一般的です。
int ma_handle;
int OnInit()
{
ma_handle = iMA(Symbol(),PERIOD_CURRENT,20,0,MODE_SMA,PRICE_CLOSE);
return(INIT_SUCCEEDED);
}
その後、CopyBufferのみをOnTickで使用します。
6.3 データ未ロード問題
MetaTraderでは、チャートの履歴データが完全にロードされていない状態が存在します。
この状態でCopyBufferを呼ぶと
- 値が取得できない
- 0が返る
- -1が返る
ことがあります。
これは次の状況で起きやすいです。
- EA起動直後
- VPS再起動直後
- 新しい通貨ペアを開いた直後
対策
インジケータ計算が完了しているか確認します。
例
if(BarsCalculated(handle) <= 0)
{
return;
}
また、十分なバー数があるか確認することも重要です。
if(Bars(Symbol(),PERIOD_CURRENT) < 100)
{
return;
}
実運用EAでのCopyBuffer設計まとめ
実際のEA開発では、次の設計がよく使われます。
OnInit
↓
インジケータハンドル生成
↓
OnTick
↓
新バー判定
↓
CopyBufferでデータ取得
↓
売買ロジック
この構造にすることで
- EA負荷を軽減
- インジケータ計算安定
- ロジックの一貫性確保
が可能になります。
CopyBufferは単純な関数ですが、EA設計の中心となる重要な処理です。
7. CopyBufferを使ったEA実装例
ここまででCopyBufferの基本構文と注意点を解説しました。
この章では、実際のEAロジックにCopyBufferを組み込む例を紹介します。
例として、最も基本的なトレードロジックである移動平均を使ったトレンド判定を実装します。
実際のEAでは以下のような流れになります。
OnInit
↓
インジケータハンドル作成
↓
OnTick
↓
CopyBufferで値取得
↓
売買判断
この構造は、多くのEAで共通する基本パターンです。
7.1 移動平均の値を取得するEA例
まず、OnInitで移動平均インジケータのハンドルを作成します。
int ma_handle;
int OnInit()
{
ma_handle = iMA(Symbol(),PERIOD_CURRENT,20,0,MODE_SMA,PRICE_CLOSE);
if(ma_handle == INVALID_HANDLE)
{
Print("Failed to create MA handle");
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
このコードでは
20期間
SMA
終値
の移動平均を生成しています。
次に、OnTickでCopyBufferを使って値を取得します。
void OnTick()
{
double ma_buffer[];
ArraySetAsSeries(ma_buffer,true);
int copied = CopyBuffer(ma_handle,0,0,2,ma_buffer);
if(copied <= 0)
{
Print("CopyBuffer failed");
return;
}
double current_ma = ma_buffer[0];
double prev_ma = ma_buffer[1];
Print("MA current:",current_ma);
Print("MA previous:",prev_ma);
}
このコードは
最新バー
1本前バー
の移動平均値を取得しています。
7.2 移動平均を使ったトレンド判定
CopyBufferで取得した値は、EAロジックに直接使用できます。
例えば、単純なトレンド判定は次のようになります。
if(current_ma > prev_ma)
{
Print("Uptrend");
}
else
{
Print("Downtrend");
}
このロジックは
移動平均上昇 → 上昇トレンド
移動平均下降 → 下降トレンド
という非常に基本的な判定です。
実際のEAでは、これを売買判断に使用します。
7.3 移動平均クロスの例
もう一つよく使われるのが移動平均クロスです。
例えば
- 短期MA(20)
- 長期MA(50)
を使う場合です。
まず2つのハンドルを作成します。
int ma_fast;
int ma_slow;
int OnInit()
{
ma_fast = iMA(Symbol(),PERIOD_CURRENT,20,0,MODE_SMA,PRICE_CLOSE);
ma_slow = iMA(Symbol(),PERIOD_CURRENT,50,0,MODE_SMA,PRICE_CLOSE);
return(INIT_SUCCEEDED);
}
次にCopyBufferで両方取得します。
double fast[];
double slow[];
ArraySetAsSeries(fast,true);
ArraySetAsSeries(slow,true);
CopyBuffer(ma_fast,0,0,2,fast);
CopyBuffer(ma_slow,0,0,2,slow);
クロス判定は次のようになります。
if(fast[1] < slow[1] && fast[0] > slow[0])
{
Print("Golden Cross");
}
if(fast[1] > slow[1] && fast[0] < slow[0])
{
Print("Dead Cross");
}
このロジックは多くのEAの基本構造になっています。
実装時の注意点
CopyBufferを使ったEAでは、次の点に注意する必要があります。
未確定足を使っている可能性
次のコード
CopyBuffer(handle,0,0,1,buffer);
は
現在形成中のバー
を取得しています。
EAの設計によっては、確定足のみ使用する方が安全な場合があります。
その場合は
CopyBuffer(handle,0,1,1,buffer);
を使用します。
戻り値チェックを忘れる
CopyBufferが失敗しているのに、そのままEAロジックを実行すると
- 誤エントリー
- 異常値
の原因になります。
必ず戻り値を確認します。
取得本数不足
クロス判定では最低でも
2本
必要です。
そのため
CopyBuffer(handle,0,0,2,buffer);
のようにします。
CopyBufferを使えば、ほぼすべてのインジケータ値をEAロジックへ組み込むことができます。
8. CopyBufferとiCustomの関係
MQL5でCopyBufferを使う場面は、標準インジケータだけではありません。
実務では、自作インジケータや外部配布のカスタムインジケータの値をEAで利用したいことがよくあります。
そのときに使うのが iCustom です。
役割を単純化すると、次の関係になります。
iCustom = カスタムインジケータのハンドルを作る
CopyBuffer = そのハンドルから値を読む
つまり、iCustomだけでは値は取れません。
iCustomで取得するのはあくまでインジケータハンドルであり、実際のバッファ値はCopyBufferで取得します。
8.1 iCustomとは
iCustomは、カスタムインジケータを読み込むための関数です。
標準の iMA iRSI iMACD などはMetaTraderに最初から用意されていますが、iCustomは .ex5 のカスタムインジケータを対象にします。
基本的な考え方は次の通りです。
iMAは標準の移動平均ハンドルを作るiCustomは任意のカスタムインジケータのハンドルを作る
例として、MyIndicator.ex5 というカスタムインジケータを使う場合は、次のように記述します。
int custom_handle;
int OnInit()
{
custom_handle = iCustom(Symbol(), PERIOD_CURRENT, "MyIndicator");
if(custom_handle == INVALID_HANDLE)
{
Print("Failed to create custom indicator handle");
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
この時点では、まだ値は取得していません。
取得したのはカスタムインジケータを識別するためのハンドルです。
8.2 iCustomの後にCopyBufferが必要な理由
カスタムインジケータも、MQL5内部ではバッファに計算結果を保持しています。
EAはそのバッファを直接読むのではなく、CopyBufferを使って配列へコピーし、その配列を参照します。
たとえば、カスタムインジケータのメイン出力を取得するなら、基本形は次のようになります。
void OnTick()
{
double values[];
ArraySetAsSeries(values, true);
int copied = CopyBuffer(custom_handle, 0, 0, 2, values);
if(copied <= 0)
{
Print("CopyBuffer failed");
return;
}
Print("Current value: ", values[0]);
Print("Previous value: ", values[1]);
}
このコードの意味は次の通りです。
custom_handleで対象インジケータを指定buffer_num = 0でバッファ0番を指定start_pos = 0で最新バーから取得count = 2で2本分取得
このように、iCustomとCopyBufferはセットで使うのが基本です。
8.3 バッファ番号の意味
iCustomを使うときに特に重要なのが、どのバッファ番号に何が入っているかです。
標準インジケータなら仕様が比較的分かりやすいですが、カスタムインジケータは作者によって設計が異なります。
たとえば、カスタムインジケータが次のような構成だったとします。
| バッファ番号 | 内容 |
|---|---|
| 0 | 買いシグナル |
| 1 | 売りシグナル |
| 2 | 補助ライン |
この場合、買いシグナルを取得したいなら buffer_num = 0、売りシグナルなら buffer_num = 1 を指定します。
double buy_signal[];
double sell_signal[];
ArraySetAsSeries(buy_signal, true);
ArraySetAsSeries(sell_signal, true);
CopyBuffer(custom_handle, 0, 0, 1, buy_signal);
CopyBuffer(custom_handle, 1, 0, 1, sell_signal);
ここを間違えると、値は取得できていても、参照している意味が違うという状態になります。
これは非常に危険です。コンパイルは通っても、売買ロジックが壊れます。
8.4 iCustomで入力パラメータを渡す場合
カスタムインジケータに入力パラメータがある場合は、iCustom の引数として後ろに渡します。
たとえば、期間としきい値を持つインジケータなら、次のような書き方になります。
custom_handle = iCustom(Symbol(), PERIOD_CURRENT, "MyIndicator", 20, 1.5);
この 20 や 1.5 は、カスタムインジケータ側の input パラメータ定義順に対応します。
順序を間違えると、意図しない設定でインジケータが動作するため注意が必要です。
初心者がつまずきやすい点
① iCustomで値が返ると勘違いする
これは非常に多い誤解です。iCustom(...) の戻り値はdouble値ではなくハンドルです。
そのため、次のような書き方は誤りです。
double signal = iCustom(Symbol(), PERIOD_CURRENT, "MyIndicator");
正しくは、まずハンドルを取得し、その後CopyBufferで値を読みます。
② バッファ番号を確認していない
カスタムインジケータの仕様を見ずに buffer_num = 0 と決め打ちすると、別の情報を読んでいる可能性があります。
特に次のケースは要注意です。
- シグナル用と描画用でバッファが分かれている
- 表示しない内部計算用バッファがある
EMPTY_VALUEを返す設計になっている
③ インジケータファイルの配置を間違える
iCustom は指定したカスタムインジケータを読み込めなければ、ハンドル作成に失敗します。
その場合は INVALID_HANDLE になります。
ファイル名、フォルダ、拡張子のビルド状態を確認してください。
④ シグナル値が常に出ると思ってしまう
カスタムインジケータによっては、シグナルがあるバーだけ値を出し、それ以外は EMPTY_VALUE を返すことがあります。
この場合、CopyBuffer自体は成功していても、EAでその値の扱いを誤ると誤判定します。
実務上の注意点
iCustomを使うEAでは、次の点を必ず確認してください。
- インジケータハンドル作成が成功しているか
- バッファ番号が仕様通りか
- 入力パラメータの順序が正しいか
EMPTY_VALUEを考慮しているか- 未確定足の値を意図せず使っていないか
標準インジケータよりも、カスタムインジケータの方が仕様依存性が強いため、検証不足はそのまま不具合につながります。
9. CopyBufferの実運用設計(重要)
CopyBufferは単にインジケータ値を取得する関数ですが、実運用EAでは設計の仕方によって結果が大きく変わります。
特にシステムトレードでは、次のような問題が実際に発生することがあります。
- バックテストでは勝つがフォワードで崩れる
- 未確定足の値で誤エントリーする
- VPS環境で処理が遅延する
- インジケータの計算タイミングがずれる
これらは多くの場合、CopyBufferの使い方やEAの設計構造が原因です。
ここでは、実際のEA開発で重要になる設計ポイントを整理します。
9.1 未確定足を使うかどうかを明確にする
CopyBufferを使うEAで最も重要な判断が、未確定足(forming bar)を使うかどうかです。
start_pos = 0 の場合、取得されるのは次のデータです。
現在形成中のバー
このバーは、次の特徴があります。
- 値がリアルタイムで変化する
- インジケータ値も変化する
- シグナルが途中で消える可能性がある
例えば、移動平均クロスの場合です。
バー途中 → ゴールデンクロス発生
↓
バー確定 → クロス消滅
このような現象は実際によく起きます。
一般的なEA設計
多くのシステムトレードでは、次の方法が採用されます。
確定足のみ使用
その場合、次のようにCopyBufferを使います。
CopyBuffer(handle,0,1,1,buffer);
つまり
1本前のバー
を使います。
これにより
- シグナルの安定性
- バックテスト再現性
が向上します。
9.2 新バーでのみ計算する設計
EAの設計で重要なのが、新バー単位で処理するかどうかです。
もし毎TickでCopyBufferを呼ぶと
- CPU負荷増加
- VPS負荷
- EA遅延
につながる可能性があります。
そのため、多くのEAでは新バー検出ロジックを使います。
例
datetime last_bar = 0;
void OnTick()
{
datetime current_bar = iTime(Symbol(),PERIOD_CURRENT,0);
if(current_bar != last_bar)
{
last_bar = current_bar;
double buffer[];
ArraySetAsSeries(buffer,true);
CopyBuffer(handle,0,1,1,buffer);
}
}
この方法により
毎Tick処理
↓
毎バー処理
へ最適化できます。
9.3 CopyBuffer取得本数の最適化
CopyBufferは、指定した本数のデータを配列へコピーします。
例えば次のコードです。
CopyBuffer(handle,0,0,1000,buffer);
これは1000本のデータを毎回取得します。
しかし多くのEAでは、実際に必要なデータは次の程度です。
- 1本
- 2本
- 3本
つまり
CopyBuffer(handle,0,0,2,buffer);
で十分なケースが多いです。
取得本数を最小化することで
- メモリ負荷
- CPU負荷
を減らすことができます。
9.4 フォワードテストでの注意点
CopyBufferを使ったEAでは、バックテストと実運用の差が問題になることがあります。
主な原因は次の通りです。
- 未確定足使用
- データ更新タイミング
- インジケータ再計算
例えば
バックテスト
↓
バー確定データのみ
ですが
リアルトレード
↓
未確定データが存在
になります。
この違いにより
- シグナルの出方
- エントリータイミング
が変わることがあります。
そのため、EA開発では次のような検証が重要です。
バックテスト
↓
フォワードテスト
↓
実運用
9.5 VPS環境での安定性
EAは多くの場合、VPS(仮想サーバー)で稼働します。
そのためCopyBuffer設計は、サーバー環境での安定性も考慮する必要があります。
注意すべきポイント
- 不要なCopyBuffer呼び出しを減らす
- インジケータハンドルを使い回す
- 新バー処理を使う
これにより
- CPU使用率低下
- EA安定動作
が期待できます。
実運用EAのCopyBuffer設計まとめ
実際のEAでは、次のような設計が一般的です。
OnInit
↓
インジケータハンドル作成
↓
OnTick
↓
新バー検出
↓
CopyBuffer
↓
売買ロジック
さらに
- 確定足使用
- 必要本数のみ取得
- 戻り値チェック
を行うことで、CopyBufferを安全に運用できます。
CopyBufferはMQL5のインジケータ処理の中心となる関数です。
その仕組みを正しく理解することで、EAの安定性と再現性を大きく改善することができます。
10. FAQ
10.1 CopyBufferとは何ですか?
CopyBufferは、インジケータの計算結果を配列へコピーするMQL5関数です。
MetaTrader 5では、インジケータの値は直接取得できず、次の2段階の処理が必要になります。
インジケータハンドル作成
↓
CopyBufferで値取得
この仕組みにより、EAはインジケータの計算結果を安全に参照できます。
10.2 CopyBufferが -1 を返すのはなぜですか?
CopyBufferの戻り値が -1 の場合、データ取得に失敗しています。
主な原因は次の通りです。
- インジケータハンドルが無効(INVALID_HANDLE)
- インジケータ計算が完了していない
- チャート履歴データが不足している
- CopyBufferのパラメータ指定ミス
実装では次のように戻り値を確認するのが安全です。
int copied = CopyBuffer(handle,0,0,1,buffer);
if(copied < 0)
{
Print("CopyBuffer error");
}
10.3 最新のインジケータ値はどのインデックスですか?
通常は
buffer[0]
が最新バーです。
ただし、この順序は配列設定によって変わる場合があります。
EA開発では次の設定を使用するのが一般的です。
ArraySetAsSeries(buffer,true);
この設定により
| インデックス | 意味 |
|---|---|
| 0 | 最新バー |
| 1 | 1本前 |
| 2 | 2本前 |
となります。
10.4 CopyBufferは毎Tick呼んでも問題ありませんか?
技術的には問題ありませんが、EAのパフォーマンスが低下する可能性があります。
特に以下の状況では負荷が増えることがあります。
- 多数のインジケータ使用
- 複数通貨ペアEA
- VPS環境
実務では次の設計がよく使われます。
新バー検出
↓
CopyBuffer実行
この方法により、処理負荷を大幅に減らすことができます。
10.5 CopyBufferとMQL4の違いは何ですか?
MQL4では、インジケータ関数を呼ぶだけで値を取得できました。
例
double ma = iMA(NULL,0,20,0,MODE_SMA,PRICE_CLOSE,0);
しかしMQL5では、ハンドル+CopyBuffer構造になっています。
インジケータ作成
↓
ハンドル取得
↓
CopyBufferで値取得
この設計は、MetaTrader 5のパフォーマンス最適化のために導入されています。
10.6 CopyBufferで複数バーを取得する方法は?
取得するバー数は count パラメータで指定します。
例
CopyBuffer(handle,0,0,5,buffer);
このコードは、最新バーから5本分のデータを取得します。
取得結果は通常、次のように配列へ格納されます。
| 配列 | 内容 |
|---|---|
| buffer[0] | 最新バー |
| buffer[1] | 1本前 |
| buffer[2] | 2本前 |
| buffer[3] | 3本前 |
| buffer[4] | 4本前 |
10.7 CopyBufferの値が0になるのはなぜですか?
CopyBufferが成功していても、値が 0 になる場合があります。
主な原因は次の通りです。
- インジケータ計算がまだ完了していない
- 必要なバー数が不足している
- カスタムインジケータが
EMPTY_VALUEを使用している
対策としては
BarsCalculated()で計算状態を確認- 十分な履歴データをロード
などがあります。
10.8 CopyBufferで未確定足を取得するリスクはありますか?
start_pos = 0 の場合、取得されるのは現在形成中のバーです。
未確定足は
- 値がリアルタイムで変化する
- シグナルが途中で消える
可能性があります。
そのため多くのEAでは
CopyBuffer(handle,0,1,1,buffer);
のように確定したバー(1本前)を使用します。