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

Как создать торгового бота на Python

В этой статье и видео представлены пошаговые инструкции по созданию торгового робота на Python.

Примечание

cTrader — единственная крупная торговая платформа с нативной поддержкой Python, позволяющая писать код на Python и создавать ботов, технические индикаторы и инструменты непосредственно в cTrader Windows или Mac. Никаких адаптеров или сложных обходных путей не требуется.

Создайте торгового робота

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

Дайте сиБоту имя и выберите Python в качестве языка программирования.

Примечание

cTrader предоставляет несколько готовых алгоритмов на Python, охватывающих широкий спектр стратегий и автоматизированных торговых действий. Эти шаблоны алгоритмов уже содержат торговую логику и настраиваемые параметры и готовы к запуску после сохранения и сборки.

Выберите Использовать шаблон в качестве метода создания, выберите Grid Sample, затем нажмите Создать.

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

Давайте соберем нашего сиБота, чтобы проверить его код.

Нажмите Ctrl+B или нажмите Сборка.

Нажмите Cmd+B или нажмите значок Сборка.

Добавить индикатор

Давайте включим индикатор в нашего торгового бота, чтобы повысить его точность.

Relative Strength Index (RSI) — популярный индикатор, который сигнализирует о перекупленности или перепроданности рынка. Этот индикатор может помочь нашему сиБоту избежать открытия новых позиций, когда рынок растянут в одном направлении.

Интегрируйте RSI в логику робота. Инициализируйте индикатор с периодом по умолчанию 14 и установите уровни перекупленности и перепроданности на 70 и 30 соответственно. Используйте цены закрытия инструмента для создания RSI.

1
2
3
4
self.rsi_period = getattr(api, "RsiPeriod", 14)
self.rsi_overbought = getattr(api, "RsiOverbought", 70)
self.rsi_oversold = getattr(api, "RsiOversold", 30)
self.rsi = api.Indicators.RelativeStrengthIndex(api.Bars.ClosePrices, self.rsi_period)

Добавьте логику для проверки того, позволяют ли условия RSI совершить сделку перед открытием позиции. Если фильтр блокирует сделку, запишите сообщение, показывающее текущее значение RSI.

1
2
3
4
5
6
if len(self.get_grid_positions()) == 0:
    # self.open_position()
    if self.passes_rsi_filter():
        self.open_position()
    else:
        api.Print("RSI filter blocked initial entry (RSI={:.2f})".format(self.get_rsi_value()))

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
if distance >= api.StepPips:
    # self.open_position()
    if self.passes_rsi_filter():
        self.open_position()
    else:
        api.Print("RSI filter: no new entry (RSI={:.2f}, range {}-{})"
                    .format(self.get_rsi_value(), self.rsi_oversold, self.rsi_overbought))

def get_rsi_value(self):
    # Use the last CLOSED bar for stability
    return self.rsi.Result.Last(1)

def passes_rsi_filter(self):
    r = self.get_rsi_value()
    return (r > self.rsi_oversold) and (r < self.rsi_overbought)

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

1
2
3
4
5
6
7
def get_distance_in_pips(self, position):
    # return (position.EntryPrice - api.Symbol.Ask if position.TradeType == TradeType.Buy else api.Symbol.Bid - position.EntryPrice) / api.Symbol.PipSize
    if position.TradeType == TradeType.Buy:
        diff = position.EntryPrice - api.Symbol.Ask
    else:
        diff = api.Symbol.Bid - position.EntryPrice
    return diff / api.Symbol.PipSize

Вы можете скопировать полный код Python с интегрированным индикатором RSI ниже:

 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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import clr

clr.AddReference("cAlgo.API")

# Import cAlgo API types
from cAlgo.API import *

# Import trading wrapper functions
from robot_wrapper import *

class SupercBot():
    def on_start(self):
        self.volumeInUnits = api.Symbol.QuantityToVolumeInUnits(api.VolumeInLots)
        self.enoughMoney = True

        self.rsi_period = getattr(api, "RsiPeriod", 14)
        self.rsi_overbought = getattr(api, "RsiOverbought", 70)
        self.rsi_oversold = getattr(api, "RsiOversold", 30)
        self.rsi = api.Indicators.RelativeStrengthIndex(api.Bars.ClosePrices, self.rsi_period)

        if len(self.get_grid_positions()) == 0:
            if self.passes_rsi_filter():
                self.open_position()
            else:
                api.Print("RSI filter blocked initial entry (RSI={:.2f})".format(self.get_rsi_value()))

    def on_tick(self):
        grid_positions = self.get_grid_positions()
        net_profit_sum = sum([p.NetProfit for p in grid_positions])
        if net_profit_sum >= api.TargetProfit:
            api.Print("Target profit is reached. Closing all grid positions")
            self.close_grid_positions()
            api.Print("All grid positions are closed. Stopping cBot")
            api.Stop()

        if len(grid_positions) > 0 and self.enoughMoney == True:
            position_with_highest_pips = sorted(grid_positions, key=lambda pos: pos.Pips, reverse=True)[0]
            distance = self.get_distance_in_pips(position_with_highest_pips)          
            if distance >= api.StepPips:
                if self.passes_rsi_filter():
                    self.open_position()
                else:
                    api.Print("RSI filter: no new entry (RSI={:.2f}, range {}-{})"
                              .format(self.get_rsi_value(), self.rsi_oversold, self.rsi_overbought))

    def get_rsi_value(self):
        # Use the last CLOSED bar for stability
        return self.rsi.Result.Last(1)

    def passes_rsi_filter(self):
        r = self.get_rsi_value()
        return (r > self.rsi_oversold) and (r < self.rsi_overbought)

    def get_grid_positions(self):
        return [pos for pos in api.Positions if pos.SymbolName == api.SymbolName and pos.TradeType == api.TradeType]

    def open_position(self):
        result = api.ExecuteMarketOrder(api.TradeType, api.SymbolName, self.volumeInUnits, "Grid")
        if result.Error == ErrorCode.NoMoney:
            self.enoughMoney = False
            api.Print("Not enough money to open additional positions")

    def close_grid_positions(self):
        for position in self.get_grid_positions():
            position.Close()

        if len(self.get_grid_positions()) > 0:
            self.close_grid_positions()

    def get_distance_in_pips(self, position):
        if position.TradeType == TradeType.Buy:
            diff = position.EntryPrice - api.Symbol.Ask
        else:
            diff = api.Symbol.Bid - position.EntryPrice
        return diff / api.Symbol.PipSize

Запуск экземпляра

Давайте вернемся к нашему сиБоту и создадим его. Теперь нажмите значок Запустить сиБота для любого экземпляра, и торговый бот начнет работу.

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

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

Запустите бота в другом месте

При включенной синхронизации с облаком сиБот автоматически появляется во всех приложениях cTrader, где активен ваш cTID. Мы можем запустить того же торгового робота в облаке в любом приложении cTrader, включая cTrader Mobile и Web.

В cTrader Algo запустите сиБота.

В этой статье показано, как создать торгового робота на Python и запустить его, что позволяет вам автоматизировать свои стратегии и запускать алгоритмы на любом устройстве.

Image title