diff --git a/.gitignore b/.gitignore
index 00c1212..bcbf966 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,4 @@ src/test2.py
tradingLog.log
generalLog.log
src/.env
+data/
diff --git a/README-EN.md b/README-EN.md
new file mode 100644
index 0000000..51f8569
--- /dev/null
+++ b/README-EN.md
@@ -0,0 +1,45 @@
+# [ENG] Side Strategy Bybit Bot
+This is a simple semi-automatic trading bot working with Bybit API and written in Python.
+The strategy is based on the side moving of the token which crosses user input levels. On the level cross long and short orders are opened.
+
+# Install guide
+Bot is installed via **docker compose** file and **docker pull** command.
+This "guide" is made for **linux** and **docker**, but you should be able to run this bot on any system with *Python 3*.
+### Installing via `docker compose`:
+1) Create bot directory
+2) Pull image from git:
+```sh
+docker pull git.frik.su/eugenebee/tradingbot-with-bybitapi:latest
+```
+3) Create **compose.yml** file:
+```yaml
+services:
+ bybit-bot:
+ image: git.frik.su/eugenebee/tradingbot-with-bybitapi:latest
+ container_name: bybit-bot
+ environment:
+ API_KEY: "bybit-API-key"
+ API_SECRET: "bybit-secret-API-key"
+ BOT_TOKEN: "telegram-bot-token"
+ WHITELIST: "chat-id-1, chat-id-2"
+ LEVERAGE: "1" # Currently not supported. Set leverage in your Bybit account
+ TESTNET: "False"
+ DEMOTRADING: "False"
+ LOOPSLEEPTIME: "1"
+ SHOWEXTRADEBUGLOGS: "False"
+ restart: unless-stopped
+```
+### After the basic pre-setup:
+*It is recommended to use Bybit sub accaunt forn the bot.*
+1) Get your **API** keys for the **Bybit** and **Telegram**
+2) Enter those in the **compose.yml** variables and your **Telegram id** into the `WHITELIST`
+2) At the **Bybit** platform turn on hedge mode foor all tokens you want the bot to trade
+3) Read the milk leaves and then make ***4*** circles around the server with the bee hive in your hands
+4) Everything is ready for the first start, enjoy!
+### To start up the bot enter the next command:
+```sh
+docker compose up -d
+```
+*If you need use the `sudo` before the command*
+
+Thank you for reafing, hope everything will work fine!
diff --git a/README.md b/README.md
index b1d1878..f543223 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,45 @@
-# Side Strategy Bybit Bot
-This is a simple semi-automatic trading bot working with Bybit API and written in Python.
-The strategy is based on the side moving of the token which crosses user input levels. On the level cross long and short orders are opened.
-### For the install and setup guide look at the `setup.md`
+# [RU] Side Strategy Bybit Bot
+Это достаточно простой полуавтоматический торговый бот, работающий с Bybit API, написанный на Python.
+Стратегия основана на боковом движении цены токена и пересечении заданных пользователей уровней. При пересечении уровня открываются long и short позиции.
+
+## Установка
+Бот устанавливается с помощью **docker compose** файла и **docker pull**.
+Данный "гайд" расчитан на **linux** и **docker**, однако вы можете развернуть данное приложение/бота на любой системе с *Python 3*.
+### Для установки через `docker compose`:
+1) Создайте директорию для бота
+2) Введите команду:
+```sh
+docker pull git.frik.su/eugenebee/tradingbot-with-bybitapi:latest
+```
+3) Создайте **compose.yml** файл с следующим содержимым:
+```yaml
+services:
+ bybit-bot:
+ image: git.frik.su/eugenebee/tradingbot-with-bybitapi:latest
+ container_name: bybit-bot
+ environment:
+ API_KEY: "bybit-API-key"
+ API_SECRET: "bybit-secret-API-key"
+ BOT_TOKEN: "telegram-bot-token"
+ WHITELIST: "chat-id-1, chat-id-2"
+ LEVERAGE: "1" # Временно не поддерживается, устанавливайте плечо в вашем Bybit аккаунте
+ TESTNET: "False"
+ DEMOTRADING: "False"
+ LOOPSLEEPTIME: "1"
+ SHOWEXTRADEBUGLOGS: "False"
+ restart: unless-stopped
+```
+### После базовой развёртки выполните следующие шаги:
+*Рекомендуется использовать суб аккаунт Bybit для бота.*
+1) Получите **API** ключи на **Bybit** и **Telegram**. Рекомендуем использовать суб аккаунт Bybit для бота
+2) Введите в **compose.yml** свои ключи в соответствующие поля и ваш **Telegram id** в `WHITELIST`
+2) На платформе **Bybit** включите режим хеджирования на все пары, которыми планируете торговать
+3) Погадайте на молочной гуще и сделайте ***4*** круга с пчелиным ульем вокруг сервера
+4) Всё готово к запуску, наслаждайтесь!
+### Для запуска бота введите следующую команду:
+```sh
+docker compose up -d
+```
+*По надобности используйте `sudo` перед командой*
+
+Спасибо что заглянули, желаем удачной настройки и стабильной работы!
diff --git a/compose.yml b/compose.yml
new file mode 100644
index 0000000..34b9874
--- /dev/null
+++ b/compose.yml
@@ -0,0 +1,15 @@
+services:
+ bybit-bot:
+ image: git.frik.su/eugenebee/tradingbot-with-bybitapi:latest
+ container_name: bybit-bot
+ environment:
+ API_KEY: "bybit-API-key"
+ API_SECRET: "bybit-secret-API-key"
+ BOT_TOKEN: "telegram-bot-token"
+ WHITELIST: "chat-id-1, chat-id-2"
+ LEVERAGE: "1" # Not supported. Set leverage in bybit account
+ TESTNET: "False"
+ DEMOTRADING: "False"
+ LOOPSLEEPTIME: "1"
+ SHOWEXTRADEBUGLOGS: "False"
+ restart: unless-stopped
\ No newline at end of file
diff --git a/setup.md b/setup.md
deleted file mode 100644
index 6066042..0000000
--- a/setup.md
+++ /dev/null
@@ -1,25 +0,0 @@
-# Краткий гайд по установке и настройке бота.
-Бот устанавливается с помощью **docker compose** файла и **git pull** (предварительно) и последующей лёгкой настройки через файлы.
-Данный "гайд" расчитан на **linux** и **docker**, однако вы можете развернуть данное приложение/бота на любой системе с *Python 3*.
-### Для установки через `docker compose`:
-1) Создайте директорию для бота
-2) Введите команду:
-```
-git clone
-```
-### После базовой развёртки выполните следующие шаги:
-1) Создайте data.json файл и заполните его данным содержимым:
-```> {}```
-2) Создайте файл `credentials.py` в `src/` и заполните его по аналогии с `cretentialsExample.py`
-*Рекомендуется использовать суб аккаунт Bybit для бота*
-3) Настройте options.py файл на ваше усмотрение (обратите особо внимание на параметр `testnet`)
-4) Погадайте на молочной гуще и сделайте ***4*** круга с пчелиным ульем вокруг сервера
-5) Всё готово к запуску, наслаждайтесь!
-### Для запуска бота введите следующую команду:
-```
-docker compose up -d
-```
-*По надобности используйте `sudo` перед командой*
-6) На платформе **Bybit** включите режим хеджирования на все пары, которыми планируете торговать
-
-Спасибо что заглянули, желаем удачной настройки и стабильной работы!
diff --git a/src/arbus.py b/src/arbus.py
index cee9be5..0c0d49f 100644
--- a/src/arbus.py
+++ b/src/arbus.py
@@ -7,10 +7,10 @@ from random import randint
startTime = None
def setStartTime():
+ global startTime
startTime = time.time()
def getPnL(pair):
- PnL = 0.0
with open("tradingLog.log", "r") as f:
lines = f.readlines()
diff --git a/src/bybit.py b/src/bybit.py
index 04b56dd..a72f2d9 100644
--- a/src/bybit.py
+++ b/src/bybit.py
@@ -14,6 +14,33 @@ from logger import generalLogger
from logger import tradingLogger
+def getPrice(client, pair):
+ ticker = client.get_tickers(
+ category = "linear",
+ symbol = pair
+ )
+ price = float(ticker.get('result').get('list')[0].get('ask1Price'))
+ return price
+
+def getStartBalance(client, pair):
+ coin = pair[:-4]
+ response = client.get_wallet_balance(
+ accountType = "UNIFIED",
+ coin = coin
+ )
+ balance = float(response['result']['list'][0]['totalAvailableBalance'])
+ return balance
+
+def getStartFilters(client, pair):
+ instrumentInfo = client.get_instruments_info(
+ symbol = pair,
+ category = "linear"
+ )
+ infoContents = instrumentInfo.get('result').get('list')[0]
+ minimumQty = float(infoContents.get('lotSizeFilter').get('minOrderQty'))
+ return minimumQty
+
+
class tradingData:
def __init__(self, pair, levels, highBreak, lowBreak, takeDelta, stopDelta, orderSize):
self.client = HTTP(
diff --git a/src/jsonProcessing.py b/src/jsonProcessing.py
index f1d6cc0..806da17 100644
--- a/src/jsonProcessing.py
+++ b/src/jsonProcessing.py
@@ -8,12 +8,18 @@ from logger import generalLogger
import options
+jsonPath = 'data/data.json'
+
+
def startUp():
- filePath = 'data.json'
+ filePath = jsonPath
+
+ if not(os.path.exists('data')):
+ os.mkdir('data')
if os.path.exists(filePath):
- timestamp = datetime.now().strftime('%Y-%m-%d_%H:%M:%S')
- backupPath = (f'data_backup_{timestamp}.json')
+ timestamp = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
+ backupPath = (f'data/data_backup_{timestamp}.json')
shutil.copy(filePath, backupPath)
generalLogger.info(f'JSON backup was created: {backupPath}')
@@ -47,10 +53,10 @@ async def checkPair(pair: str):
# Returnes 1 if pair exists and 0 if not
currentData = {}
try:
- with open('data.json', 'r') as f:
+ with open(jsonPath, 'r') as f:
currentData = json.load(f)
except json.decoder.JSONDecodeError as e:
- generalLogger.info('WARNING: JSON file is empty! Ignore if your installation is fresh.')
+ generalLogger.warning('JSON file is empty!')
if pair in currentData:
return 1
@@ -61,14 +67,14 @@ async def deletePair(pair: str):
# Returnes 0 if deleted successfully and -1 if not
currentData = {}
try:
- with open('data.json', 'r') as f:
+ with open(jsonPath, 'r') as f:
currentData = json.load(f)
except json.decoder.JSONDecodeError as e:
- generalLogger.info('WARNING: JSON file is empty! Ignore if your installation is fresh.')
+ generalLogger.warning('JSON file is empty!')
if pair in currentData:
del currentData[pair]
- with open('data.json', 'w', encoding = 'utf-8') as f:
+ with open(jsonPath, 'w', encoding = 'utf-8') as f:
json.dump(currentData, f, ensure_ascii = False, indent = 4)
generalLogger.info(f'Pair {pair} was deleted successfully!')
return 0
@@ -85,16 +91,16 @@ async def savePairParams(pair: str, params):
currentData = {}
try:
- with open('data.json', 'r') as f:
+ with open(jsonPath, 'r') as f:
currentData = json.load(f)
except json.decoder.JSONDecodeError as e:
- generalLogger.info('WARNING: JSON file is empty! Ignore if your installation is fresh.')
+ generalLogger.warning('JSON file is empty!')
if pair in currentData:
generalLogger.info(f"Pair {pair} already exists.")
return -2
else:
- with open('data.json', 'w', encoding = 'utf-8') as f:
+ with open(jsonPath, 'w', encoding = 'utf-8') as f:
currentData.update(newData)
json.dump(currentData, f, ensure_ascii = False, indent = 4)
generalLogger.info(f"Pair {pair} was added!")
@@ -105,8 +111,8 @@ async def loadJson():
# Returnes the contents of the JSON file as a dictionary
data = {}
try:
- with open('data.json', 'r') as f:
+ with open(jsonPath, 'r') as f:
data = json.load(f)
except json.decoder.JSONDecodeError as e:
- generalLogger.info('WARNING: JSON file is empty! Ignore if your installation is fresh.')
+ generalLogger.warning('JSON file is empty!')
return data
diff --git a/src/logger.py b/src/logger.py
index 6730cc7..9985bcf 100644
--- a/src/logger.py
+++ b/src/logger.py
@@ -1,11 +1,12 @@
import logging
import sys
+import os
import options
-generalLogPath = "./generalLog.log"
-tradingLogPath = "./tradingLog.log"
+generalLogPath = "./data/generalLog.log"
+tradingLogPath = "./data/tradingLog.log"
def setupLogger(name, level, logPath, formatter):
@@ -23,15 +24,18 @@ def setupLogger(name, level, logPath, formatter):
return logger
+if not(os.path.exists('data')):
+ os.mkdir('data')
+
# Основной лог
generalFormatter = logging.Formatter('%(asctime)s - %(module)s - %(levelname)s - %(message)s')
generalLogger = setupLogger('general', logging.INFO, generalLogPath, generalFormatter)
-# Торговый лог (ордера)
+# Торговый лог (открытие, закрытие ордеров, запуск стратегии)
tradingFormatter = logging.Formatter('%(asctime)s - %(message)s')
tradingLogger = setupLogger('trade', logging.NOTSET, tradingLogPath, tradingFormatter)
-# Максимально подробный общий лог, вызывает повторы в логировании. Его наличие настраивается
+# Максимально подробный дебаг лог, вызывает повторы в логировании. Его наличие настраивается
if options.showExtraDebugLogs:
- logging.basicConfig(level=logging.DEBUG)
+ logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
superGeneralLogger = logging.getLogger('superGeneral')
diff --git a/src/main.py b/src/main.py
index 3297a83..89de3a6 100644
--- a/src/main.py
+++ b/src/main.py
@@ -115,6 +115,7 @@ async def capture_params(message: Message, state: FSMContext):
data = await state.get_data()
t = await jsonProcessing.savePairParams(pair=data.get("pair"), params=data.get("params"))
+ params = await jsonProcessing.parseParams(params=data.get("params"))
if t == 0:
client = await bybit.getClient(
credentials.api_key,
@@ -124,15 +125,31 @@ async def capture_params(message: Message, state: FSMContext):
)
if client == -1:
msgText = strings.authFailed
+ generalLogger.info("Auth failed. Strategy not started")
await jsonProcessing.deletePair(pair=data.get("pair"))
else:
- try:
- asyncio.create_task(bybit.strategy(data.get("pair"), data.get("params")))
- msgText = (f'Вы запустили стратегию на паре {data.get("pair")} \
- с данными параметрами:\n{data.get("params")}\n')
- except:
+ orderSize = float(params.get('orderSize'))
+ minqty = bybit.getStartFilters(client, data.get("pair"))
+ qtyDecimals = arbus.countDecimals(minqty)
+ balance = bybit.getStartBalance(client, data.get("pair"))
+ price = bybit.getPrice(client, data.get("pair"))
+
+ qty = arbus.floor(orderSize/price, qtyDecimals)
+ if qty <= minqty:
+ generalLogger.info("Qty < minqty. Strategy not started")
+ msgText = strings.orderSizeLowerThanQty
await jsonProcessing.deletePair(pair=data.get("pair"))
- msgText = (strings.strategyError)
+ elif balance <= orderSize:
+ generalLogger.info("Balance < order size. Strategy not started")
+ msgText = strings.notEnoughBalance
+ await jsonProcessing.deletePair(pair=data.get("pair"))
+ else:
+ try:
+ asyncio.create_task(bybit.strategy(data.get("pair"), data.get("params")))
+ msgText = (f'Вы запустили стратегию на паре {data.get("pair")} с данными параметрами:\n{data.get("params")}\n')
+ except:
+ await jsonProcessing.deletePair(pair=data.get("pair"))
+ msgText = (strings.strategyError)
elif t == -1:
msgText = strings.wrongFormat
elif t == -2:
diff --git a/src/strings.py b/src/strings.py
index d91daaa..53bf52a 100644
--- a/src/strings.py
+++ b/src/strings.py
@@ -54,6 +54,10 @@ pairNotFound = "Стратегия на данную монетную пару
authFailed = (f"Аутентификация не удалась, пожалуйста сообщите администратору если увидете данное сообщение.")
+orderSizeLowerThanQty = "Введённая сумма меньше минимального размера ордера на Bybit."
+
+notEnoughBalance = "На балансе недостаточно средств для зпуска стратегии."
+
wrongFormat = "Параметры введены в неверном формате, пожалуйста начните заново."
diff --git a/todo.md b/todo.md
index 675fe38..65d0e26 100644
--- a/todo.md
+++ b/todo.md
@@ -2,6 +2,8 @@
### ToFix
- [x] ID бота (создать нового)
- [x] Замена print на логирование
+ - [x] EN README.md
+ - [ ] UA README.md
### Новые функции
- [x] Реализация базы программы
@@ -21,19 +23,18 @@
- - - [x] Strategy (Запуск стратегии)
- - - [x] Stop (Остановка стратегии)
- - - [x] Info (Информация о запущеных стратегиях)
- - - - [ ] Проверка на баланс и минимальное количество
+ - - - [x] Проверка на баланс и минимальное количество
- - [x] Обеспечение безопасности и приватности бота (через chat id или пароль)
- [x] Реализация стратегии
- - [x] Основная функция для запуска стратегии
- - [x] Класс работы по параметрам
- - [x] Реализация уровней
- - [x] Установка позиций
- - [ ] Рализация развёртывания программы
- - - [ ] Написать compose.yml
- - - [ ] Добавить requirements.txt
- - - [ ] Сделать подсасывание контейнера с гита
+ - [x] Рализация развёртывания программы
+ - - [x] Написать compose.yml
+ - - [x] Добавить requirements.txt
+ - - [x] Сделать подсасывание контейнера с гита
- - [x] Составить список и реализовать получение переменных окружения
- - [ ] QOL
+ - [x] QOL
- - [x] Написать todo.md
- - - [ ] Написать README.md
- - - [ ] Написать setup.md
+ - - [x] Написать README.md