跳转至

如何向 cTrader API 添加方法

扩展方法是一个关键工具,如果您想向 cTrader API 添加新功能。 使用相对简单的语法,您可以向任何预定义的 API 类(例如 SymbolPosition)添加新行为。 定义扩展方法后,您可以从您扩展的类的任何对象中调用它。

扩展方法的使用案例

首先,我们将快速演示为什么您可能希望使用扩展方法。

使用 cBot,我们希望能够访问给定头寸的大小(以手为单位),因为此信息直接影响我们首选的交易策略。 为此,我们可以尝试初始化 Position 类的变量,然后尝试访问其 Lots() 方法。

1
2
Position position = Positions.Last();
Print(position.Lots());

如果我们简单地按原样键入代码,我们将收到一个错误,提示 Lots() 访问器在 API 中不存在。 但是,如果有一种方法可以向现有 API 成员添加新方法而不影响任何其他功能呢?

假设此方法存在,我们可以创建一个简单的 cBot,在每次柱状图时,它会遍历所有当前头寸的列表,并在日志中打印它们的大小(以手为单位)。 我们将 OnBar() 方法定义如下。

1
2
3
4
5
6
protected override void OnBar()
{
    foreach (var position in Positions) {
        Print(position.Lots());
    }
}

扩展方法允许我们在几行代码中添加 Lots() 功能,然后在我们想要的任何 Position 类对象上重复使用它。 下面,我们解释如何创建一个扩展方法,并提供几个使用它们的算法示例。

扩展方法的工作原理

在使用扩展方法时,请注意以下规则。

  • 扩展方法始终是静态的。

要声明一个静态方法,只需使用 static 关键字。 下面,声明一个带有空主体的 Lots() 方法。

1
2
3
4
public static class MyExtensions {
    public static double Lots() {}

}
  • 扩展方法可以有任意数量的参数,但第一个参数必须指定该方法要调用的数据类型/类,并在前面加上 this 关键字。

我们将添加一个 Position 类的对象作为 Lots() 方法的第一个也是唯一的参数。 由于 Position 对象还包含有关开仓符号的信息,因此我们不需要任何其他参数。

1
2
3
public static class MyExtensions {
    public static double Lots(this Position position) {}
}
  • 扩展方法可以包含适合所提供参数的任意逻辑。

在定义扩展方法的主体时,无需使用特殊语法。 我们可以将其视为任何其他方法,因此可以如下定义其主体。

1
2
3
4
5
public static class MyExtensions {
    public static double Lots(this Position position) {
        return position.VolumeInUnits / position.Symbol.LotSize;
    }
}
  • 扩展方法可以作为实例方法或静态方法调用。

在 cBot 的代码中,有两种可能的方式来调用我们的扩展方法。

使用实例方法语法时,我们从 Position 类型的任何合适对象调用该方法。

1
2
var position = Positions.Last();
Print(position.Lots());

使用静态方法语法时,我们可以在完全指定其对应的静态类后调用我们的扩展方法。

1
2
var position = Positions.Last();
Print(MyExtensions.Lots(position));

由您决定哪种调用扩展方法的方式最方便。

方法签名

在使用实例语法时,避免您的扩展方法与任何内置 API 方法(例如 Position.Close())具有相同的签名。 在这些情况下,每次尝试调用具有匹配签名的扩展方法时,都会调用内置方法。

IntelliSense

当我们尝试调用扩展方法时,IntelliSense 使用特殊图标将其与内置 API 成员区分开来。

为了演示我们的新方法的实际效果,我们可以创建一个 cBot,在启动时下三个订单,每个订单的交易量不同。 在每个柱上,cBot 会打印当前所有开仓头寸的交易量(以手为单位)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
protected override void OnStart()
{
    ExecuteMarketOrder(TradeType.Buy, SymbolName, 10000);
    ExecuteMarketOrder(TradeType.Buy, SymbolName, 100000);
    ExecuteMarketOrder(TradeType.Buy, SymbolName, 50000);
}

protected override void OnBar()
{
    foreach (var position in Positions) {
        Print(position.Lots());
    }
}

在构建并启动我们的 cBot 后,我们应该会在日志中看到正确的值被打印出来。

在 cBot 中使用扩展方法

我们现在将尝试创建一个更复杂的 cBot。 在每个柱上,我们的算法将遍历当前开仓列表,并调整它们的止损水平,使其处于盈亏平衡状态。 为此,我们需要为 Position 类创建一个 BreakEven() 扩展方法。

我们创建一个新的 cBot 并重命名它。 之后,我们删除所有不需要的代码并添加 MyExtensions 类。

1
2
3
public static class MyExtensions {

}

我们的 BreakEven() 方法的代码相对简单。 我们检查一个头寸是否有止损,其总盈利是否大于零,以及当前设置的止损是否不等于头寸的建仓价。 如果所有这些条件都为真,我们将修改头寸的止损,使其等于头寸的建仓价。

1
2
3
4
5
6
7
public static class MyExtensions {
    public static void BreakEven(this Position position) {
        if (position.StopLoss is not null && position.GrossProfit > 0 && position.StopLoss != position.EntryPrice) {
            position.ModifyStopLossPrice(position.EntryPrice);
        }
    }
}

在 cBot 本身中,我们不需要使用 OnBar() 以外的任何方法。 在每个柱上,我们要求 cBot 执行一个简单的操作,即遍历 Positions 集合并为其中的每个元素调用新的 BreakEven() 方法。

1
2
3
4
5
6
protected override void OnBar() 
{
    foreach (var position in Positions) {
        position.BreakEven();
    }
}

在我们构建并启动 cBot 后,我们可以看到它的实际运行情况。 它可以成为一个有用的交易助手,尤其是在管理许多开仓头寸时。

在指标中使用扩展方法

我们还将创建一个依赖于扩展方法的有用指标。 该指标将通过绘制每个柱上符号价格相对于该柱开盘价的百分比变化来衡量波动性。

为此,我们将创建一个新指标并重命名它。 在代码编辑器窗口中,我们将创建 MyExtensions 类以扩展 Bar 类。

1
2
3
public static class MyExtensions {

}

我们还将添加 PercentageChange() 方法。 在 priceChange 变量中,我们从柱的收盘价中减去其开盘价。 该方法返回我们的价格变化除以开盘价并乘以 100。

1
2
3
4
5
6
7
8
public static class MyExtensions 
{
    public static double PercentageChange(this Bar bar) 
    {
        var priceChange = bar.Open - bar.Close;
        return priceChange / bar.Open * 100;
    }
}

在指标代码本身中,我们不需要 Initialize() 方法和不必要的参数。 在 Calculate() 方法的主体中,我们只需在每个柱上调用新的 PercentageChange() 方法。

1
2
3
4
5
6
7
8
[Output("Main")]
public IndicatorDataSeries Result { get; set; }


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

之后,我们保存并构建我们的指标。 创建其实例后,我们应该会看到绘制的正确百分比变化。 它们可用于确定短期和长期波动性,从而有益于各种交易策略。

摘要

总之,如果您想创建可重用的代码以向 cTrader API 添加新功能,扩展方法是一个有价值的工具。 我们强烈建议您尝试使用扩展方法,因为它们可以使您的算法更高效且更易于维护。