How to make a trading robot for Binance


A few words about me

Programming for me is a hobby and a favorite thing. And so I am a certified system architect. Therefore, I ask you not to scold too much for the code πŸ™‚

Currently, I am fond of writing trading robots. Gradually I study neural networks for their application to the analysis of prices / volumes of stocks / futures.

Usually I wrote trading robots to work with Brokers and did auto-trading in Stocks or Futures, but suddenly an idea arose.

- А Ρ‡Ρ‚ΠΎ, Ссли ΡƒΠΆΠ΅ Π³ΠΎΡ‚ΠΎΠ²Ρ‹ΠΉ ΠΊΠΎΠ΄ ΠΌΠΎΠΆΠ½ΠΎ ΠΏΡ€ΠΈΠΌΠ΅Π½ΡΡ‚ΡŒ ΠΈ Π½Π° Π΄Ρ€ΡƒΠ³ΠΈΡ… Π°ΠΊΡ‚ΠΈΠ²Π°Ρ…??? НапримСр Π½Π° ΠΊΡ€ΠΈΠΏΡ‚ΠΎ Π°ΠΊΡ‚ΠΈΠ²Π°Ρ… для Π‘ΠΈΡ‚ΠΊΠΎΠΈΠ½Π° ΠΈΠ»ΠΈ Π­Ρ„ΠΈΡ€Π° ΠΈΠ»ΠΈ Π΄Ρ€ΡƒΠ³ΠΈΡ…?

Having already studied many libraries and examples for a long time writing my trading robots, I decided to make a small library backtrader_binance to integrate the Binance API and the trading strategy testing library back trader.

Here with the help backtrader_binancenow and create an algorithmic robot for trading BTC and ETH.

Preparing the Environment

  1. Installing the latest version Python 3.11

  2. Installing the development environment PyCharm Community 2023.1

  3. Launching PyCharm Community

  4. We create a new project in it, let’s call it algo_trade_robot and indicate that we create a virtual environment Virtualenvwith Python 3.11 => click “Create”.

    Creating a new project for algo trading

    Creating a new project for algo trading

  5. After the project was created and a virtual environment was created in it, we were ready to install the necessary libraries))) Click on “Terminal” at the bottom left to open the terminal, in which we will enter the commands to install the libraries.

    Project open terminal

    Project open terminal

  6. Installing the required libraries

    To install the library that integrates the Binance API with back trader enter the command

    pip install backtrader_binance

    entering the command to install backtrader_binance in the terminal

    enter the installation command backtrader_binance in the terminal

    Now you need to install the trading strategy testing library back trader

    pip install git+https://github.com/WISEPLAT/backtrader.git

    PS Please use back trader from my repository (since you can put your commits there).

    And finally we have some dependencies that you need to install as well

    pip install python-binance pandas matplotlib

  7. Now you need to make a copy of the entire repository to the root of the project in order to take examples of the code of trading strategies from it, this is done with one command, also through the terminal.

    git clone https://github.com/WISEPLAT/backtrader_binance

    And now our project looks like this

    Trading robot project for Binance

    Trading robot project for Binance

Creating a configuration for a trading strategy

To make it easier to understand how everything works, I have made for you a lot of examples in folders DataExamplesBinance_en And StrategyExamplesBinance_en.

Before running the example, you need to get your API key And secret keyand write them in a file ConfigBinance\Config.py:

# content of ConfigBinance\Config.py 
class Config:
    BINANCE_API_KEY = "YOUR_API_KEY"
    BINANCE_API_SECRET = "YOUR_SECRET_KEY"

How to get a Binance API token

  1. Register your account at Binance

  2. Go to section “API Management”

  3. Then click the “Generate API” button and select “System Generated”.

  4. In the API Limits section, enable “Enable spot and margin trading”.

  5. Copy and paste to file ConfigBinance\Config.py received “API key” And “The secret key”

Now you can run examples from folders DataExamplesBinance_en And StrategyExamplesBinance_en.

Creating a trading robot for Binance

To create a trading robot, they usually follow a certain code structure, you can say a template according to which the code works with a trading strategy and with data from the market by ticker / tickers, and after working out, some result is displayed.

ΠΈΠΌΠΏΠΎΡ€Ρ‚ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΡ‹Ρ…_Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊ

класс Π˜Π½Π΄ΠΈΠΊΠ°Ρ‚ΠΎΡ€ΠΎΠ²

класс Π‘Ρ‚Ρ€Π°Ρ‚Π΅Π³ΠΈΠΈ/Π’ΠΎΡ€Π³ΠΎΠ²ΠΎΠΉ систСмы

