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

Примеры кода сиБотов

Эта страница содержит несколько примеров кода на Python и C# для создания торговых ботов и разработки алгоритмов. Обратите внимание, что ни один из перечисленных ниже сиБотов не гарантирует какой-либо финансовой прибыли. Обязательно проведите бэктестинг и настройте свои сиБоты перед развертыванием каких-либо экземпляров на реальном счете.

Репозитории с примерами сиБотов

Полные примеры кода сиБотов, включая готовые к запуску шаблоны для различных автоматизированных стратегий и стилей торговли, доступны в отдельных репозиториях Python и C# на GitHub.

Синхронные операции

Все сиБоты в этом разделе выполняют свои операции синхронно.

Выполнение рыночных ордеров

  • Простой сиБот, выполняющий успешные операции

    Следующий торговый бот создает рыночный ордер при запуске и сохраняет результат в переменной result.

    Если выполнение ордера прошло успешно, цена входа выводится в журнал.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    [Robot()]
    public class Sample_cBot : Robot
    {
        protected override void OnStart()
        {
            var result = ExecuteMarketOrder(TradeType.Buy, SymbolName, 10000);
    
            if (result.IsSuccessful)
            {
                var position = result.Position;
                Print("Position entry price is {0}", position.EntryPrice);
            }
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    class Sample_cBot():
        def on_start(self):
            result = api.ExecuteMarketOrder(TradeType.Buy, api.SymbolName, 10000)
    
            if result.IsSuccessful:
                position = result.Position
                api.Print(f"Position entry price is {position.EntryPrice}")
    

    Вывод журнала

    • сиБот "New cBot" был успешно запущен для EURUSD, h1.
    • Выполнение рыночного ордера на покупку 10000 EURUSD
    • Выполнение рыночного ордера на покупку 10000 EURUSD УСПЕШНО, позиция PID14576001
    • Цена входа позиции — 1.19067
  • Простой сиБот с настраиваемыми параметрами

    При объявлении различных свойств сиБота вы можете превратить их в настраиваемые параметры, используя объявление [Parameter()]. При запуске нового экземпляра вашего сиБота вы (или другие пользователи) сможете назначить пользовательские значения этим параметрам.

    Рассмотрим следующий пример.

    1
    2
    [Parameter("SMA Period", DefaultValue = 14)]
    public int SmaPeriod { get; set; }
    

    Примечание

    СиБоты на Python используют настраиваемые параметры, объявленные в их файлах .cs.

    1
    2
    [Parameter("SMA Period", DefaultValue = 14)]
    public int SmaPeriod { get; set; }
    

    В приведенном выше примере мы определяем следующие характеристики:

    • Название параметра. Впоследствии оно появится в интерфейсе cTrader ("SMA Period").
    • Значение параметра по умолчанию, которое будет применяться ко всем новым экземплярам, если оно не будет изменено пользователями (DefaultValue = 14).

    В приведенном ниже коде мы показываем, как свойство SmaPeriod может использоваться в реальном торговом боте.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class SamplecBotReferenceSMA : Robot
    {
        [Parameter("Source")]
        public DataSeries Source { get; set; }
    
        [Parameter("SMA Period", DefaultValue = 14)]
        public int SmaPeriod { get; set; }
    
        private SampleSMA sma;
    
        protected override void OnStart()
        {
            sma = Indicators.GetIndicator<SampleSMA>(Source, SmaPeriod);
        }
    
        protected override void OnTick()
        {
            Print("{0}", sma.Result.LastValue);
        }
    }
    

    Примечание

    СиБоты на Python используют настраиваемые параметры, объявленные в их файлах .cs.

    1
    2
    3
    4
    5
    6
    class Sample_cBot():
        def on_start(self):
            self.sma = api.Indicators.GetIndicator<SampleSMA>(api.Source, api.SmaPeriod)
    
        def on_tick(self):
            api.Print(f"{self.sma.Result.LastValue}")
    

    Наш бот берет настраиваемое свойство SmaPeriod и при запуске передает его значение методу Indicators.GetIndicator<SampleSMA>(). Этот метод возвращает значение простой скользящей средней для указанного периода.

    При создании экземпляра сиБота все настраиваемые параметры можно настроить в окне Добавить экземпляр.

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

    Вывод журнала

    • Экземпляр CBot [Sample cBot Reference SMA, EURUSD, h1] запущен.
    • 0.975685714285714
    • 0.975681428571429
    • 0.97568
    • Экземпляр CBot [Sample cBot Reference SMA, EURUSD, h1] остановлен пользователем.
  • Выполнение рыночного ордера с дополнительными аргументами

    В предыдущем примере мы передали минимально возможное количество аргументов методу ExecuteMarketOrder(). Это были тип сделки (TradeType.Buy), инструмент (Symbol) и объем (-1).

    Метод ExecuteMarketOrder() может быть вызван с дополнительными аргументами, такими как Label, StopLoss, TakeProfit и Comment. В приведенном ниже примере указывается метка ("order 1"), механизм защиты стоп-лосс (10) и уровень тейк-профит (10).

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    [Robot(TimeZone = TimeZones.UTC)]
    public class Sample_cBot : Robot
    {
        protected override void OnStart()
        {
            var result = ExecuteMarketOrder(TradeType.Buy, SymbolName, 10000, "order 1", 10, 10);
    
            if (result.IsSuccessful)
            {
                var position = result.Position;
                Print("Position entry price is {0}", position.EntryPrice);
                Print("Position SL price is {0}", position.StopLoss);
            }
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    class Sample_cBot():
        def on_start(self):
            result = api.ExecuteMarketOrder(TradeType.Buy, api.SymbolName, 10000, "order 1", 10, 10)
    
            if result.IsSuccessful:
                position = result.Position
                api.Print(f"Position entry price is {position.EntryPrice}")
                api.Print(f"Position SL price is {position.StopLoss}")
    

    Вывод журнала

    • сиБот "New cBot" был успешно запущен для EURUSD, h1.
    • Выполнение рыночного ордера на покупку 10000 EURUSD (SL: 10, TP: 10)
    • Выполнение рыночного ордера на покупку 10000 EURUSD (SL: 10, TP: 10) УСПЕШНО, позиция PID14576098
    • Цена входа позиции — 1.1896
    • Цена SL позиции — 1.1886

Изменить позицию

В приведенном ниже примере мы добавляем только значение тейк-профит (10) при выполнении ордера. После этого мы изменяем позицию, чтобы добавить стоп-лосс.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
[Robot()]
public class SamplecBbot : Robot
{
    protected override void OnStart()
    {
        var result = ExecuteMarketOrder(TradeType.Buy, SymbolName, 10000,
                                "order 1", null, 10);
        if (result.IsSuccessful)
        {
            var position = result.Position;
            Print("Position SL price is {0}", position.StopLoss);

            var stopLoss = position.EntryPrice - 10*Symbol.PipSize;
            ModifyPosition(position, stopLoss, position.TakeProfit);

            Print("New Position SL price is {0}", position.StopLoss);

        }
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class Sample_cBot():
    def on_start(self):
        result = api.ExecuteMarketOrder(TradeType.Buy, api.SymbolName, 10000, "order 1", None, 10)

        if result.IsSuccessful:
            position = result.Position
            api.Print(f"Position SL price is {position.StopLoss}")
            stopLoss = position.EntryPrice - 10 * api.Symbol.PipSize
            api.ModifyPosition(position, stopLoss, position.TakeProfit)
            api.Print(f"New Position SL price is {position.StopLoss}")

Вывод журнала

  • сиБот "New cBot" был успешно запущен для EURUSD, h1.
  • Выполнение рыночного ордера на покупку 10000 EURUSD (TP: 10)
  • Выполнение рыночного ордера на покупку 10000 EURUSD (TP: 10) УСПЕШНО, позиция PID14576161
  • Цена SL позиции — null
  • Изменение позиции PID14576161 (SL: 1.18744, TP: 1.18944)
  • Изменение позиции PID14576161 (SL: 1.18744, TP: 1.18944) УСПЕШНО, позиция PID14576161
  • Новая цена SL позиции — 1.18744

Закрытие позиции

  • Выполнение полного закрытия

    Приведенный ниже пример кода размещает рыночный ордер. Если валовая прибыль полученной позиции превышает определенное значение (null && position.GrossProfit > 10), она закрывается.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    [Robot()]
    public class Sample_cBot : Robot
    {
        protected override void OnStart()
        {
            ExecuteMarketOrder(TradeType.Buy, SymbolName, 10000, "myLabel");
        }
    
        protected override void OnTick()
        {
            var position = Positions.Find("myLabel");
            if (position != null && position.GrossProfit > 10)
            {
                ClosePosition(position);
                Stop();
            }
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Sample_cBot():
        def on_start(self):
            api.ExecuteMarketOrder(TradeType.Buy, api.SymbolName, 10000, "myLabel")
    
        def on_tick(self):
            position = api.Positions.Find("myLabel")
            if position is not None and position.GrossProfit > 10:
                api.ClosePosition(position)
                api.Stop()
    

    Вывод журнала

    • сиБот "New cBot" был успешно запущен для EURUSD, h1.
    • Исполнение рыночного ордера на покупку 10000 EURUSD
    • Исполнение рыночного ордера на покупку 10000 EURUSD ВЫПОЛНЕНО, Позиция PID14576180
  • Выполнить частичное закрытие

    Мы изменим предыдущий пример, чтобы создать два рыночных ордера с одинаковыми метками ("myLabel"). На каждом новом баре наш торговый робот будет закрывать ровно половину одного из этих ордеров, но только если его объем равен или превышает 20 000.

    Мы также используем метод Positions.FindAll(). Он возвращает список позиций, по которым мы можем выполнить итерацию.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    [Robot()]
    public class Sample_cBot : Robot
    {
        protected override void OnStart()
        {
            ExecuteMarketOrder(TradeType.Buy, SymbolName, 20000, "myLabel");
            ExecuteMarketOrder(TradeType.Buy, SymbolName, 30000, "myLabel");
        }
    
        protected override void OnBar()
        {
            var positions = Positions.FindAll("myLabel", SymbolName, TradeType.Buy);
    
            foreach (var position in positions)
            {
                if (position.VolumeInUnits >= 20000)
                {
                    ClosePosition(position, 15000);
                }
            }
        }
    }
    
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    class Sample_cBot():
        def on_start(self):
            api.ExecuteMarketOrder(TradeType.Buy, api.SymbolName, 20000, "myLabel")
            api.ExecuteMarketOrder(TradeType.Buy, api.SymbolName, 30000, "myLabel")
    
        def on_tick(self):
            positions = api.Positions.FindAll("myLabel", api.SymbolName, TradeType.Buy)
            for position in positions:
                if position.VolumeInUnits >= 20000:
                    api.ClosePosition(position, 15000)
    

    Вывод журнала

    • сиБот "New cBot" был успешно запущен для EURUSD, h1.
    • Исполнение рыночного ордера на покупку 20000 EURUSD
    • Исполнение рыночного ордера на покупку 20000 EURUSD ВЫПОЛНЕНО, Позиция PID14579299
    • Исполнение рыночного ордера на покупку 30000 EURUSD
    • Исполнение рыночного ордера на покупку 30000 EURUSD ВЫПОЛНЕНО, Позиция PID14579300

Создать отложенные ордера

  • Создать лимитные и стоп ордера

    Лимитные и стоп ордера являются отложенными ордерами; несмотря на это, они создаются аналогично рыночным ордерам. Однако при создании лимитных и стоп ордеров вы также должны указать их целевую цену, и макс. проскальзывание не используется.

    Этот cBot создает два лимитных ордера и стоп ордер. Затем он выполняет итерацию по ордерам и выводит их метки и ID в лог.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    [Robot()]
    public class Sample_cBot : Robot
    {
        protected override void OnStart()
        {
            PlaceLimitOrder(TradeType.Buy, SymbolName, 10000, Symbol.Bid, "myLimitOrder");
            PlaceLimitOrder(TradeType.Buy, SymbolName, 20000, Symbol.Bid-2*Symbol.PipSize,
                    "myLimitOrder");
            PlaceStopOrder(TradeType.Buy, SymbolName, 10000, Symbol.Ask, "myStopOrder");
    
            foreach (var pendingOrder in PendingOrders)
            {
                Print("Order placed with label {0}, id {1}", pendingOrder.Label, pendingOrder.Id);
            }
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    class Sample_cBot():
        def on_start(self):
            api.PlaceLimitOrder(TradeType.Buy, api.SymbolName, 10000, api.Symbol.Bid, "myLimitOrder")
            api.PlaceLimitOrder(TradeType.Buy, api.SymbolName, 20000, api.Symbol.Bid - 2 * api.Symbol.PipSize, "myLimitOrder")
            api.PlaceStopOrder(TradeType.Buy, api.SymbolName, 10000, api.Symbol.Ask, "myStopOrder")
    
            for pendingOrder in api.PendingOrders:
                Print(f"Order placed with label {pendingOrder.Label}, id {pendingOrder.Id}")
    

    Вывод журнала

    • сиБот "New cBot" был успешно запущен для EURUSD, h1.
    • Размещение лимитного ордера на покупку 10000 EURUSD (Цена: 1.19036)
    • Размещение лимитного ордера на покупку 10000 EURUSD (Цена: 1.19036) ВЫПОЛНЕНО, Отложенный ордер OID25220794
    • Размещение лимитного ордера на покупку 20000 EURUSD (Цена: 1.19017)
    • Размещение лимитного ордера на покупку 20000 EURUSD (Цена: 1.19017) ВЫПОЛНЕНО, Отложенный ордер OID25220795
    • Размещение стоп ордера на покупку 10000 EURUSD (Цена: 1.19040)
    • Размещение стоп ордера на покупку 10000 EURUSD (Цена: 1.19040) ВЫПОЛНЕНО, Отложенный ордер OID25220796
    • Ордер размещен с меткой myLimitOrder, id 25220794
    • Ордер размещен с меткой myLimitOrder, id 25220795
    • Ордер размещен с меткой myStopOrder, id 25220796
  • Создать отложенные ордера с дополнительными параметрами

    Как и в случае с рыночными ордерами, вы также можете указать метку ордера, различные механизмы защиты, дату истечения срока действия ордера и добавить комментарий.

     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
    [Robot()]
    public class Sample_cBot : Robot
    {
        protected override void OnStart()
        {
            var midnight = Server.Time.AddDays(1).Date;
    
            PlaceLimitOrder(TradeType.Buy, SymbolName, 10000, Symbol.Bid, "mySample_cBot", 10, null, midnight, "First");
    
            PlaceStopOrder(TradeType.Buy, SymbolName, 10000, Symbol.Ask, "mySample_cBot", 10, 10, null, "Second");
    
            foreach (var order in PendingOrders)
            {
                var sl = order.StopLoss == null ? "" : "SL: " + order.StopLoss;
                var tp = order.TakeProfit == null ? "" : " TP: " + order.TakeProfit;
    
                var text = string.Format("{0} {1}", sl, tp);
    
                if (order.OrderType == PendingOrderType.Limit)
                    Print(order.Comment + " Limit Order " + text);
                else
                    Print(order.Comment + " Stop Order " + text);
            }
        }
    }
    
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    class Sample_cBot():
        def on_start(self):
            midnight = api.Server.Time.AddDays(1).Date
    
            api.PlaceLimitOrder(TradeType.Buy, api.SymbolName, 10000, api.Symbol.Bid, "mySample_cBot", 10, None, midnight, "First")
            api.PlaceStopOrder(TradeType.Buy, api.SymbolName, 10000, api.Symbol.Ask, "mySample_cBot", 10, 10, None, "Second")
    
            for order in api.PendingOrders:
                sl = "" if order.StopLoss is None else f"SL: {order.StopLoss}"
                tp = "" if order.TakeProfit is None else f"TP: {order.TakeProfit}"
    
                api.Print(f"{order.Comment} {order.OrderType} Order {sl} {tp}")
    

    Вывод журнала

    • сиБот "New cBot" был успешно запущен для EURUSD, h1.
    • Размещение лимитного ордера на покупку 10000 EURUSD (Цена: 1.19049, SL: 10, Истекает: 12/05/2018 00:00:00)
    • Размещение лимитного ордера на покупку 10000 EURUSD (Цена: 1.19049, SL: 10, Истекает: 12/05/2018 00:00:00) ВЫПОЛНЕНО, Отложенный ордер OID25220807
    • Размещение стоп ордера на покупку 10000 EURUSD (Цена: 1.19053, SL: 10, TP: 10)
    • Размещение стоп ордера на покупку 10000 EURUSD (Цена: 1.19053, SL: 10, TP: 10) ВЫПОЛНЕНО, Отложенный ордер OID25220808
    • Лимитный ордер
    • Первый лимитный ордер SL: 1.18949
    • Второй стоп ордер SL: 1.18953 TP: 1.19153

Изменение отложенных ордеров

Можно изменить несколько характеристик отложенных ордеров.

Пример ниже демонстрирует, как изменить целевую цену, уровни защиты или дату и время истечения срока действия отложенного ордера.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[Robot()]
public class Sample_cBot : Robot
{
    protected override void OnStart()
    {
        var price = Symbol.Ask + 10 * Symbol.PipSize;
        var expiry = Server.Time.AddHours(12);
        PlaceStopOrder(TradeType.Buy, SymbolName, 10000, price, "myLabel", 10, 10, expiry);
    }

    protected override void OnBar()
    {
        foreach (var order in PendingOrders)
        {
            if (order.Label == "myLabel")
            {
                double newPrice = Symbol.Ask + 5 * Symbol.PipSize;
                ModifyPendingOrder(order, newPrice, order.StopLossPips,
                                                            order.TakeProfitPips, order.ExpirationTime);
            }
        }
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Sample_cBot():
    def on_start(self):
        price = api.Symbol.Ask + 10 * api.Symbol.PipSize
        expiry = Server.Time.AddHours(12)

        api.PlaceStopOrder(TradeType.Buy, api.SymbolName, 10000, price, "myLabel", 10, 10, expiry)

    def on_bar(self):
        for order in api.PendingOrders:
            if order.Label == "myLabel":
                newPrice = api.Symbol.Ask + 5 * api.Symbol.PipSize
                api.ModifyPendingOrder(order, newPrice, order.StopLossPips, order.TakeProfitPips, order.ExpirationTime)

Вывод журнала

  • сиБот "New cBot" был успешно запущен для EURUSD, h1.
  • Размещение стоп ордера на покупку 10000 EURUSD (Цена: 1.19254, SL: 10, TP: 10, Истекает: 11/05/2018 20:07:25)
  • Размещение стоп ордера на покупку 10000 EURUSD (Цена: 1.19254, SL: 10, TP: 10, Истекает: 11/05/2018 20:07:25) ВЫПОЛНЕНО, Отложенный ордер OID25220877

Отмена отложенных ордеров

Синтаксис для отмены ордера: CancelPendingOrder(order), где order имеет тип PendingOrder.

Пример ниже показывает отмену всех ордеров с меткой "myLabel".

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
[Robot()]
public class Sample_cBot : Robot
{
    protected override void OnTick()
    {
        foreach (var order in PendingOrders)
        {
            if (order.Label == "myLabel")
            {
                CancelPendingOrder(order);
            }
        }
    }
}
1
2
3
4
5
class Sample_cBot():
    def on_tick(self):
        for order in api.PendingOrders:
            if order.Label == "myLabel":
                api.CancelPendingOrder(order)

События позиции

Можно подписаться на события, связанные с различными торговыми операциями. Например, чтобы проверить, открыта ли позиция, мы подписываемся на событие, которое вызывается для всех объектов Position при открытии.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
[Robot()]
public class Sample_cBot : Robot
{
    protected override void OnStart()
    {
        Positions.Opened += PositionsOnOpened;
        ExecuteMarketOrder(TradeType.Buy, Symbol, 10000, "myLabel", 10, 10);
    }

    private void PositionsOnOpened(PositionOpenedEventArgs args)
    {
        var pos = args.Position;
        Print("Position opened at {0}", pos.EntryPrice);
    }
}
1
2
3
4
5
6
7
8
class Sample_cBot():
    def on_start(self):
        api.Positions.Opened += self.on_position_opened
        api.ExecuteMarketOrder(TradeType.Buy, api.Symbol, 10000, "myLabel", 10, 10)

    def on_position_opened(self, args):
        pos = args.Position
        api.Print(f"Position opened at {pos.EntryPrice}")

Вывод журнала

  • сиБот "New cBot" был успешно запущен для EURUSD, h1.
  • Исполнение рыночного ордера на покупку 10000 EURUSD (SL: 10, TP: 10)
  • Исполнение рыночного ордера на покупку 10000 EURUSD (SL: 10, TP: 10) ВЫПОЛНЕНО, Позиция PID14579468
  • Позиция открыта по 1.19224

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
[Robot()]
public class Sample_cBot : Robot
{
    protected override void OnStart()
    {
        Positions.Closed += PositionsOnClosed;
        ExecuteMarketOrder(TradeType.Buy, Symbol, 10000, "myLabel", 10, 10);
    }

    protected override void OnBar()
    {
        var position = Positions.Find("myLabel");
        if (position != null)
            ClosePosition(position);
    }

    private void PositionsOnClosed(PositionClosedEventArgs args)
    {
        var pos = args.Position;
        Print("Position closed with {0} profit", pos.GrossProfit);
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Sample_cBot():
    def on_start(self):
        api.Positions.Closed += self.on_position_closed
        api.ExecuteMarketOrder(TradeType.Buy, api.Symbol, 10000, "myLabel", 10, 10)

    def on_bar(self):
        position = api.Positions.Find("myLabel")

        if position is not None:
            api.ClosePosition(position);            

    def on_position_closed(self, args):
        pos = args.Position
        api.Print(f"Position closed with {pos.GrossProfit} profit")

Вывод журнала

  • сиБот "New cBot" был успешно запущен для EURUSD, h1.
  • Исполнение рыночного ордера на покупку 10000 EURUSD (SL: 10, TP: 10)
  • Исполнение рыночного ордера на покупку 10000 EURUSD (SL: 10, TP: 10) ВЫПОЛНЕНО, Позиция PID14579479
  • Закрытие позиции PID14579299
  • Закрытие позиции PID14579299 ВЫПОЛНЕНО, Позиция PID14579299
  • Позиция закрыта с прибылью 20.64

Преобразование координат

Приведенный ниже cBot позволяет пользователям размещать лимитные ордера в нужном направлении простым щелчком правой кнопкой мыши на графике. Это достигается путем преобразования Y-координат мыши в Y-координаты графика (которые для символов соответствуют ценам символов).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
[Robot(AccessRights = AccessRights.None)]
public class CoordinatesConverter : Robot
{
    protected override void OnStart()
    {
        Chart.MouseUp += Chart_MouseUp;
    }

    private void Chart_MouseUp(ChartMouseEventArgs obj)
    {
        var desiredPrice = Chart.YToYValue(obj.MouseY);
        var desiredTradeType = desiredPrice > Symbol.Bid ? TradeType.Sell : TradeType.Buy;
        PlaceLimitOrder(desiredTradeType, SymbolName, 10000, desiredPrice);
    }
}
1
2
3
4
5
6
7
8
class Sample_cBot():
    def on_start(self):
        api.Chart.MouseUp += self.on_chart_mouse_up        

    def on_chart_mouse_up(self, args):
        desiredPrice = api.Chart.YToYValue(args.MouseY)
        desiredTradeType = TradeType.Sell if desiredPrice > api.Symbol.Bid else TradeType.Buy
        api.PlaceLimitOrder(desiredTradeType, api.SymbolName, 10000, desiredPrice)

Асинхронное выполнение

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

Исполнить рыночные ордера асинхронно

Синтаксис асинхронных методов аналогичен синтаксису синхронных. Хотя они принимают одинаковые типы аргументов, их возвращаемый тип — TradeOperation вместо TradeResult.

  • Базовые асинхронные операции

    Следующий cBot демонстрирует, как работают асинхронные операции. Создается рыночный ордер; в следующем условии cBot проверяет, выполняется ли операция.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    [Robot()]
    public class Sample_cBot : Robot
    {
        protected override void OnStart()
        {
            var operation = ExecuteMarketOrderAsync(TradeType.Buy, SymbolName, 10000);
    
            if (operation.IsExecuting)
            {
                Print("Operation Is Executing");
            }
        }
    }
    
    1
    2
    3
    4
    5
    6
    class Sample_cBot():
        def on_start(self):
            operation = api.ExecuteMarketOrderAsync(TradeType.Buy, api.SymbolName, 10000)
    
            if operation.IsExecuting:
                api.Print("Operation Is Executing")
    

    Вывод журнала

    • сиБот "New cBot" был успешно запущен для EURUSD, h1.
    • Исполнение рыночного ордера на покупку 10000 EURUSD
    • Операция выполняется
    • Исполнение рыночного ордера на покупку 10000 EURUSD ВЫПОЛНЕНО, Позиция PID14579532
  • Исполнить ордер

    Следующий пример подчеркивает разницу между синхронными и асинхронными методами.

    cBot проверяет, выполняется ли операция, сразу после вызова асинхронного метода. Он делает это снова после вызова синхронного метода. Вывод в лог для этих двух действий различается.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    [Robot()]
    public class Sample_cBot : Robot
    {
        protected override void OnStart()
        {
            var operation = ExecuteMarketOrderAsync(TradeType.Buy, SymbolName, 10000);
            Print(operation.IsExecuting ? "Operation Is Executing" : "Operation executed");
            ExecuteMarketOrder(TradeType.Buy, SymbolName, 20000);
            Print(operation.IsExecuting ? "Operation Is Executing" : "Operation executed");
        }
    }
    
    1
    2
    3
    4
    5
    6
    class Sample_cBot():
        def on_start(self):
            operation = api.ExecuteMarketOrderAsync(TradeType.Buy, api.SymbolName, 10000)
            api.Print("Operation Is Executing" if operation.IsExecuting else "Operation executed")
            api.ExecuteMarketOrder(TradeType.Buy, api.SymbolName, 20000)
            api.Print("Operation Is Executing" if operation.IsExecuting else "Operation executed")
    

    Вывод журнала

    • сиБот "New cBot" был успешно запущен для EURUSD, h1.
    • Исполнение рыночного ордера на покупку 10000 EURUSD
    • Операция выполняется
    • Исполнение рыночного ордера на покупку 20000 EURUSD
    • Исполнение рыночного ордера на покупку 10000 EURUSD ВЫПОЛНЕНО, Позиция PID14579541
    • Исполнение рыночного ордера на покупку 20000 EURUSD ВЫПОЛНЕНО, Позиция PID14579542
    • Операция выполнена
  • Исполнить ордер с дополнительными параметрами

    Следующий cBot размещает ордер, указывая его метку ("myLabel"), уровни защиты (10, 10), символ (SymbolName) и объем (10000).

    Пример также содержит коллекцию Positions и метод FindAll(). Find() и FindAll() можно использовать для поиска позиций с одинаковой меткой, символом и типом сделки.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    [Robot()]
    public class Sample_cBot : Robot
    {
        protected override void OnStart()
        {
            ExecuteMarketOrderAsync(TradeType.Buy, SymbolName, 10000, "myLabel", 10, 10);
        }
        protected override void OnTick()
        {
            var positions = Positions.FindAll("myLabel", SymbolName, TradeType.Buy);
    
            if (positions.Length == 0)
                return;
    
            foreach (var position in positions)
                Print("Buy at {0} SL {1}", position.EntryPrice, position.StopLoss);
    
            Stop();
        }
    }
    
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    class Sample_cBot():
        def on_start(self):
            api.ExecuteMarketOrderAsync(TradeType.Buy, api.SymbolName, 10000, "myLabel", 10, 10)
    
        def on_tick(self):
            positions = api.Positions.FindAll("myLabel", api.SymbolName, TradeType.Buy)
    
            if positions.Length == 0:
                return
    
            for position in positions:
                api.Print(f"Buy at {position.EntryPrice} SL {position.StopLoss}")
    
            api.Stop()            
    

    Вывод журнала

    • сиБот "New cBot" был успешно запущен для EURUSD, h1.
    • Исполнение рыночного ордера на покупку 10000 EURUSD (SL: 10, TP: 10)
    • Исполнение рыночного ордера на покупку 10000 EURUSD (SL: 10, TP: 10) ВЫПОЛНЕНО, Позиция PID14579719
    • Покупка по 1.19087 SL null
    • Покупка по 1.19357 SL 1.19257
    • cBot "New cBot" остановлен для EURUSD, h1.

Изменить позицию асинхронно

Приведенный ниже cBot размещает рыночный ордер, а затем изменяет вновь открытую позицию.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
[Robot()]
public class Sample_cBot : Robot
{
    protected override void OnStart()
    {
        ExecuteMarketOrderAsync(TradeType.Buy, SymbolName, 10000, "myLabel", null, 10);
    }

    protected override void OnTick()
    {
        Position myPosition = Positions.Find("myLabel");
        if (myPosition != null && myPosition.StopLoss == null)
        {
            double stopLoss = Symbol.Bid - 10 * Symbol.PipSize;
            ModifyPositionAsync(myPosition, stopLoss, myPosition.TakeProfit);
        }
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Sample_cBot():
    def on_start(self):
        api.ExecuteMarketOrderAsync(TradeType.Buy, api.SymbolName, 10000, "myLabel", None, 10)

    def on_tick(self):
        myPosition = Positions.Find("myLabel")

        if myPosition is None or myPosition.StopLoss is not None:
            return

        stopLoss = api.Symbol.Bid - 10 * api.Symbol.PipSize

        api.ModifyPositionAsync(myPosition, stopLoss, myPosition.TakeProfit)        

Вывод журнала

  • сиБот "New cBot" был успешно запущен для EURUSD, h1.
  • Исполнение рыночного ордера на покупку 10000 EURUSD (TP: 10)
  • Исполнение рыночного ордера на покупку 10000 EURUSD (TP: 10) ВЫПОЛНЕНО, Позиция PID14579736
  • Изменение позиции PID14579300 (SL: 1.19213, TP: null)
  • Изменение позиции PID14579300 (SL: 1.19213, TP: null) ВЫПОЛНЕНО, Позиция PID14579300

Закрыть позицию асинхронно

Следующий пример демонстрирует асинхронное закрытие позиции, если она существует.

Метод Find() используется для поиска в коллекции Positions позиции с определенной меткой.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
[Robot()]
public class Sample_cBot : Robot
{
    protected override void OnStart()
    {
        ExecuteMarketOrderAsync(TradeType.Buy, Symbol, 10000, "myLabel", null, 10);
    }

    protected override void OnTick()
    {
        Position myPosition = Positions.Find("myLabel");
        if (myPosition != null && myPosition.GrossProfit > 10)
        {
            ClosePositionAsync(myPosition);
            Stop();
        }
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Sample_cBot():
    def on_start(self):
        api.ExecuteMarketOrderAsync(TradeType.Buy, api.SymbolName, 10000, "myLabel", None, 10)

    def on_tick(self):
        myPosition = Positions.Find("myLabel")

        if myPosition is None or myPosition.GrossProfit <= 10:
            return

        api.ClosePositionAsync(myPosition)
        api.Stop()        

Вывод журнала

  • сиБот "New cBot" был успешно запущен для EURUSD, h1.
  • Выполнение рыночного ордера на покупку 10000 EURUSD (TP: 10)
  • Выполнение рыночного ордера на покупку 10000 EURUSD (TP: 10) ВЫПОЛНЕНО УСПЕШНО, Позиция PID14579740
  • Закрытие позиции PID14579300
  • Закрытие позиции PID14579300 ВЫПОЛНЕНО УСПЕШНО, Позиция PID14579300
  • сиБот "New cBot" был остановлен для EURUSD, h1.

Размещение лимитных и стоп ордеров асинхронно

Как указано выше, размещение отложенных ордеров аналогично созданию рыночных ордеров.

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[Robot()]
public class Sample_cBot : Robot
{
    protected override void OnStart()
    {
        var expiry = Server.Time.AddHours(12);
        PlaceLimitOrderAsync(TradeType.Buy, SymbolName, 10000, Symbol.Bid, "myLabel", null, null, expiry);
        PlaceStopOrderAsync(TradeType.Buy, SymbolName, 10000, Symbol.Ask + 10 * Symbol.PipSize, "myLabel", null, null, expiry);
    }
}
1
2
3
4
5
class Sample_cBot():
    def on_start(self):
        expiry = api.Server.Time.AddHours(12)
        api.PlaceLimitOrderAsync(TradeType.Buy, api.SymbolName, 10000, api.Symbol.Bid, "myLabel", None, None, expiry)
        api.PlaceStopOrderAsync(TradeType.Buy, api.SymbolName, 10000, api.Symbol.Ask + 10 * api.Symbol.PipSize, "myLabel", None, None, expiry)    

Вывод журнала

  • сиБот "New cBot" был успешно запущен для EURUSD, h1.
  • Размещение лимитного ордера на покупку 10000 EURUSD (Цена: 1.19382, Истекает: 12/05/2018 00:18:19)
  • Размещение стоп ордера на покупку 10000 EURUSD (Цена: 1.19487, Истекает: 12/05/2018 00:18:19)
  • Размещение лимитного ордера на покупку 10000 EURUSD (Цена: 1.19382, Истекает: 12/05/2018 00:18:19) ВЫПОЛНЕНО УСПЕШНО, Отложенный ордер OID25221859
  • Размещение стоп ордера на покупку 10000 EURUSD (Цена: 1.19487, Истекает: 12/05/2018 00:18:19) ВЫПОЛНЕНО УСПЕШНО, Отложенный ордер OID25221860

Изменение отложенных ордеров асинхронно

Следующий сиБот изменяет лимитные ордера асинхронно.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
[Robot()]
public class Sample_cBot : Robot
{
    protected override void OnStart()
    {
        var expiry = Server.Time.AddHours(12);
        PlaceLimitOrderAsync(TradeType.Buy, SymbolName, 10000, Symbol.Bid, "myLabel", null, 10, expiry);

    }
    protected override void OnTick()
    {
        foreach (var order in PendingOrders)
        {
            if (order.Label == "myLabel" && order.StopLoss == null)
                ModifyPendingOrderAsync(order, order.TargetPrice, 10, 10, null);
        }
    }
}
1
2
3
4
5
6
7
8
9
class Sample_cBot():
    def on_start(self):
        expiry = api.Server.Time.AddHours(12)
        api.PlaceLimitOrderAsync(TradeType.Buy, api.SymbolName, 10000, api.Symbol.Bid, "myLabel", None, 10, expiry)

    def on_tick(self):
        for order in api.PendingOrders:
            if order.Label == "myLabel" and order.StopLoss is None:
                api.ModifyPendingOrderAsync(order, order.TargetPrice, 10, 10, None)

Вывод журнала

  • сиБот "New cBot" был успешно запущен для EURUSD, h1.
  • Размещение лимитного ордера на покупку 10000 EURUSD (Цена: 1.19347, TP: 10, Истекает: 12/05/2018 00:22:08)
  • Изменение отложенного ордера OID25221860 (Объем: 10000, Цена: 1.19487, SL: 10, TP: 10, Истекает: 12/05/2018 00:18:19)
  • Размещение лимитного ордера на покупку 10000 EURUSD (Цена: 1.19347, TP: 10, Истекает: 12/05/2018 00:22:08) ВЫПОЛНЕНО УСПЕШНО, Отложенный ордер OID25221906
  • Изменение отложенного ордера OID25221860 (Объем: 10000, Цена: 1.19487, SL: 10, TP: 10, Истекает: 12/05/2018 00:18:19) ВЫПОЛНЕНО УСПЕШНО, Отложенный ордер OID25221860
  • Изменение отложенного ордера OID25221906 (Объем: 10000, Цена: 1.19347, SL: 10, TP: 10, Истекает: 12/05/2018 00:22:08)
  • Изменение отложенного ордера OID25221906 (Объем: 10000, Цена: 1.19347, SL: 10, TP: 10, Истекает: 12/05/2018 00:22:08) ВЫПОЛНЕНО УСПЕШНО, Отложенный ордер OID25221906

Отмена отложенных ордеров асинхронно

  • Отмена всех отложенных ордеров

    Приведенный ниже сиБот асинхронно отменяет все текущие отложенные ордера.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    [Robot()]
    public class Sample_cBot : Robot
    {
        protected override void OnBar()
        {
            foreach (var pendingOrder in PendingOrders)
            {
                CancelPendingOrderAsync(pendingOrder);
            }
        }
    }
    
    1
    2
    3
    4
    class Sample_cBot():
        def on_bar(self):
            for order in api.PendingOrders:
                api.CancelPendingOrderAsync(order)
    

    Вывод журнала

    • сиБот "cancel pending order" был успешно запущен для EURUSD, h1.
    • Отмена отложенного ордера OID274705
    • Отмена отложенного ордера OID274706
    • Отмена отложенного ордера OID274707
    • Отмена отложенного ордера OID274708
    • Отмена отложенного ордера OID274709
    • Отмена отложенного ордера OID274705 ВЫПОЛНЕНА УСПЕШНО, Отложенный ордер OID274705
    • Отмена отложенного ордера OID274706 ВЫПОЛНЕНА УСПЕШНО, Отложенный ордер OID274706
    • Отмена отложенного ордера OID274707 ВЫПОЛНЕНА УСПЕШНО, Отложенный ордер OID274707
    • Отмена отложенного ордера OID274708 ВЫПОЛНЕНА УСПЕШНО, Отложенный ордер OID274708
    • Отмена отложенного ордера OID274709 ВЫПОЛНЕНА УСПЕШНО, Отложенный ордер OID274709
  • Отмена отложенных ордеров с определенной меткой

    Этот сиБот отменяет только отложенные ордера с определенной меткой.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    [Robot()]
    public class Sample_cBot : Robot
    {
        protected override void OnBar()
        {
            foreach (var pendingOrder in PendingOrders)
            {
                if (pendingOrder.Label == "myLabel")
                    CancelPendingOrderAsync(pendingOrder);
            }
        }
    }
    
    1
    2
    3
    4
    5
    class Sample_cBot():
        def on_bar(self):
            for order in api.PendingOrders:
                if order.Label == "myLabel":
                    api.CancelPendingOrderAsync(order)
    

Функции обратного вызова для асинхронных методов

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

Эта функция будет вызвана, как только от сервера будет получен ответ. Например, она может быть вызвана при открытии, изменении или закрытии позиции.

  • Асинхронный рыночный ордер с обратным вызовом

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    [Robot()]
    public class Sample_cBot : Robot
    {
        protected override void OnStart()
        {
            var operation = ExecuteMarketOrderAsync(TradeType.Buy, SymbolName, 10000, PositionOpened);
            if (operation.IsExecuting)
                Print(operation.ToString());
            else
                Print(operation.TradeResult.ToString());
    
        }
    
        private void PositionOpened(TradeResult tradeResult)
        {
            var position = tradeResult.Position;
            Print(tradeResult.ToString());
            if (tradeResult.IsSuccessful)
                Print("Position {0} opened at {1}", position.Id, position.EntryPrice);
        }
    }
    
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    from System import Action
    
    class Sample_cBot():
        def on_start(self):
            operation = api.ExecuteMarketOrderAsync(TradeType.Buy, api.SymbolName, 10000, Action[TradeResult](self.on_position_opened))
    
            if (operation.IsExecuting)
                api.Print(operation.ToString())
            else
                api.Print(operation.TradeResult.ToString())
    
        def on_position_opened(self, tradeResult):
            position = tradeResult.Position
    
            api.Print(tradeResult.ToString())
    
            if tradeResult.IsSuccessful:
                api.Print(f"Position {position.Id} opened at {position.EntryPrice}")
    

    Вывод журнала

    • сиБот "New cBot" был успешно запущен для EURUSD, h1.
    • Выполнение рыночного ордера на покупку 10000 EURUSD
    • TradeOperation (Выполнение рыночного ордера на покупку 10000 EURUSD ВЫПОЛНЯЕТСЯ)
    • Выполнение рыночного ордера на покупку 10000 EURUSD ВЫПОЛНЕНО УСПЕШНО, Позиция PID14579835
    • TradeResult (Success, Position: PID14579835)
    • Позиция 14579835 открыта по цене 1.19414
  • Использование лямбда-выражений

    Вместо определения именованного метода обратного вызова вы можете использовать лямбда-выражения.

    В следующем примере при размещении ордера описание результата будет выведено в лог.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    [Robot()]
    public class Sample_cBot : Robot
    {
        protected override void OnStart()
        {
            PlaceLimitOrderAsync(TradeType.Buy, SymbolName, 10000,
                    Symbol.Ask - 20 * Symbol.PipSize, "myLabel", result => Print(result.ToString()));
        }
    }
    
    1
    2
    3
    4
    5
    from System import Action
    
    class Samples():
        def on_start(self):
            operation = api.PlaceLimitOrderAsync(TradeType.Buy, api.SymbolName, 10000, api.Symbol.Ask - 20 * api.Symbol.PipSize, "myLabel", Action[TradeResult](lambda result: api.Print(result.ToString())))
    

    Вывод в лог

    • сиБот "New cBot" был успешно запущен для EURUSD, h1.
    • Размещение лимитного ордера на покупку 10000 EURUSD (Цена: 1.19320)
    • Размещение лимитного ордера на покупку 10000 EURUSD (Цена: 1.19320) ВЫПОЛНЕНО УСПЕШНО, Отложенный ордер OID25222083
    • TradeResult (Success, PendingOrder: OID25222083)

Image title