Saving logs+data to dir. fixes

This commit is contained in:
2025-05-22 14:27:04 +03:00
parent e9d10af8da
commit b1835e2f72
12 changed files with 198 additions and 62 deletions

1
.gitignore vendored
View File

@ -9,3 +9,4 @@ src/test2.py
tradingLog.log
generalLog.log
src/.env
data/

45
README-EN.md Normal file
View File

@ -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!

View File

@ -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` перед командой*
Спасибо что заглянули, желаем удачной настройки и стабильной работы!

15
compose.yml Normal file
View File

@ -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

View File

@ -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** включите режим хеджирования на все пары, которыми планируете торговать
Спасибо что заглянули, желаем удачной настройки и стабильной работы!

View File

@ -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()

View File

@ -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(

View File

@ -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

View File

@ -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')

View File

@ -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'Вы запустили стратегию на паре <b>{data.get("pair")}</b> \
с данными параметрами:\n<b>{data.get("params")}</b>\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'Вы запустили стратегию на паре <b>{data.get("pair")}</b> с данными параметрами:\n<b>{data.get("params")}</b>\n')
except:
await jsonProcessing.deletePair(pair=data.get("pair"))
msgText = (strings.strategyError)
elif t == -1:
msgText = strings.wrongFormat
elif t == -2:

View File

@ -54,6 +54,10 @@ pairNotFound = "Стратегия на данную монетную пару
authFailed = (f"Аутентификация не удалась, пожалуйста сообщите администратору если увидете данное сообщение.")
orderSizeLowerThanQty = "Введённая сумма меньше минимального размера ордера на Bybit."
notEnoughBalance = "На балансе недостаточно средств для зпуска стратегии."
wrongFormat = "Параметры введены в неверном формате, пожалуйста начните заново."

17
todo.md
View File

@ -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