1. OnCalculate関数の概要と実務での活用法
MQL5におけるOnCalculateは、主に「カスタムインジケーター」で使用されるイベントハンドラ関数です。チャート上の価格が動いた(ティックが届いた)時、またはヒストリーデータが更新された時に自動的に呼び出されます。
実務開発において、初心者が最もつまずきやすいポイントは「計算効率」の設計です。FXのチャートには数万本のローソク足が存在することもあります。毎ティックごとに全ローソク足の計算をやり直すと、PCのCPUに過剰な負荷がかかり、チャートのフリーズやMT5自体のクラッシュを招きます。
熟練のクオンツエンジニアは、この関数内で「新しく確定した足だけを計算する」という増分計算(Incremental Calculation)を徹底します。OnCalculateを使いこなすことは、単に計算式を書くことではなく、リソースを最適化し、リアルタイム性の高い分析環境を構築することを意味します。
2. 構文と戻り値
OnCalculateには2種類の形式がありますが、基本的には全てのデータにアクセスできる以下の形式が一般的に使用されます。
int OnCalculate(
const int rates_total, // 入力データの全バー数
const int prev_calculated, // 前回の呼び出し時に計算済みのバー数
const datetime& time[], // 時間配列
const double& open[], // 始値配列
const double& high[], // 高値配列
const double& low[], // 安値配列
const double& close[], // 終値配列
const long& tick_volume[], // ティック出来高配列
const long& volume[], // リアル出来高配列
const int& spread[] // スプレッド配列
);
パラメーターのポイント
- rates_total: 現在チャートに表示されているローソク足の総数です。
- prev_calculated: 前回の処理が終わった時点の
rates_totalの値が渡されます。初回実行時は「0」です。 - 各種配列: 価格データが格納されています。デフォルトではインデックス[0]が最も古いデータ(過去)を指しますが、
ArraySetAsSeriesを使用することで、最新の足を[0]として扱うことも可能です。
戻り値
- int型: 計算済みのバー数を返します。通常は
return(rates_total);と記述し、次回の呼び出し時にこの値がprev_calculatedとして引き継がれます。
3. 具体的な使い方・実践サンプルコード
以下は、最も基本的かつ実用的な「単純移動平均線(SMA)」を計算するカスタムインジケーターの例です。無駄な再計算を省くためのロジックが含まれています。
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots 1
// プロパティ設定
#property indicator_label1 "Simple MA"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrDodgerBlue
#property indicator_style1 STYLE_SOLID
#property indicator_width1 2
// 入力パラメーター
input int InpMAPeriod = 14; // 平均期間
// バッファ
double MABuffer[];
//--- 初期化
int OnInit()
{
// 配列をインジケーターバッファに紐付け
SetIndexBuffer(0, MABuffer, INDICATOR_DATA);
return(INIT_SUCCEEDED);
}
//--- メイン計算ロジック
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
// 計算に必要なバーが足りない場合は終了
if(rates_total < InpMAPeriod) return(0);
// 計算を開始するインデックスを特定(増分計算のキモ)
int limit = prev_calculated - 1;
if(prev_calculated <= 0) limit = InpMAPeriod; // 初回計算時
// ループ処理:未計算の部分だけを計算
for(int i = limit; i < rates_total; i++)
{
double sum = 0.0;
for(int j = 0; j < InpMAPeriod; j++)
{
sum += close[i - j]; // 過去に遡って合計
}
MABuffer[i] = sum / InpMAPeriod;
}
// 次回のprev_calculatedのために、現在の全バー数を返す
return(rates_total);
}
4. 使用上の注意点とよくあるエラー
-
配列の向き(時系列)の勘違い
OnCalculateで渡されるclose[]などの配列は、標準ではclose[0]がチャートの左端(最古)を指します。EA開発(OnTick)で慣れている「最新の足=[0]」という感覚でコードを書くと、計算結果が全く異なるものになります。必要に応じてArraySetAsSeries(close, true)を検討してください。 -
prev_calculatedの初期化
ヒストリーデータの欠落をMT5が補填した場合や、時間軸を変更した際、prev_calculatedは「0」にリセットされます。この考慮が漏れると、古い計算データが残り続けてグラフが崩れる原因になります。 -
ゼロ除算の回避
期間設定(input変数)をユーザーが「0」に設定できてしまうコードだと、移動平均の計算などでゼロ除算エラー(Zero divide)が発生し、インジケーターが強制停止します。必ずOnInit等で入力値のバリデーションを行ってください。
5. 【重要】自動売買における約定スピードと環境の罠
アルゴリズムトレードにおいて、OnCalculateで導き出した完璧なシグナルも、実行環境が貧弱であれば無価値になります。多くの開発者が陥る罠が「自宅PCでの運用」です。家庭用インターネット回線は、金融特化のネットワークと比較して数百ミリ秒単位の遅延(レイテンシ)が発生します。この遅延は、相場急変時のスリッページを引き起こし、バックテストでは勝てていたロジックを実運用で破綻させる致命的な要因となります。
プロのクオンツエンジニアとして断言しますが、コンマ数秒の約定スピードが収益を左右するFXシストレにおいて、取引サーバー(証券会社)のデータセンターに物理的に近い場所で稼働する「専用VPS」の導入は必須条件です。ネットワーク遅延を極限まで排除した環境を整えて初めて、プログラムの優位性が正しく利益に反映されます。投資効率を最大化し、インフラ側のリスクで資金を失わないためにも、信頼性の高いVPSの選択には妥協しないでください。
💡 この記事の内容を実運用で活かすには?
この記事の内容を実運用で活かすには、正しい環境が必要です。
特にVPSを使わないと、このロジックは再現できません。

コメント