Перейти к содержанию

Как добавить методы в API cTrader

Методы расширения — это важный инструмент, если вы хотите добавить новую функциональность в API cTrader. Используя относительно простой синтаксис, вы можете добавить новое поведение к любому предопределенному классу API, например Symbol или Position. После того как вы определите метод расширения, вы можете вызвать его из любого объекта класса, который вы расширили.

Вариант использования методов расширения

Сначала мы проведем быструю демонстрацию того, почему вам может понадобиться использовать методы расширения.

С помощью сиБота мы хотим иметь возможность получить доступ к размеру данной позиции в лотах, поскольку эта информация напрямую влияет на нашу предпочтительную торговую стратегию. Для этого мы можем попытаться инициализировать переменную класса Position, а затем попытаться получить доступ к ее методу Lots().

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

Если мы просто напишем код как есть, мы получим ошибку, указывающую на то, что метод доступа Lots() не существует в API. Но что, если бы был способ добавить новый метод к существующему члену API, не затрагивая никакие другие функциональности?

Предполагая, что этот метод существует, мы могли бы создать простой сиБот, который на каждом баре перебирал бы список всех текущих позиций и выводил их размер в лотах в журнал. Мы бы определили метод 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;
    }
}
  • Методы расширения могут вызываться как методы экземпляра или как статические методы.

Существует два возможных способа вызвать наш метод расширения в коде сиБота.

При использовании синтаксиса метода экземпляра мы вызываем метод из любого подходящего объекта типа 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.

Чтобы продемонстрировать наш новый метод в действии, мы можем создать сиБот, который размещает три ордера при запуске, каждый с разным объемом. На каждом баре сиБот выводит объем всех текущих открытых позиций в лотах.

 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());
    }
}

После сборки и запуска нашего сиБота мы должны увидеть правильные значения, выводимые в журнал.

Используйте методы расширения в сиБотах

Теперь мы попытаемся создать более сложный сиБот. На каждом баре наш алгоритм будет просматривать список текущих открытых позиций и корректировать их уровни стоп-лосс так, чтобы они находились на уровне безубыточности. Для этого нам нужно будет создать метод расширения BreakEven() для класса Position.

Мы создаем нового сиБота и переименовываем его. После этого мы удаляем весь ненужный код и добавляем класс 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);
        }
    }
}

В самом сиБоте нам не нужно использовать никакой другой метод, кроме OnBar(). На каждом баре мы просим сиБота выполнить простую операцию, а именно итерировать коллекцию Positions и вызвать новый метод BreakEven() для каждого элемента в ней.

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

После того как мы соберем и запустим нашего сиБота, мы сможем увидеть его в действии. Он может стать полезным торговым помощником, особенно при управлении множеством открытых позиций.

Используйте методы расширения в индикаторах

Мы также создадим полезный индикатор, который использует методы расширения. Индикатор будет измерять волатильность, отображая процент изменения цены инструмента на каждом баре по сравнению с ценой открытия этого бара.

Для этого мы создадим новый индикатор и переименуем его. В окне редактора кода мы создадим класс 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();
}

После этого мы сохраняем и собираем наш индикатор. После создания его экземпляра мы должны увидеть отображение правильных процентных изменений. Их можно использовать для определения краткосрочной и долгосрочной волатильности, что полезно для всех видов торговых стратегий.

Обзор

В заключение, методы расширения — это ценный инструмент, если вы хотите создать многократно используемый код, который добавляет новые функции к API cTrader. Мы настоятельно рекомендуем экспериментировать с методами расширения, так как они могут сделать ваши алгоритмы более эффективными и простыми в обслуживании.