コードサンプル

このページには、さまざまな複雑さの指標のコードサンプルが含まれています。

サンプルリポジトリ

より多くのコードサンプルは、git@github.com:spotware/ctrader-automate-samples.git リポジトリに常にあります。アクセスするには、こちらをクリックしてください。

シンプルな指標

高値マイナス安値指標は、現在のバーの高値と安値の差を計算し、それを出力系列に表示します。この系列は、各バーの結果値を結ぶ線としてチャートに描画されます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
    [Indicator(IsOverlay = false, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None, ScalePrecision = 5)]
    public class HighMinusLow : Indicator
    {
        [Output("Main", LineColor = "Orange")]
        public IndicatorDataSeries Result { get; set; }

        public override void Calculate(int index)
        {
            Result[index] = Bars.HighPrices[index] - Bars.LowPrices[index];
        }
    }

上記の[Indicator...]宣言には、次のようないくつかのパラメータが含まれています。

  • IsOverlay。ラインがチャートに重ねて表示されるか、別のUIパネルに表示されるかを定義するブール値。
  • TimeZone。指標データとサーバー時間のタイムゾーンを指定するTimeZonesクラスのフィールド。
  • AccessRights。指標に割り当てられるアクセス権を決定する「AccessRights」クラスのフィールド。
  • ScalePrecision。指標出力のスケール精度を設定する整数。

前述のようにOutput属性は、プロパティを指標出力としてマークするために宣言されます。このプロパティは、他のクラスから参照できるようにpublicでなければなりません。

指標出力は常にIndicatorDataSeriesデータ型である必要があります。これは配列のようにインデックス可能なダブルのリストです。したがって、リストResultの各[index]の値は、以下のようにCalculateメソッドで割り当てることができます。

1
2
3
4
public override void Calculate(int index)
{
       Result[index] = Bars.HighPrices[index] - Bars.LowPrices[index];
}

パラメータ付きの指標

ほとんどの場合、指標の出力はユーザー入力に応じて変わることがあります。指標のカスタマイズ可能なパラメータの設定方法は、cBotsでの設定方法と似ています

以下の単純移動平均指標は、価格ソースと期間をカスタマイズ可能なパラメータとして受け取るように設計されています。このようなパラメータ(この例ではSourcePeriods)は、Parameter 属性で前置される必要があります。

以前に説明した[Indicator()]および[Output()]属性と同様に、[Parameter()]属性はユーザー入力に適用される特定の特性を定義できます。

1
[Parameter("MA Periods", DefaultValue = 14, MinValue = 1, MaxValue = 20)]