# --- основной Ρ€Π°Π·Π΄Π΅Π» ---
ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ ΠΏΠΎ API ΠΊ Π±ΠΈΡ€ΠΆΠ΅
Π·Π°Π΄Π°Π½ΠΈΠ΅ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ² запуска стратСгии
запуск стратСгии
  ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΠ΅ Π΄Π°Π½Π½Ρ‹Ρ… ΠΏΠΎ Ρ‚ΠΈΠΊΠ΅Ρ€Ρƒ/Ρ‚ΠΈΠΊΠ΅Ρ€Π°ΠΌ ΠΏΠΎ API
  ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° этих Π΄Π°Π½Π½Ρ‹Ρ… стратСгиСй
  выставлСниС заявок Π½Π° ΠΏΠΎΠΊΡƒΠΏΠΊΡƒ/ΠΏΡ€ΠΎΠ΄Π°ΠΆΡƒ
Π²ΠΎΠ·Π²Ρ€Π°Ρ‚ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ΠΎΠ² ΠΈΠ· стратСгии
Π²Ρ‹Π²ΠΎΠ΄ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ΠΎΠ²

In the examples you will find several options for launching strategies, but here is an approximately standard code structure for a trading robot, the file “07 – Offline Backtest Indicators.py”:

import datetime as dt
import backtrader as bt
from backtrader_binance import BinanceStore
from ConfigBinance.Config import Config  # Π€Π°ΠΉΠ» ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ


# Π²ΠΈΠ΄Π΅ΠΎ ΠΏΠΎ созданию этой стратСгии
# RuTube: https://rutube.ru/video/417e306e6b5d6351d74bd9cd4d6af051/
# YouTube: https://youtube.com/live/k82vabGva7s

class UnderOver(bt.Indicator):
    lines = ('underover',)
    params = dict(data2=20)
    plotinfo = dict(plot=True)

    def __init__(self):
        self.l.underover = self.data < self.p.data2             # Π΄Π°Π½Π½Ρ‹Π΅ ΠΏΠΎΠ΄ data2 == 1


