跳转至

在 cTrader 中创建技术指标

本文概述了 创建 自定义技术指标的步骤,包括用于算法交易或手动操作的工具。

Algo 应用的 指标 选项卡中,点击 新建 按钮,打开算法创建向导。

为您的指标输入名称,然后在 C#Python 之间选择一种编程语言。

从以下方法中选择创建方式:

  • 从零开始 – 新指标将仅包含基本模板。

  • 使用模板 – 您可以从 Python#C# 模板列表中选择预制的算法,涵盖各种指标类型、技术分析工具等。

注意

预制算法已经包含了计算逻辑和可自定义的参数。 此类指标在保存并构建后即可显示在图表上。

点击创建后,代码编辑器将打开,您可以开始编辑指标代码。

编辑代码

根据您的创建方法,指标代码示例应包含以下一个或多个元素:

 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
[Indicator(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class SimpleIndicator : Indicator
{
    [Parameter(DefaultValue = "Hello world!")]
    public string Message { get; set; }

    [Output("Main")]
    public IndicatorDataSeries Result { get; set; }

    protected override void Initialize()
    {
    // Called once when the indicator is launched
    // Initialise variables, nested indicators or series

        Print(Message);
    }

    public override void Calculate(int index)
    {
    // Called automatically on each historical bar
    // Also called in real-time on each tick
    // Place the indicator formula and logic here
    }

    // === Custom Methods ===
    // Add helper methods below to structure your indicator logic

}

Indicator 属性及其可选属性(如 TimeZoneAccessRights)位于指标类(SimpleIndicator)声明之前。

默认情况下,每个指标都包含以下方法:

  • Initialize() 方法在指标启动时调用一次。 它通常用于设置从一开始就需要准备好的变量、嵌套指标和系列。
  • Calculate() 方法在每个历史柱上自动调用,并在每个新跳动点上实时调用。 这是您实现指标公式并定义如何计算和显示结果的地方。

[Output()] 属性指定指标生成的值及其在图表上的显示方式。 您可以自定义每个输出的名称和样式,例如设置其线条颜色。

Initialize()Calculate() 方法是每个自定义指标的基础,绝不能省略。 您可以添加自己的方法来扩展功能,就像编写 cBot 时一样。 要了解更多关于算法的内容,请从 C# 基础 指南开始,并探索 指标代码示例

注意

参考资料 包含了在 cTrader 中构建算法的所有类、事件、方法、变量等,而完整的算法示例和模板可在 GitHub 仓库 中找到。

应用您的新知识来编辑指标代码并适应您的需求。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class SimpleIndicator:
    def initialize(self):
        # Called once when the indicator is launched
        # Initialise variables, nested indicators or series
        api.Print(api.Message)

    def calculate(self, index):
        # Called automatically on each historical bar
        # Also called in real-time on each tick
        # Place the indicator formula and logic here
        api.Result[index] = 100 # Example calculation

    # === Custom Methods ===
    # Add helper methods below to structure your indicator logic

SimpleIndicator 类定义了 Python 中的自定义指标。

默认情况下,每个指标都包含以下方法:

  • initialize(self) 方法在指标启动时调用一次。 它通常用于初始化从一开始就需要准备好的变量、嵌套指标和系列。
  • calculate(self, index) 方法在每个历史柱上自动调用,并在每个新跳动点上实时调用。 这是您实现指标公式并定义如何计算和显示结果的地方。

initialize(self)calculate(self, index) 方法是每个自定义指标的基础,绝不能省略。 您还可以在类中添加自己的辅助方法来扩展功能,就像编写 cBot 时一样。 要了解更多关于算法的内容,请从 Python 基础 开始,并探索 指标代码示例

应用您的新知识来编辑指标代码并适应您的需求。

保存和构建

通过点击代码编辑器顶部的 Save 按钮或使用 Ctrl+S 快捷键来保存您的代码。

在使用指标之前,您需要通过构建指标项目来验证其代码。 点击代码编辑器顶部的 Build 按钮或按 Ctrl+B

通过点击代码编辑器顶部的 Save 图标或使用 Cmd+S 快捷键来保存您的代码。

在使用指标之前,您需要通过构建指标项目来验证其代码。 点击代码编辑器顶部的 Build 图标或按 Cmd+B

构建成功后,您会在 构建结果 中看到确认消息。 如果构建失败,则会显示所有遇到的错误摘要。

如果自上次构建以来代码有变更,Build 图标旁边会出现一个星号。 在这种情况下,您应再次构建指标,然后将其实例添加到任何图表中。

要在图表上使用或显示指标,请 添加实例,或者您可以继续按照章节中的说明修改指标代码。

使用 NaN 算术

指标通过计算输出特定的视觉效果,因此您的代码在这些计算过程中正确处理边缘情况和边界条件至关重要。

考虑以下来自输出去趋势价格振荡器的指标代码片段:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
private MovingAverage movingAverage;

protected override void Initialize()
{
    movingAverage = Indicators.SimpleMovingAverage(Source, Periods);
}

public override void Calculate(int index)
{
    Result[index] = Source[index] - movingAverage.Result[index - Periods / 2 - 1];
}

上述代码定义了通用计算规则,但忽略了边界条件。 例如,如果 Periods 设置为 10index0–9 范围内,计算将返回 NaN(非数字)。 此问题发生的原因是指标无法计算具有负索引的柱状图的值。

为了解决这个问题,您可以添加如下条件:

1
2
3
4
5
6
7
public override void Calculate(int index)
{
    if (index >= Periods + Periods / 2 + 1)
    {
        Result[index] = Source[index] - movingAverage.Result[index - Periods / 2 - 1];
    }
}

简单移动平均本身的代码还必须检查 index >= Periods。 如果您使用嵌套指标(例如另一种类型的移动平均),则条件会有所不同以考虑额外的计算。

虽然您可以定义显式的边界条件,但 C# 提供了一种通过 NaN 算术避免这些问题的便捷方法。 具体来说,double 数据类型完全支持 NaN 计算。 例如:

1
2
3
4
5
double result = double.NaN + 1.4; 
// result will be NaN

bool isNaN = double.IsNaN(result); 
// isNaN will be true

然而,当计算中的任何操作数为 NaN 时,结果也将为 NaN。 由于自定义 cTrader 指标在 DataSeries 类型上运行,如果您尝试访问具有负索引的 DataSeries 元素,它将返回 NaN,并且该值不会绘制在交易图表上。 这种行为允许指标继续运行而不会抛出异常。 在大多数情况下,您可以安全地忽略边界条件,只关注通用计算逻辑。

添加显式检查

虽然 NaN 算术简化了边界条件的处理,但某些情况仍需要显式的 NaN 检查。 递归指标是一个常见的例子。 递归指标根据先前计算的值计算其当前值。

考虑以下指数移动平均的实现:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public override void Calculate(int index)
{
    var previousValue = Result[index - 1];

    if (double.IsNaN(previousValue))
    {
        Result[index] = Source[index];
    }
    else
    {
        Result[index] = Source[index] * _exp + previousValue * (1 - _exp);
    }
}

当指标计算第一个周期时,初始值必须显式设置为非 NaN 类型。 否则,所有后续计算都将返回 NaN,使指标无用。

在上面的代码中,这是通过 double.IsNaN(previousValue) 方法处理的。 == 运算符不能用于此检查(previousValue == double.NaN),因为 NaN 与任何值(包括其自身)的比较总是评估为 false

当您对指标代码满意时,构建 它,然后通过 添加实例 到图表上来使用它。

Image title