cTrader インジケーターを自作する手順 — Indicator クラスの構成とC#コード全文

cTrader のインジケーターは cBot と同じく C# で記述します。発注を伴わない分だけ構造は単純で、自作の入り口としては cBot より取り組みやすい部類です。標準搭載のインジケーターを内部で呼び出せるため、ATR や移動平均の計算を自前で書き直す必要もありません。

本記事では、空のプロジェクトから描画されるインジケーターを1本作るまでを、コード全文とあわせて整理します。発注まで扱う場合は cTrader で cBot を作る手順 を参照してください。

cBot との構造上の違い

インジケーターと cBot は、基底クラスと呼び出され方が異なります。

インジケーターcBot
基底クラスIndicatorRobot
初期化Initialize()OnStart()
計算の入り口Calculate(int index)OnTick() / OnBar()
発注できないできる
出力IndicatorDataSeries に書く建玉として現れる

最も大きな違いは、インジケーターの Calculate が「そのインデックスの値を求める関数」として呼ばれる点です。cBot のようにイベントごとに1回走るのではなく、チャート上の各足に対して呼び出され、さらに形成中の足については値が動くたびに繰り返し呼ばれます。

Indicator クラスの骨格

必要な要素は次の4つです。

  1. [Indicator(...)] 属性 — チャートに重ねるか、別ウィンドウに出すかを指定
  2. [Parameter(...)] — 利用者が調整できる設定値
  3. [Output(...)] — 描画する系列
  4. Initialize()Calculate(int index) — 準備と計算

IsOverlay = true を指定すると価格チャートに重ねて描画され、指定しなければ下部の別ウィンドウに表示されます。価格と同じ単位の線を引くなら true、オシレーターのように異なる尺度なら false を選びます。

動くインジケーターの全文

移動平均を中心線とし、その上下に ATR の倍数でバンドを描く構成です。そのままビルドできます。

using System;
using cAlgo.API;
using cAlgo.API.Indicators;

namespace cAlgo.Indicators
{
    [Indicator(IsOverlay = true, AccessRights = AccessRights.None)]
    public class AtrBands : Indicator
    {
        [Parameter("MA Period", DefaultValue = 20, MinValue = 2, Group = "Center")]
        public int MaPeriod { get; set; }

        [Parameter("ATR Period", DefaultValue = 14, MinValue = 1, Group = "Band")]
        public int AtrPeriod { get; set; }

        [Parameter("ATR Multiplier", DefaultValue = 2.0, MinValue = 0.1, Group = "Band")]
        public double Multiplier { get; set; }

        [Output("Middle", LineColor = "Gray")]
        public IndicatorDataSeries Middle { get; set; }

        [Output("Upper", LineColor = "DodgerBlue")]
        public IndicatorDataSeries Upper { get; set; }

        [Output("Lower", LineColor = "Tomato")]
        public IndicatorDataSeries Lower { get; set; }

        private MovingAverage _ma;
        private AverageTrueRange _atr;

        protected override void Initialize()
        {
            _ma  = Indicators.MovingAverage(Bars.ClosePrices, MaPeriod, MovingAverageType.Simple);
            _atr = Indicators.AverageTrueRange(AtrPeriod, MovingAverageType.Exponential);
        }

        public override void Calculate(int index)
        {
            // 計算に必要な本数が揃うまでは書き込まない
            if (index < Math.Max(MaPeriod, AtrPeriod)) return;

            double mid  = _ma.Result[index];
            double band = _atr.Result[index] * Multiplier;

            if (double.IsNaN(mid) || double.IsNaN(band)) return;

            Middle[index] = mid;
            Upper[index]  = mid + band;
            Lower[index]  = mid - band;
        }
    }
}

標準インジケーターを内部で呼ぶ

Initialize() の中で Indicators.MovingAverage(...)Indicators.AverageTrueRange(...) を呼び出すと、cTrader 標準の計算をそのまま利用できます。ATR の計算式を自分で実装する必要はありません。

呼び出しで注意したいのは引数の形です。移動平均は計算対象の系列を第1引数に取りますが、ATR は高値・安値・終値をすべて使うため、系列を渡さず期間と平滑化方式だけを指定します。

_ma  = Indicators.MovingAverage(Bars.ClosePrices, MaPeriod, MovingAverageType.Simple);
_atr = Indicators.AverageTrueRange(AtrPeriod, MovingAverageType.Exponential);

平滑化方式によって ATR の値は変わります。既存の他ツールと数字を突き合わせるときは、方式が揃っているかを先に確認してください。ATR そのものの読み方は ATR とボラティリティの基本 で整理しています。

Calculate が繰り返し呼ばれることの意味

インジケーターで最も誤解されやすいのが、Calculate の呼ばれ方です。

過去の確定した足に対しては原則1回ですが、チャート右端の形成中の足に対しては、値が動くたびに同じインデックスで何度も呼ばれます。したがって Calculate の中で状態を書き換える処理を持つと、同じ足で何度も加算されるといった不整合が起きます。

// 避けたい書き方: 同じ index で何度も加算されうる
_counter++;

カウンタや履歴を持ちたい場合は、インデックスごとに値を確定させる形にします。

// index を鍵にして書く。何度呼ばれても同じ結果になる
_series[index] = _series[index - 1] + (condition ? 1 : 0);

同じ入力に対して常に同じ結果を返すように書いておけば、再計算されても値がずれません。これは cBot 側で OnBar() を使って確定足だけを読むのとは異なる考え方で、インジケーター特有の作法です。

出力の設定

[Output(...)] は描画の指定を兼ねています。線の色は文字列で指定でき、破線や太さも属性で調整できます。

[Output("Upper", LineColor = "DodgerBlue", LineStyle = LineStyle.Dots, Thickness = 2)]
public IndicatorDataSeries Upper { get; set; }

値を書き込まなかったインデックスは描画されません。計算に必要な本数が揃うまで return している区間は自動的に空白になるため、初期区間を無理に埋める必要はありません。

つまずきやすい点

症状多い原因
線が一切表示されないIsOverlay の指定が用途と逆 / 値を書き込んでいない
チャート左端で線が乱れる必要本数が揃う前に計算している
値が他ツールと合わない平滑化方式やソース系列が異なる
右端だけ値が揺れる形成中の足を見ている(仕様どおりの挙動)
数えた回数が合わないCalculate 内で状態を加算している

右端の値が確定しないのは不具合ではなく、足が閉じるまで値が動き続けるという性質によるものです。確定値だけを扱いたい場合は、参照側でひとつ前のインデックスを読みます。

まとめ

インジケーターの自作は、Initialize で標準インジケーターを組み立て、Calculate でインデックスごとの値を確定させるという型を覚えれば、あとは計算内容を差し替えるだけで応用が利きます。

作った判定を発注まで自動化する段階になったら、同じロジックを cBot 側へ移すことになります。その際は、インジケーターでは許容される「形成中の足を読む」挙動が、cBot では検証結果を歪める原因になる点に注意してください。詳しくは cTrader バックテストの実行手順 で扱っています。

cTrader 向けインジケーターの開発相談や完成品ツールについては ai-programming.xyz をご覧ください。

本記事は一般的な情報提供を目的としています。投資助言ではありません。 取引判断はご自身の責任で行ってください。