# Ворговая систСма
class RSIStrategy(bt.Strategy):
    """
    ДСмонстрация live стратСгии с ΠΈΠ½Π΄ΠΈΠΊΠ°Ρ‚ΠΎΡ€Π°ΠΌΠΈ SMA, RSI
    """
    params = (  # ΠŸΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ Ρ‚ΠΎΡ€Π³ΠΎΠ²ΠΎΠΉ систСмы
        ('coin_target', ''),
        ('timeframe', ''),
    )

    def __init__(self):
        """Π˜Π½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΡ, Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΈΠ½Π΄ΠΈΠΊΠ°Ρ‚ΠΎΡ€ΠΎΠ² для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ Ρ‚ΠΈΠΊΠ΅Ρ€Π°"""
        self.orders = {}  # ΠžΡ€Π³Π°Π½ΠΈΠ·ΠΎΠ²Ρ‹Π²Π°Π΅ΠΌ заявки Π² Π²ΠΈΠ΄Π΅ справочника, ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½ΠΎ для этой стратСгии ΠΎΠ΄ΠΈΠ½ Ρ‚ΠΈΠΊΠ΅Ρ€ - ΠΎΠ΄Π½Π° активная заявка
        for d in self.datas:  # ΠŸΡ€ΠΎΠ±Π΅Π³Π°Π΅ΠΌΡΡ ΠΏΠΎ всСм Ρ‚ΠΈΠΊΠ΅Ρ€Π°ΠΌ
            self.orders[d._name] = None  # Заявки ΠΏΠΎ Ρ‚ΠΈΠΊΠ΅Ρ€Ρƒ ΠΏΠΎΠΊΠ° Π½Π΅Ρ‚

        # создаСм ΠΈΠ½Π΄ΠΈΠΊΠ°Ρ‚ΠΎΡ€Ρ‹ для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ Ρ‚ΠΈΠΊΠ΅Ρ€Π°
        self.sma1 = {}
        self.sma2 = {}
        self.sma3 = {}
        self.crossover = {}
        self.underover_sma = {}
        self.rsi = {}
        self.underover_rsi = {}
        for i in range(len(self.datas)):
            ticker = list(self.dnames.keys())[i]    # key name is ticker name
            self.sma1[ticker] = bt.indicators.SMA(self.datas[i], period=9)  # SMA1 indicator
            self.sma2[ticker] = bt.indicators.SMA(self.datas[i], period=30)  # SMA2 indicator
            self.sma3[ticker] = bt.indicators.SMA(self.datas[i], period=60)  # SMA3 indicator

            # signal 1 - пСрСсСчСниС быстрой SMA снизу Π²Π²Π΅Ρ€Ρ… ΠΌΠ΅Π΄Π»Π΅Π½Π½ΠΎΠΉ SMA
            self.crossover[ticker] = bt.ind.CrossOver(self.sma1[ticker], self.sma2[ticker])  # crossover SMA1 and SMA2

            # signal 2 - ΠΊΠΎΠ³Π΄Π° SMA3 находится Π½ΠΈΠΆΠ΅ SMA2
            self.underover_sma[ticker] = UnderOver(self.sma3[ticker].lines.sma, data2=self.sma2[ticker].lines.sma)

            self.rsi[ticker] = bt.indicators.RSI(self.datas[i], period=20)  # RSI indicator

            # signal 3 - ΠΊΠΎΠ³Π΄Π° RSI находится Π½ΠΈΠΆΠ΅ 30
            self.underover_rsi[ticker] = UnderOver(self.rsi[ticker].lines.rsi, data2=30)

    def next(self):
        """ΠŸΡ€ΠΈΡ…ΠΎΠ΄ Π½ΠΎΠ²ΠΎΠ³ΠΎ Π±Π°Ρ€Π° Ρ‚ΠΈΠΊΠ΅Ρ€Π°"""
        for data in self.datas:  # ΠŸΡ€ΠΎΠ±Π΅Π³Π°Π΅ΠΌΡΡ ΠΏΠΎ всСм Π·Π°ΠΏΡ€ΠΎΡˆΠ΅Π½Π½Ρ‹ΠΌ Π±Π°Ρ€Π°ΠΌ всСх Ρ‚ΠΈΠΊΠ΅Ρ€ΠΎΠ²
            ticker = data._name
            status = data._state  # 0 - Live data, 1 - History data, 2 - None
            _interval = self.p.timeframe

            if status in [0, 1]:
                if status: _state = "False - History data"
                else: _state = "True - Live data"

                print('{} / {} [{}] - Open: {}, High: {}, Low: {}, Close: {}, Volume: {} - Live: {}'.format(
                    bt.num2date(data.datetime[0]),
                    data._name,
                    _interval,  # Ρ‚Π°ΠΉΠΌΡ„Ρ€Π΅ΠΉΠΌ Ρ‚ΠΈΠΊΠ΅Ρ€Π°
                    data.open[0],
                    data.high[0],
                    data.low[0],
                    data.close[0],
                    data.volume[0],
                    _state,
                ))
                print(f'\t - RSI =', self.rsi[ticker][0])
                print(f"\t - crossover =", self.crossover[ticker].lines.crossover[0])

                coin_target = self.p.coin_target
                print(f"\t - Free balance: {self.broker.getcash()} {coin_target}")

                # сигналы Π½Π° Π²Ρ…ΠΎΠ΄
                signal1 = self.crossover[ticker].lines.crossover[0]  # signal 1 - пСрСсСчСниС быстрой SMA снизу Π²Π²Π΅Ρ€Ρ… ΠΌΠ΅Π΄Π»Π΅Π½Π½ΠΎΠΉ SMA
                signal2 = self.underover_sma[ticker]  # signal 2 - ΠΊΠΎΠ³Π΄Π° SMA3 находится Π½ΠΈΠΆΠ΅ SMA2

                # сигналы Π½Π° Π²Ρ‹Ρ…ΠΎΠ΄
                signal3 = self.underover_rsi[ticker]  # signal 3 - ΠΊΠΎΠ³Π΄Π° RSI находится Π½ΠΈΠΆΠ΅ 30

                if not self.getposition(data):  # Если ΠΏΠΎΠ·ΠΈΡ†ΠΈΠΈ Π½Π΅Ρ‚
                    if signal1 == 1:
                        if signal2 == 1:
                            # buy
                            free_money = self.broker.getcash()
                            price = data.close[0]  # ΠΏΠΎ Ρ†Π΅Π½Π΅ закрытия
                            size = (free_money / price) * 0.25  # 25% ΠΎΡ‚ доступных срСдств
                            print("-"*50)
                            print(f"\t - buy {ticker} size = {size} at price = {price}")
                            self.orders[data._name] = self.buy(data=data, exectype=bt.Order.Limit, price=price, size=size)
                            print(f"\t - ВыставлСна заявка {self.orders[data._name].p.tradeid} Π½Π° ΠΏΠΎΠΊΡƒΠΏΠΊΡƒ {data._name}")
                            print("-" * 50)

                else:  # Если позиция Π΅ΡΡ‚ΡŒ
                    if signal3 == 1:
                        # sell
                        print("-" * 50)
                        print(f"\t - ΠŸΡ€ΠΎΠ΄Π°Π΅ΠΌ ΠΏΠΎ Ρ€Ρ‹Π½ΠΊΡƒ {data._name}...")
                        self.orders[data._name] = self.close()  # Заявка Π½Π° Π·Π°ΠΊΡ€Ρ‹Ρ‚ΠΈΠ΅ ΠΏΠΎΠ·ΠΈΡ†ΠΈΠΈ ΠΏΠΎ Ρ€Ρ‹Π½ΠΎΡ‡Π½ΠΎΠΉ Ρ†Π΅Π½Π΅
                        print("-" * 50)

    def notify_order(self, order):
        """ИзмСнСниС статуса заявки"""
        order_data_name = order.data._name  # Имя Ρ‚ΠΈΠΊΠ΅Ρ€Π° ΠΈΠ· заявки
        print("*"*50)
        self.log(f'Заявка Π½ΠΎΠΌΠ΅Ρ€ {order.ref} {order.info["order_number"]} {order.getstatusname()} {"ΠŸΠΎΠΊΡƒΠΏΠΊΠ°" if order.isbuy() else "ΠŸΡ€ΠΎΠ΄Π°ΠΆΠ°"} {order_data_name} {order.size} @ {order.price}')
        if order.status == bt.Order.Completed:  # Если заявка ΠΏΠΎΠ»Π½ΠΎΡΡ‚ΡŒΡŽ исполнСна
            if order.isbuy():  # Заявка Π½Π° ΠΏΠΎΠΊΡƒΠΏΠΊΡƒ
                self.log(f'ΠŸΠΎΠΊΡƒΠΏΠΊΠ° {order_data_name} Π¦Π΅Π½Π°: {order.executed.price:.2f}, ΠžΠ±ΡŠΡ‘ΠΌ: {order.executed.value:.2f}, Комиссия: {order.executed.comm:.2f}')
            else:  # Заявка Π½Π° ΠΏΡ€ΠΎΠ΄Π°ΠΆΡƒ
                self.log(f'ΠŸΡ€ΠΎΠ΄Π°ΠΆΠ° {order_data_name} Π¦Π΅Π½Π°: {order.executed.price:.2f}, ΠžΠ±ΡŠΡ‘ΠΌ: {order.executed.value:.2f}, Комиссия: {order.executed.comm:.2f}')
                self.orders[order_data_name] = None  # БбрасываСм заявку Π½Π° Π²Ρ…ΠΎΠ΄ Π² ΠΏΠΎΠ·ΠΈΡ†ΠΈΡŽ
        print("*" * 50)

    def notify_trade(self, trade):
        """ИзмСнСниС статуса ΠΏΠΎΠ·ΠΈΡ†ΠΈΠΈ"""
        if trade.isclosed:  # Если позиция Π·Π°ΠΊΡ€Ρ‹Ρ‚Π°
            self.log(f'ΠŸΡ€ΠΈΠ±Ρ‹Π»ΡŒ ΠΏΠΎ Π·Π°ΠΊΡ€Ρ‹Ρ‚ΠΎΠΉ ΠΏΠΎΠ·ΠΈΡ†ΠΈΠΈ {trade.getdataname()} ΠžΠ±Ρ‰Π°Ρ={trade.pnl:.2f}, Π‘Π΅Π· комиссии={trade.pnlcomm:.2f}')

    def log(self, txt, dt=None):
        """Π’Ρ‹Π²ΠΎΠ΄ строки с Π΄Π°Ρ‚ΠΎΠΉ Π½Π° консоль"""
        dt = bt.num2date(self.datas[0].datetime[0]) if not dt else dt  # Заданная Π΄Π°Ρ‚Π° ΠΈΠ»ΠΈ Π΄Π°Ρ‚Π° Ρ‚Π΅ΠΊΡƒΡ‰Π΅Π³ΠΎ Π±Π°Ρ€Π°
        print(f'{dt.strftime("%d.%m.%Y %H:%M")}, {txt}')  # Π’Ρ‹Π²ΠΎΠ΄ΠΈΠΌ Π΄Π°Ρ‚Ρƒ ΠΈ врСмя с Π·Π°Π΄Π°Π½Π½Ρ‹ΠΌ тСкстом Π½Π° консоль


