跳转至

指标代码示例

本页提供了多个 Python 和 C# 代码示例,用于创建用于手动或算法交易的技术指标。

指标示例仓库

全面的指标代码示例,包括各种指标类型和技术分析工具的现成模板,可在单独的 PythonC# GitHub 仓库中找到。

简单指标

High Minus Low 指标计算当前柱的最高价和最低价之间的差值,并将其显示在输出系列中。 该系列在图表上绘制为一条线,连接每个柱的结果值。

 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];
    }
}

注意

Python 指标使用在其 .cs 文件 中声明的可自定义参数。

1
2
3
4
5
6
7
from System import Action

class HighMinusLow():
    def calculate(self, index):
        # In Python you have to declare parameters and outputs in your algo C# file like C# algos
        # So Result here is already declared in C# file of this indicator and here we can use it
        api.Result[index] = api.Bars.HighPrices[index] - api.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];
}
1
2
def calculate(self, index):
    api.Result[index] = api.Bars.HighPrices[index] - api.Bars.LowPrices[index]

带参数的指标

在大多数情况下,指标的输出可以根据用户输入而变化。 为指标设置可自定义参数的方式与 为 cBots 设置的方式 类似。

下面的简单移动平均指标设计为接受价格源和周期作为可自定义参数。 此类参数(在此特定示例中为 SourcePeriods)必须带有 Parameter 属性。

与前面讨论的 [Indicator()][Output()] 属性类似,[Parameter()] 属性可以定义应用于用户输入的某些特征。

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

注意

Python 指标使用在其 .cs 文件 中声明的可自定义参数。

在上面,我们指定了以下特征:

  • 在 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;
    }
}

注意

Python 指标使用在其 .cs 文件 中声明的可自定义参数。

1
2
3
4
5
6
7
8
class SimpleMovingAverage():
    def calculate(self, index):
        sum = 0
        # Periods, Source and Result are declared in the C# file
        for i in range(index - api.Periods + 1, index):
            sum += api.Source[i]

        api.Result[index] = sum / api.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] = max / (min + max);
    }
}

注意

Python 指标使用在其 .cs 文件 中声明的可自定义参数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class SampleDeMarker():
    def initialize(self):
        self.deMin = api.CreateDataSeries()
        self.deMax = api.CreateDataSeries()
        # Periods is declared in the C# file
        self.deMinMA = api.Indicators.MovingAverage(self.deMin, api.Periods, MovingAverageType.Simple)
        self.deMaxMA = api.Indicators.MovingAverage(self.deMax, api.Periods, MovingAverageType.Simple)

    def calculate(self, index):
        self.deMin[index] = max(api.Bars.LowPrices[index - 1] - api.Bars.LowPrices[index], 0)
        self.deMax[index] = max(api.Bars.HighPrices[index] - api.Bars.HighPrices[index - 1], 0)

        minValue = self.deMinMA.Result[index]
        maxValue = self.deMaxMA.Result[index]

        # DMark Output is declared in the C# file
        api.DMark[index] = maxValue / (minValue + maxValue)

在上面的示例中,deMinMAdeMaxMA 是用于计算DeMarker指标值的两个变量。

嵌套指标需要在 Initialize() 方法中定义。 例如,deMinMA 被定义为 deMin 系列的简单移动平均。

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

deMin 又在 Calculate() 方法中定义为最后两个最低价格值的最大值。

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

为了简化嵌套指标的使用,IntelliSense 在代码编辑器中输入 Indicators 后跟一个点时,会自动填充所有内置指标的列表。 一旦你从此列表中选择某个指标,它还将显示所有相关的输入参数。

Image title

延迟加载

cTrader Algo 在使用引用指标时采用延迟加载。 引用指标提供的值直到你的代码开始主动使用它们时才会被计算。

如果你访问引用指标的 Outputs 数据,cTrader 将通过调用其 Calculate() 方法来加载过去和未来柱的指标数据。 在其他情况下,引用指标将保持空闲状态,因此不会消耗任何系统资源。

这也意味着,如果你的指标没有任何 Output 或者你尝试访问其任何公共属性,你将获得该属性的默认值。 要解决此问题,请从当前指标的 Calculate() 方法中调用自定义指标的 Calculate() 方法。

振荡器和水平线属性

“振荡器”一词涵盖了围绕某个常量变量振荡的所有指标。

在创建振荡器时,首先在该常量值处绘制一条水平线或“水平”线是有用的;然后指标将围绕此线振荡。 在许多情况下,常量值等于零。

在下面的示例中,我们定义了一个动量振荡器。 它通常围绕值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];
    }
}

注意

Python 指标使用在其 .cs 文件 中声明的可自定义参数。

1
2
3
4
class MomentumOscillator():
    def calculate(self, index):
        # Result, Source, and Periods are declared in the C# file
        api.Result[index] = 100 * api.Source[index] / api.Source[index - api.Periods]

请注意,Levels 属性只能在指标未叠加在图表上时使用,这意味着 IsOverlay 属性未设置为 true。 默认情况下,IsOverlay 的值为 false。 如果此属性在代码中被省略,Levels 应正常工作。

如果需要建立多条“水平”线,请在括号内添加逗号分隔的值列表,如下所示。

1
[Levels(0, 50, 100)] 
1
[Levels(50.5, 50.75)] 
1
[Levels(0.001, 0.002)] 

!!! note "注意" “Python指标中的水平线”