上記では、以下の特性を指定しています。

  • cTrader UIにこのパラメータを示すために表示される名前("MA Periods")。
  • パラメータのデフォルト値。インスタンスをカスタマイズする際にユーザーが変更できます(DefaultValue = 14
  • パラメータの最小値と最大値(MinValue = 1, MaxValue = 20

以下のスニペットでは、カスタマイズ可能なパラメータが指標コードに統合される方法を示します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    [Indicator(IsOverlay = false, ScalePrecision = 5)]
    public class SimpleMovingAverage : Indicator
    {
        [Parameter("Price")]
        public DataSeries Source { get; set; }

        [Parameter("MA Type", DefaultValue = MovingAverageType.Exponential)]
        public MovingAverageType MaType { get; set; }

        [Parameter("MA Periods", DefaultValue = 14, MinValue = 1, MaxValue = 20)]
        public int Periods { get; set; }

        [Parameter("Message", DefaultValue = true)]
        public bool DisplayChartMessage { get; set; }

        [Output("Main", LineColor = "Red")]
        public IndicatorDataSeries Result { get; set; }

        public override void Calculate(int index)
        {
            double sum = 0;

            for (var i = index - Periods + 1; i <= index; i++)
            {
                sum += Source[i];
            }
            Result[index] = sum / Periods;
        }
    }

ネストされたインジケーター

ネストされたインジケーターは、他のインジケーターによる計算結果に依存するインジケーターです。特定のタイプの拡張機能を書く際に便利です。たとえば、DeMark 9 インジケーターのサンプルコードを見てみましょう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
    [Indicator(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class SampleDeMarker : Indicator
    {
        [Parameter(DefaultValue = 14)]
        public int Periods { get; set; }

        [Output("DMark", Color = Colors.Turquoise)]
        public IndicatorDataSeries DMark { get; set; }

        private IndicatorDataSeries deMin;
        private IndicatorDataSeries deMax;
        private MovingAverage deMinMA;
        private MovingAverage deMaxMA;

        protected override void Initialize()
        {
            deMin = CreateDataSeries();
            deMax = CreateDataSeries();
            deMinMA = Indicators.MovingAverage(deMin, Periods, MovingAverageType.Simple);
            deMaxMA = Indicators.MovingAverage(deMax, Periods, MovingAverageType.Simple);
        }

        public override void Calculate(int index)
        {
            deMin[index] = Math.Max(Bars.LowPrices[index - 1] - Bars.LowPrices[index], 0);
            deMax[index] = Math.Max(Bars.HighPrices[index] - Bars.HighPrices[index - 1], 0);

            var min = deMinMA.Result[index];
            var max = deMaxMA.Result[index];

            DMark[index] = min / (min + max);
        }
    }

上記の例では、deMinMAdeMaxMA は、DeMarkerインジケーターの値を計算するために使用される2つの変数です。

ネストされたインジケーターは、Initialize() メソッド内で定義する必要があります。たとえば、deMinMA は、シリーズ deMin の単純移動平均として定義されています。

1
deMinMA = Indicators.MovingAverage(deMin, Periods, MovingAverageType.Simple);

deMin は、Calculate() メソッド内で、最後の2つの安値の最大値として定義されています。

1
deMin[index] = Math.Max(Bars.LowPrices[index - 1] - Bars.LowPrices[index], 0);

ネストされたインジケーターを簡単に扱うために、IntelliSense は、コードエディターで Indicators の後にドットを入力すると、すべての組み込みインジケーターのリストを自動的に表示します。また、特定のインジケーターを選択すると、関連する入力パラメーターも表示されます。

Image title

レイジーローディング

cTrader Algo は、参照されたインジケーターを使用する際にレイジーローディングを使用します。参照されたインジケーターによって提供される値は、コードがそれらを積極的に使用し始めるまで計算されません。

参照されたインジケーターの Outputs データにアクセスすると、cTrader は Calculate() メソッドを呼び出して過去および未来のバーに対してインジケーター データの読み込みを開始します。それ以外の場合、参照されたインジケーターはアイドル状態のままで、したがってシステムリソースを消費しません。

これは、インジケーターに Output がない場合や、その公開プロパティのいずれかにアクセスしようとした場合に、そのプロパティのデフォルト値が返されることも意味します。この問題を解決するには、現在のインジケーターの Calculate() メソッドからカスタムインジケーターの Calculate() メソッドを呼び出します。

オシレーターと「Levels」属性

「オシレーター」という用語は、特定の定数変数の周りで振動するすべてのインジケーターを含みます。

オシレーターを作成する際には、まずその定数値で水平または「レベル」ラインを描くと便利です。インジケーターはこのラインの周りで振動します。多くの場合、定数値はゼロに等しいです。

以下の例では、モメンタムオシレーターを定義します。これは通常、100の値の周りで振動します。この値でレベルラインを追加するために、インジケーター属性の前に宣言された Levels 属性を使用します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
    [Levels(100)]
    [Indicator()]
    public class MomentumOscillator : Indicator
    {
        [Parameter()]
        public DataSeries Source { get; set; }

        [Parameter(DefaultValue = 14, MinValue = 1)]
        public int Periods { get; set; }

        [Output("Main", LineColor = "Green")]
        public IndicatorDataSeries Result { get; set; }

        public override void Calculate(int index)
        {
            Result[index] = 100 * Source[index] / Source[index - Periods];
        }
    }

Levels」属性は、インジケーターがチャート上にオーバーレイされていない場合、つまり「IsOverlay」プロパティが「true」に設定されていない場合にのみ使用できます。デフォルトでは、IsOverlay」の値は「false」です。この属性がコードから省略された場合でも、「Levels」は適切に機能するはずです。

複数の「レベル」ラインを設定する必要がある場合は、以下に示すようにカンマ区切りの値のリストを括弧内に追加します。

1
[Levels(0, 50, 100)] 

「IsLastBar」プロパティ

場合によっては、トレーディングチャートの最後のバーに対してのみ計算されるインジケーターを作成したいことがあります。これを簡単にするために、IsLastBar プロパティを使用して、Calculate() メソッドのインデックスパラメータが最後のバーのものであるかどうかを確認できます。

以下のインジケーターはUTC時間に基づいていますが、ニューヨークと東京の最後のオープン時間を表示できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC)]
    public class TimeInDifferentParts : Indicator
    {
        public override void Calculate(int index)
        {
            if (IsLastBar)
                DisplayTime(index);
        }

        protected void DisplayTime(int index)
        {
            DateTime nyDateTime = Bars.OpenTimes[index].AddHours(-5);
            DateTime tokyoDateTime = Bars.OpenTimes[index].AddHours(7);

            string nyTime = nyDateTime.ToShortTimeString();
            string tokyoTime = tokyoDateTime.ToShortTimeString();

            Chart.DrawStaticText("Title", "Last Bar OpenTime \n NY " + nyTime + "\n" + "Tokyo " + tokyoTime, VerticalAlignment.Top, HorizontalAlignment.Left, Color.Lime);
        }
    }

インディケーターの組み合わせ

cTraderでは、複数のインディケーターを同じパネルまたは同じチャート内で組み合わせることができます。

以下のインディケーターは、Aroon、RSI、方向性移動システムのインディケーターを1つに組み合わせたものです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
    [Indicator(IsOverlay = false, TimeZone = TimeZones.UTC, ScalePrecision = 5)]
    public class Aroon_RSI_DMS : Indicator
    {
        private Aroon aroon;
        private RelativeStrengthIndex rsi;
        private DirectionalMovementSystem dms;

        [Parameter()]
        public DataSeries Source { get; set; }

        [Parameter(DefaultValue = 14)]
        public int Periods { get; set; }

        [Output("Aroon Up", LineColor = "LightSkyBlue")]
        public IndicatorDataSeries AroonUp { get; set; }

        [Output("Aroon Down", LineColor = "Red")]
        public IndicatorDataSeries AroonDn { get; set; }

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

        [Output("DI Plus", LineColor = "DarkGreen")]
        public IndicatorDataSeries DmsDIPlus { get; set; }

        [Output("DI Minus", LineColor = "DarkRed")]
        public IndicatorDataSeries DmsDIMinus { get; set; }

        [Output("ADX", LineColor = "Blue")]
        public IndicatorDataSeries DmsADX { get; set; }

        protected override void Initialize()
        {
            // インディケーターの初期化と作成
            aroon = Indicators.Aroon(Periods);
            rsi = Indicators.RelativeStrengthIndex(Source, Periods);
            dms = Indicators.DirectionalMovementSystem(Periods);
        }

        public override void Calculate(int index)
        {
            AroonUp[index] = aroon.Up[index];
            AroonDn[index] = aroon.Down[index];

            Rsi[index] = rsi.Result[index];

            DmsADX[index] = dms.ADX[index];
            DmsDIMinus[index] = dms.DIMinus[index];
            DmsDIPlus[index] = dms.DIPlus[index];
        }
    }

複数の時間枠

  • 複数の時間枠を使用する

以下の例では、異なる時間枠で移動平均インジケーターを表示しています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC)]
    public class MultiTF_MA : Indicator
    {
        [Parameter(DefaultValue = 50)]
        public int Period { get; set; }

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

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

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

        private Bars bars5;
        private Bars bars10;

        private MovingAverage ma;
        private MovingAverage ma5;
        private MovingAverage ma10;

        protected override void Initialize()
        {
            bars5 = MarketData.GetBars(TimeFrame.Minute5);
            bars10 = MarketData.GetBars(TimeFrame.Minute10);

            ma = Indicators.MovingAverage(Bars.ClosePrices, Period, MovingAverageType.Triangular);
            ma5 = Indicators.MovingAverage(bars5.ClosePrices, Period, MovingAverageType.Triangular);
            ma10 = Indicators.MovingAverage(bars10.ClosePrices, Period, MovingAverageType.Triangular);
        }

        public override void Calculate(int index)
        {
            MA[index] = ma.Result[index];

            var index5 = bars5.OpenTimes.GetIndexByTime(Bars.OpenTimes[index]);
            if (index5 != -1)
                MA5[index] = ma5.Result[index5];

            var index10 = bars10.OpenTimes.GetIndexByTime(Bars.OpenTimes[index]);

            if (index10 != -1)
                MA10[index] = ma10.Result[index10];
        }
    }
  • 複数の時間枠とシンボルの使用

以下の例は、複数の時間枠とシンボルに対して移動平均指標を表示します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC)]
    public class MultiSymbolMA : Indicator
    {
        private MovingAverage ma1, ma2, ma3;
        private Bars bars2, bars3;
        private Symbol symbol2, symbol3;

        [Parameter(DefaultValue = "EURCHF")]
        public string Symbol2 { get; set; }

        [Parameter(DefaultValue = "EURCAD")]
        public string Symbol3 { get; set; }

        [Parameter(DefaultValue = 14)]
        public int Period { get; set; }

        [Parameter(DefaultValue = MovingAverageType.Simple)]
        public MovingAverageType MaType { get; set; }

        [Output("MA Symbol 1", LineColor = "Magenta")]
        public IndicatorDataSeries Result1 { get; set; }

        [Output("MA Symbol 2", LineColor = "Magenta")]
        public IndicatorDataSeries Result2 { get; set; }

        [Output("MA Symbol 3", LineColor = "Magenta")]
        public IndicatorDataSeries Result3 { get; set; }

        protected override void Initialize()
        {
            symbol2 = Symbols.GetSymbol(Symbol2);
            symbol3 = Symbols.GetSymbol(Symbol3);

            bars2 = MarketData.GetBars(TimeFrame, symbol2.Name);
            bars3 = MarketData.GetBars(TimeFrame, symbol3.Name);

            ma1 = Indicators.MovingAverage(Bars.ClosePrices, Period, MaType);
            ma2 = Indicators.MovingAverage(bars2.ClosePrices, Period, MaType);
            ma3 = Indicators.MovingAverage(bars3.ClosePrices, Period, MaType);
        }

        public override void Calculate(int index)
        {
            ShowOutput(Symbol, Result1, ma1, Bars, index);
            ShowOutput(symbol2, Result2, ma2, bars2, index);
            ShowOutput(symbol3, Result3, ma3, bars3, index);
        }

        private void ShowOutput(Symbol symbol, IndicatorDataSeries result, MovingAverage movingAverage, Bars bars, int index)
        {
            var index2 = bars.OpenTimes.GetIndexByTime(Bars.OpenTimes[index]);
            result[index] = movingAverage.Result[index2];

            string text = string.Format("{0} {1}", symbol.Name, Math.Round(result[index], symbol.Digits));
            Chart.DrawStaticText(symbol.Name, text, VerticalAlignment.Top, HorizontalAlignment.Right, Color.Yellow);
        }
    }