if __name__ == '__main__':
    cerebro = bt.Cerebro(quicknotify=True)

    cerebro.broker.setcash(2000)  # УстанавливаСм сколько Π΄Π΅Π½Π΅Π³
    cerebro.broker.setcommission(commission=0.0015)  # Π£ΡΡ‚Π°Π½ΠΎΠ²ΠΈΡ‚ΡŒ комиссию- 0.15% ... Ρ€Π°Π·Π΄Π΅Π»ΠΈΡ‚Π΅ Π½Π° 100, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡƒΠ΄Π°Π»ΠΈΡ‚ΡŒ %

    coin_target="USDT"  # Π±Π°Π·ΠΎΠ²Ρ‹ΠΉ Ρ‚ΠΈΠΊΠ΅Ρ€, Π² ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ Π±ΡƒΠ΄ΡƒΡ‚ ΠΎΡΡƒΡ‰Π΅ΡΡ‚Π²Π»ΡΡ‚ΡŒΡΡ расчСты
    symbol="BTC" + coin_target  # Ρ‚ΠΈΠΊΠ΅Ρ€, ΠΏΠΎ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌΡƒ Π±ΡƒΠ΄Π΅ΠΌ ΠΏΠΎΠ»ΡƒΡ‡Π°Ρ‚ΡŒ Π΄Π°Π½Π½Ρ‹Π΅ Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ <ΠšΠΎΠ΄Π’ΠΈΠΊΠ΅Ρ€Π°Π‘Π°Π·ΠΎΠ²Ρ‹ΠΉΠ’ΠΈΠΊΠ΅Ρ€>
    symbol2 = 'ETH' + coin_target  # Ρ‚ΠΈΠΊΠ΅Ρ€, ΠΏΠΎ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌΡƒ Π±ΡƒΠ΄Π΅ΠΌ ΠΏΠΎΠ»ΡƒΡ‡Π°Ρ‚ΡŒ Π΄Π°Π½Π½Ρ‹Π΅ Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ <ΠšΠΎΠ΄Π’ΠΈΠΊΠ΅Ρ€Π°Π‘Π°Π·ΠΎΠ²Ρ‹ΠΉΠ’ΠΈΠΊΠ΅Ρ€>

    store = BinanceStore(
        api_key=Config.BINANCE_API_KEY,
        api_secret=Config.BINANCE_API_SECRET,
        coin_target=coin_target,
        testnet=False)  # Π₯Ρ€Π°Π½ΠΈΠ»ΠΈΡ‰Π΅ Binance

    # # live ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ ΠΊ Binance - для Offline Π·Π°ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ эти Π΄Π²Π΅ строки
    # broker = store.getbroker()
    # cerebro.setbroker(broker)

    # -----------------------------------------------------------
    # Π’Π½ΠΈΠΌΠ°Π½ΠΈΠ΅! - Π’Π΅ΠΏΠ΅Ρ€ΡŒ это Offline для тСстирования стратСгий #
    # -----------------------------------------------------------

    # # Π˜ΡΡ‚ΠΎΡ€ΠΈΡ‡Π΅ΡΠΊΠΈΠ΅ 1-ΠΌΠΈΠ½ΡƒΡ‚Π½Ρ‹Π΅ Π±Π°Ρ€Ρ‹ Π·Π° 10 часов + Π½ΠΎΠ²Ρ‹Π΅ live Π±Π°Ρ€Ρ‹ / Ρ‚Π°ΠΉΠΌΡ„Ρ€Π΅ΠΉΠΌ M1
    # timeframe = "M1"
    # from_date = dt.datetime.utcnow() - dt.timedelta(minutes=60*10)
    # data = store.getdata(timeframe=bt.TimeFrame.Minutes, compression=1, dataname=symbol, start_date=from_date, LiveBars=False)  # ΠΏΠΎΡΡ‚Π°Π²ΡŒΡ‚Π΅ здСсь True - Ссли Π½ΡƒΠΆΠ½ΠΎ ΠΏΠΎΠ»ΡƒΡ‡Π°Ρ‚ΡŒ live Π±Π°Ρ€Ρ‹
    # # data2 = store.getdata(timeframe=bt.TimeFrame.Minutes, compression=1, dataname=symbol2, start_date=from_date, LiveBars=False)  # ΠΏΠΎΡΡ‚Π°Π²ΡŒΡ‚Π΅ здСсь True - Ссли Π½ΡƒΠΆΠ½ΠΎ ΠΏΠΎΠ»ΡƒΡ‡Π°Ρ‚ΡŒ live Π±Π°Ρ€Ρ‹

    # Π˜ΡΡ‚ΠΎΡ€ΠΈΡ‡Π΅ΡΠΊΠΈΠ΅ D1 Π±Π°Ρ€Ρ‹ Π·Π° 365 Π΄Π½Π΅ΠΉ + Π½ΠΎΠ²Ρ‹Π΅ live Π±Π°Ρ€Ρ‹ / Ρ‚Π°ΠΉΠΌΡ„Ρ€Π΅ΠΉΠΌ D1
    timeframe = "D1"
    from_date = dt.datetime.utcnow() - dt.timedelta(days=365*3)
    data = store.getdata(timeframe=bt.TimeFrame.Days, compression=1, dataname=symbol, start_date=from_date, LiveBars=False)  # ΠΏΠΎΡΡ‚Π°Π²ΡŒΡ‚Π΅ здСсь True - Ссли Π½ΡƒΠΆΠ½ΠΎ ΠΏΠΎΠ»ΡƒΡ‡Π°Ρ‚ΡŒ live Π±Π°Ρ€Ρ‹
    data2 = store.getdata(timeframe=bt.TimeFrame.Days, compression=1, dataname=symbol2, start_date=from_date, LiveBars=False)  # ΠΏΠΎΡΡ‚Π°Π²ΡŒΡ‚Π΅ здСсь True - Ссли Π½ΡƒΠΆΠ½ΠΎ ΠΏΠΎΠ»ΡƒΡ‡Π°Ρ‚ΡŒ live Π±Π°Ρ€Ρ‹

    cerebro.adddata(data)  # ДобавляСм Π΄Π°Π½Π½Ρ‹Π΅
    cerebro.adddata(data2)  # ДобавляСм Π΄Π°Π½Π½Ρ‹Π΅

    cerebro.addstrategy(RSIStrategy, coin_target=coin_target, timeframe=timeframe)  # ДобавляСм Ρ‚ΠΎΡ€Π³ΠΎΠ²ΡƒΡŽ систСму

    cerebro.run()  # Запуск Ρ‚ΠΎΡ€Π³ΠΎΠ²ΠΎΠΉ систСмы
    cerebro.plot()  # РисуСм Π³Ρ€Π°Ρ„ΠΈΠΊ

    print()
    print("$"*77)
    print(f"Ликвидационная ΡΡ‚ΠΎΠΈΠΌΠΎΡΡ‚ΡŒ портфСля: {cerebro.broker.getvalue()}")  # Ликвидационная ΡΡ‚ΠΎΠΈΠΌΠΎΡΡ‚ΡŒ портфСля
    print(f"ΠžΡΡ‚Π°Ρ‚ΠΎΠΊ свободных срСдств: {cerebro.broker.getcash()}")  # ΠžΡΡ‚Π°Ρ‚ΠΎΠΊ свободных срСдств
    print("$" * 77)

