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
Installing the latest version Python 3.11
Installing the development environment PyCharm Community 2023.1
Launching PyCharm Community
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”.
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.
Installing the required libraries
To install the library that integrates the Binance API with back trader enter the command
pip install backtrader_binance
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
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
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
Register your account at Binance
Go to section “API Management”
Then click the “Generate API” button and select “System Generated”.
In the API Limits section, enable “Enable spot and margin trading”.
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
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 # Π€Π°ΠΉΠ» ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ
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
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}') # ΠΡΠ²ΠΎΠ΄ΠΈΠΌ Π΄Π°ΡΡ ΠΈ Π²ΡΠ΅ΠΌΡ Ρ Π·Π°Π΄Π°Π½Π½ΡΠΌ ΡΠ΅ΠΊΡΡΠΎΠΌ Π½Π° ΠΊΠΎΠ½ΡΠΎΠ»Ρ
— main section — line 141
API connection to the exchange – lines 151..155
setting strategy launch parameters 172..180
strategy launch – line 182
getting data by ticker/tickers via API line 172..175
processing this data by the strategy – lines 61..115
placing orders for the purchase / sale – lines 105 – purchase and 114 – sale
return results from strategy – lines 183, 187, 188
output of results – lines 183, 187, 188
The trading system class has several main methods:
init – so clear – here we initialize auxiliary variables and indicators for data flows
next – called every time when a new bar arrives by ticker
notify_order – called when a buy or sell occurs
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:
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.
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!