在Python中,你可以在指标C#文件中声明水平线,与C#指标相同。

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)
    {
        var nyDateTime = Bars.OpenTimes[index].AddHours(-5);
        var tokyoDateTime = Bars.OpenTimes[index].AddHours(7);

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

        Chart.DrawStaticText("Title", "Last Bar OpenTime \n NY " + nyTime + "\n" + "Tokyo " + tokyoTime, VerticalAlignment.Top, HorizontalAlignment.Left, Color.Lime);
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class TimeInDifferentParts():
    def calculate(self, index):
        if api.IsLastBar:
            self.display_time(index)

    def display_time(self, index):
        nyDateTime = api.Bars.OpenTimes[index].AddHours(-5)
        tokyoDateTime = api.Bars.OpenTimes[index].AddHours(7)

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

        api.Chart.DrawStaticText("Title", f"Last Bar OpenTime\nNY {nyTime}\nTokyo {tokyoTime}", VerticalAlignment.Top, HorizontalAlignment.Left, Color.Lime)

组合指标

cTrader 允许在同一面板或同一图表中组合多个指标。

以下指标将Aroon、RSI和Directional Movement System指标组合在一起。

 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()
    {
        // Initialize and create nested indicators
        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];
    }
}

注意

Python 指标使用在其 .cs 文件 中声明的可自定义参数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class Aroon_RSI_DMS():
    def initialize(self):
        # Initialize and create nested indicators
        # Periods and Source parameters are declared in the C# file
        self.aroon = api.Indicators.Aroon(Periods)
        self.rsi = api.Indicators.RelativeStrengthIndex(Source, Periods)
        self.dms = api.Indicators.DirectionalMovementSystem(Periods)

    def calculate(self, index):
        # AroonUp, AroonDn, Rsi, DmsADX, DmsDIMinus, and DmsDIPlus
        # outputs are declared in the C# file
        api.AroonUp[index] = self.aroon.Up[index]
        api.AroonDn[index] = self.aroon.Down[index]

        api.Rsi[index] = self.rsi.Result[index]

        api.DmsADX[index] = self.dms.ADX[index]
        api.DmsDIMinus[index] = self.dms.DIMinus[index]
        api.DmsDIPlus[index] = self.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];
        }
    }
    

    注意

    Python指标使用在其.cs文件中声明的可自定义参数。

     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
    class MultiTF_MA():
        def initialize(self):
            self.bars5 = api.MarketData.GetBars(TimeFrame.Minute5)
            self.bars10 = api.MarketData.GetBars(TimeFrame.Minute10)
    
            # Period is declared as a parameter in the C# file 
            # We use indicator time frame bars for this moving average
            self.ma = Indicators.MovingAverage(api.Bars.ClosePrices, api.Period, MovingAverageType.Triangular)
    
            # We use other two time frame bars that we created previously
            # for these two moving averages
            self.ma5 = Indicators.MovingAverage(self.bars5.ClosePrices, api.Period, MovingAverageType.Triangular)
            self.ma10 = Indicators.MovingAverage(self.bars10.ClosePrices, api.Period, MovingAverageType.Triangular)
    
        def calculate(self, index):
            # MA, MA5, and MA10 are outputs we declared in the C# file
            api.MA[index] = self.ma.Result[index]
    
            index5 = self.bars5.OpenTimes.GetIndexByTime(api.Bars.OpenTimes[index])
    
            if index5 > -1:
                api.MA5[index] = self.ma5.Result[index5]
    
            index10 = self.bars10.OpenTimes.GetIndexByTime(api.Bars.OpenTimes[index])
    
            if index10 > -1:
                api.MA10[index] = self.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);
        }
    }
    

    注意

    Python指标使用在其.cs文件中声明的可自定义参数。

     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
    class MultiSymbolMA():
        def initialize(self):
            # Symbol2 and Symbol3 are declared as parameters in the C# file
            self.symbol2 = api.Symbols.GetSymbol(api.Symbol2)
            self.symbol3 = api.Symbols.GetSymbol(api.Symbol3)
    
            self.bars2 = api.MarketData.GetBars(api.TimeFrame, self.symbol2.Name)
            self.bars3 = api.MarketData.GetBars(api.TimeFrame, self.symbol3.Name)
    
            self.ma1 = api.Indicators.MovingAverage(api.Bars.ClosePrices, api.Period, api.MaType)
            self.ma2 = api.Indicators.MovingAverage(self.bars2.ClosePrices, api.Period, api.MaType)
            self.ma3 = api.Indicators.MovingAverage(self.bars3.ClosePrices, api.Period, api.MaType)
    
        def calculate(self, index):
            # Result1, Result2, and Result3 are declared as outputs in the C# file
            self.show_output(api.Symbol, api.Result1, self.ma1, api.Bars, index)
            self.show_output(self.symbol2, api.Result2, self.ma2, self.bars2, index)
            self.show_output(self.symbol3, api.Result3, self.ma3, self.bars3, index)
    
        def show_output(self, symbol, result, movingAverage, bars, index):
            index2 = bars.OpenTimes.GetIndexByTime(api.Bars.OpenTimes[index])
    
            result[index] = movingAverage.Result[index2]
    
            text = f"{symbol.Name} {round(result[index], symbol.Digits)}"
    
            api.Chart.DrawStaticText(symbol.Name, text, VerticalAlignment.Top, HorizontalAlignment.Right, Color.Yellow)
    

Image title