Looking at the code above, you can easily see that

  1. import of necessary libraries is carried out by lines 1..4

import datetime as dt
import backtrader as bt
from backtrader_binance import BinanceStore
from ConfigBinance.Config import Config  # Π€Π°ΠΉΠ» ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ
  1. indicator class 11..17 lines, usually placed in a separate file

class UnderOver(bt.Indicator):
    lines = ('underover',)
    params = dict(data2=20)
    plotinfo = dict(plot=True)

    def __init__(self):
        self.l.underover = self.data < self.p.data2             # Π΄Π°Π½Π½Ρ‹Π΅ ΠΏΠΎΠ΄ data2 == 1
  1. Strategy/Trading system class 21..138, usually placed in a separate file

# Ворговая систСма
class RSIStrategy(bt.Strategy):
    """
    ДСмонстрация live стратСгии с ΠΈΠ½Π΄ΠΈΠΊΠ°Ρ‚ΠΎΡ€Π°ΠΌΠΈ SMA, RSI
    """
    params = (  # ΠŸΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ Ρ‚ΠΎΡ€Π³ΠΎΠ²ΠΎΠΉ систСмы
        ('coin_target', ''),
        ('timeframe', ''),
    )

    def __init__(self):
        """Π˜Π½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΡ, Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΈΠ½Π΄ΠΈΠΊΠ°Ρ‚ΠΎΡ€ΠΎΠ² для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ Ρ‚ΠΈΠΊΠ΅Ρ€Π°"""
        self.orders = {}  # ΠžΡ€Π³Π°Π½ΠΈΠ·ΠΎΠ²Ρ‹Π²Π°Π΅ΠΌ заявки Π² Π²ΠΈΠ΄Π΅ справочника, ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½ΠΎ для этой стратСгии ΠΎΠ΄ΠΈΠ½ Ρ‚ΠΈΠΊΠ΅Ρ€ - ΠΎΠ΄Π½Π° активная заявка
        for d in self.datas:  # ΠŸΡ€ΠΎΠ±Π΅Π³Π°Π΅ΠΌΡΡ ΠΏΠΎ всСм Ρ‚ΠΈΠΊΠ΅Ρ€Π°ΠΌ
            self.orders[d._name] = None  # Заявки ΠΏΠΎ Ρ‚ΠΈΠΊΠ΅Ρ€Ρƒ ΠΏΠΎΠΊΠ° Π½Π΅Ρ‚

        # создаСм ΠΈΠ½Π΄ΠΈΠΊΠ°Ρ‚ΠΎΡ€Ρ‹ для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ Ρ‚ΠΈΠΊΠ΅Ρ€Π°
        self.sma1 = {}
        self.sma2 = {}
        self.sma3 = {}
        self.crossover = {}
        self.underover_sma = {}
        self.rsi = {}
        self.underover_rsi = {}
        for i in range(len(self.datas)):
            ticker = list(self.dnames.keys())[i]    # key name is ticker name
            self.sma1[ticker] = bt.indicators.SMA(self.datas[i], period=9)  # SMA1 indicator
            self.sma2[ticker] = bt.indicators.SMA(self.datas[i], period=30)  # SMA2 indicator
            self.sma3[ticker] = bt.indicators.SMA(self.datas[i], period=60)  # SMA3 indicator

            # signal 1 - пСрСсСчСниС быстрой SMA снизу Π²Π²Π΅Ρ€Ρ… ΠΌΠ΅Π΄Π»Π΅Π½Π½ΠΎΠΉ SMA
            self.crossover[ticker] = bt.ind.CrossOver(self.sma1[ticker], self.sma2[ticker])  # crossover SMA1 and SMA2

            # signal 2 - ΠΊΠΎΠ³Π΄Π° SMA3 находится Π½ΠΈΠΆΠ΅ SMA2
            self.underover_sma[ticker] = UnderOver(self.sma3[ticker].lines.sma, data2=self.sma2[ticker].lines.sma)

            self.rsi[ticker] = bt.indicators.RSI(self.datas[i], period=20)  # RSI indicator

            # signal 3 - ΠΊΠΎΠ³Π΄Π° RSI находится Π½ΠΈΠΆΠ΅ 30
            self.underover_rsi[ticker] = UnderOver(self.rsi[ticker].lines.rsi, data2=30)

    def next(self):
        """ΠŸΡ€ΠΈΡ…ΠΎΠ΄ Π½ΠΎΠ²ΠΎΠ³ΠΎ Π±Π°Ρ€Π° Ρ‚ΠΈΠΊΠ΅Ρ€Π°"""
        for data in self.datas:  # ΠŸΡ€ΠΎΠ±Π΅Π³Π°Π΅ΠΌΡΡ ΠΏΠΎ всСм Π·Π°ΠΏΡ€ΠΎΡˆΠ΅Π½Π½Ρ‹ΠΌ Π±Π°Ρ€Π°ΠΌ всСх Ρ‚ΠΈΠΊΠ΅Ρ€ΠΎΠ²
            ticker = data._name
            status = data._state  # 0 - Live data, 1 - History data, 2 - None
            _interval = self.p.timeframe

            if status in [0, 1]:
                if status: _state = "False - History data"
                else: _state = "True - Live data"

                print('{} / {} [{}] - Open: {}, High: {}, Low: {}, Close: {}, Volume: {} - Live: {}'.format(
                    bt.num2date(data.datetime[0]),
                    data._name,
                    _interval,  # Ρ‚Π°ΠΉΠΌΡ„Ρ€Π΅ΠΉΠΌ Ρ‚ΠΈΠΊΠ΅Ρ€Π°
                    data.open[0],
                    data.high[0],
                    data.low[0],
                    data.close[0],
                    data.volume[0],
                    _state,
                ))
                print(f'\t - RSI =', self.rsi[ticker][0])
                print(f"\t - crossover =", self.crossover[ticker].lines.crossover[0])

                coin_target = self.p.coin_target
                print(f"\t - Free balance: {self.broker.getcash()} {coin_target}")

                # сигналы Π½Π° Π²Ρ…ΠΎΠ΄
                signal1 = self.crossover[ticker].lines.crossover[0]  # signal 1 - пСрСсСчСниС быстрой SMA снизу Π²Π²Π΅Ρ€Ρ… ΠΌΠ΅Π΄Π»Π΅Π½Π½ΠΎΠΉ SMA
                signal2 = self.underover_sma[ticker]  # signal 2 - ΠΊΠΎΠ³Π΄Π° SMA3 находится Π½ΠΈΠΆΠ΅ SMA2

                # сигналы Π½Π° Π²Ρ‹Ρ…ΠΎΠ΄
                signal3 = self.underover_rsi[ticker]  # signal 3 - ΠΊΠΎΠ³Π΄Π° RSI находится Π½ΠΈΠΆΠ΅ 30

                if not self.getposition(data):  # Если ΠΏΠΎΠ·ΠΈΡ†ΠΈΠΈ Π½Π΅Ρ‚
                    if signal1 == 1:
                        if signal2 == 1:
                            # buy
                            free_money = self.broker.getcash()
                            price = data.close[0]  # ΠΏΠΎ Ρ†Π΅Π½Π΅ закрытия
                            size = (free_money / price) * 0.25  # 25% ΠΎΡ‚ доступных срСдств
                            print("-"*50)
                            print(f"\t - buy {ticker} size = {size} at price = {price}")
                            self.orders[data._name] = self.buy(data=data, exectype=bt.Order.Limit, price=price, size=size)
                            print(f"\t - ВыставлСна заявка {self.orders[data._name].p.tradeid} Π½Π° ΠΏΠΎΠΊΡƒΠΏΠΊΡƒ {data._name}")
                            print("-" * 50)

                else:  # Если позиция Π΅ΡΡ‚ΡŒ
                    if signal3 == 1:
                        # sell
                        print("-" * 50)
                        print(f"\t - ΠŸΡ€ΠΎΠ΄Π°Π΅ΠΌ ΠΏΠΎ Ρ€Ρ‹Π½ΠΊΡƒ {data._name}...")
                        self.orders[data._name] = self.close()  # Заявка Π½Π° Π·Π°ΠΊΡ€Ρ‹Ρ‚ΠΈΠ΅ ΠΏΠΎΠ·ΠΈΡ†ΠΈΠΈ ΠΏΠΎ Ρ€Ρ‹Π½ΠΎΡ‡Π½ΠΎΠΉ Ρ†Π΅Π½Π΅
                        print("-" * 50)

    def notify_order(self, order):
        """ИзмСнСниС статуса заявки"""
        order_data_name = order.data._name  # Имя Ρ‚ΠΈΠΊΠ΅Ρ€Π° ΠΈΠ· заявки
        print("*"*50)
        self.log(f'Заявка Π½ΠΎΠΌΠ΅Ρ€ {order.ref} {order.info["order_number"]} {order.getstatusname()} {"ΠŸΠΎΠΊΡƒΠΏΠΊΠ°" if order.isbuy() else "ΠŸΡ€ΠΎΠ΄Π°ΠΆΠ°"} {order_data_name} {order.size} @ {order.price}')
        if order.status == bt.Order.Completed:  # Если заявка ΠΏΠΎΠ»Π½ΠΎΡΡ‚ΡŒΡŽ исполнСна
            if order.isbuy():  # Заявка Π½Π° ΠΏΠΎΠΊΡƒΠΏΠΊΡƒ
                self.log(f'ΠŸΠΎΠΊΡƒΠΏΠΊΠ° {order_data_name} Π¦Π΅Π½Π°: {order.executed.price:.2f}, ΠžΠ±ΡŠΡ‘ΠΌ: {order.executed.value:.2f}, Комиссия: {order.executed.comm:.2f}')
            else:  # Заявка Π½Π° ΠΏΡ€ΠΎΠ΄Π°ΠΆΡƒ
                self.log(f'ΠŸΡ€ΠΎΠ΄Π°ΠΆΠ° {order_data_name} Π¦Π΅Π½Π°: {order.executed.price:.2f}, ΠžΠ±ΡŠΡ‘ΠΌ: {order.executed.value:.2f}, Комиссия: {order.executed.comm:.2f}')
                self.orders[order_data_name] = None  # БбрасываСм заявку Π½Π° Π²Ρ…ΠΎΠ΄ Π² ΠΏΠΎΠ·ΠΈΡ†ΠΈΡŽ
        print("*" * 50)

    def notify_trade(self, trade):
        """ИзмСнСниС статуса ΠΏΠΎΠ·ΠΈΡ†ΠΈΠΈ"""
        if trade.isclosed:  # Если позиция Π·Π°ΠΊΡ€Ρ‹Ρ‚Π°
            self.log(f'ΠŸΡ€ΠΈΠ±Ρ‹Π»ΡŒ ΠΏΠΎ Π·Π°ΠΊΡ€Ρ‹Ρ‚ΠΎΠΉ ΠΏΠΎΠ·ΠΈΡ†ΠΈΠΈ {trade.getdataname()} ΠžΠ±Ρ‰Π°Ρ={trade.pnl:.2f}, Π‘Π΅Π· комиссии={trade.pnlcomm:.2f}')

    def log(self, txt, dt=None):
        """Π’Ρ‹Π²ΠΎΠ΄ строки с Π΄Π°Ρ‚ΠΎΠΉ Π½Π° консоль"""
        dt = bt.num2date(self.datas[0].datetime[0]) if not dt else dt  # Заданная Π΄Π°Ρ‚Π° ΠΈΠ»ΠΈ Π΄Π°Ρ‚Π° Ρ‚Π΅ΠΊΡƒΡ‰Π΅Π³ΠΎ Π±Π°Ρ€Π°
        print(f'{dt.strftime("%d.%m.%Y %H:%M")}, {txt}')  # Π’Ρ‹Π²ΠΎΠ΄ΠΈΠΌ Π΄Π°Ρ‚Ρƒ ΠΈ врСмя с Π·Π°Π΄Π°Π½Π½Ρ‹ΠΌ тСкстом Π½Π° консоль
  1. — main section — line 141

  2. API connection to the exchange – lines 151..155

  3. setting strategy launch parameters 172..180

  4. strategy launch – line 182

  5. getting data by ticker/tickers via API line 172..175

  6. processing this data by the strategy – lines 61..115

  7. placing orders for the purchase / sale – lines 105 – purchase and 114 – sale

  8. return results from strategy – lines 183, 187, 188

  9. output of results – lines 183, 187, 188

The trading system class has several main methods:

  1. init – so clear – here we initialize auxiliary variables and indicators for data flows

  2. next – called every time when a new bar arrives by ticker

  3. notify_order – called when a buy or sell occurs

  4. notify_trade – called when position status changes

You can extend/add new methods/functionality as you wish.

Sometimes it’s better to see once than read a hundred times.

Therefore, I recorded a video especially for you on creating this strategy step by step:

RuTube

YouTube

If you have any thoughts on the creation, write let’s see.

The result of the trading strategy for BTC and ETH

The strategy parameters have not been optimized, so it may give a better result.

Buy/Sell on D1

Buy/Sell on D1

The result of the trading strategy

The result of the trading strategy

Those. 2000 USDT turned into 5515 USDT => 175% gain

As I see it, it turned out quite interesting πŸ™‚ And I’m waiting for your commits / fixes / ideas!

PS The library code is partially written by the community, a significant change I made is the ability to trade a portfolio of tickers – not just one, but many tickers. I fixed some bugs, tested it many times and added many good examples to create my own full-fledged strategies. Of course, there are still things to work on.

Have a nice day, everyone! Thank you for your time!

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *