diff --git a/src/arbus.py b/src/arbus.py
index 09aa2a2..cee9be5 100644
--- a/src/arbus.py
+++ b/src/arbus.py
@@ -1,8 +1,52 @@
-import options
+import time
+from datetime import datetime
+import re
from random import randint
-async def getLevels(amount, highPrice, lowPrice, roundDecimals):
+startTime = None
+
+def setStartTime():
+ startTime = time.time()
+
+def getPnL(pair):
+ PnL = 0.0
+ with open("tradingLog.log", "r") as f:
+ lines = f.readlines()
+
+ logEntries = []
+ for i in lines:
+ # Get timestamp + message tuples
+ t = re.match(r"(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3}) - (.+)", i)
+ if t:
+ timestamp_str, message = t.groups()
+ timestamp = datetime.strptime(timestamp_str, "%Y-%m-%d %H:%M:%S,%f")
+ logEntries.append((timestamp, message.strip()))
+
+ strategyStartTime = None
+ for timestamp, message in logEntries:
+ if message == f"Starting strategy with {pair}":
+ strategyStartTime = timestamp
+
+ if not strategyStartTime:
+ print(f"No 'Starting strategy' found for pair {pair}")
+ return 0.0
+
+ totalPnL = 0.0
+ pattern = re.compile(
+ rf"(Long|Short) order on {re.escape(pair)} level .*? filled with P&L (-?\d+\.?\d*) and qty .*"
+ )
+
+ for timestamp, message in logEntries:
+ if timestamp >= strategyStartTime:
+ match = pattern.search(message)
+ if match:
+ t = float(match.group(2))
+ totalPnL += t
+
+ return totalPnL
+
+def getLevels(amount, highPrice, lowPrice, roundDecimals):
# Returns array of prices from low to high for each level
levels = []
delta = (highPrice - lowPrice)/amount
diff --git a/src/bybit.py b/src/bybit.py
index 6a667fb..04b56dd 100644
--- a/src/bybit.py
+++ b/src/bybit.py
@@ -126,6 +126,7 @@ class tradingData:
tradingLogger.info(f"Placed oder on {self.pair} with TP {tp}; SL {sl}")
else:
generalLogger.warning(f"Failed to place order on {self.pair}; qty is too small!")
+ tradingLogger.warning(f"Failed to place order on {self.pair}; qty is too small!")
return orderID
@@ -233,8 +234,9 @@ async def strategy(pair: str, params):
priceDecimals = int(infoContents.get('priceScale'))
- # Levels have 3 IDs for both long and short position. The 1 is main order (market opening), 2 is SL and 3 is TP.
- levelsRaw = await arbus.getLevels(levelsAmount, highEnd, lowEnd, priceDecimals)
+ # Levels have 3 IDs for both long and short position
+ # The first is main order (market opening), second is SL and third is TP
+ levelsRaw = arbus.getLevels(levelsAmount, highEnd, lowEnd, priceDecimals)
levels = []
for i in range(levelsAmount):
levels.append({'price':levelsRaw[i], 'long':False, 'longIDs':['-1', '-1', '-1'], 'short':False, 'shortIDs':['-1', '-1', '-1']})
@@ -277,7 +279,7 @@ async def strategy(pair: str, params):
while t:
t = await jsonProcessing.checkPair(pair)
if t != 1:
- generalLogger.info("Closing websockets for {pair}")
+ generalLogger.info(f"Closing websockets for {pair}")
ws.exit()
wsPrivate.exit()
break
@@ -285,4 +287,5 @@ async def strategy(pair: str, params):
i += 1
generalLogger.info(f"Ending strategy with {pair}; Ended on the iteration number {i}")
+ tradingLogger.info(f"Ending strategy with {pair}")
return i
diff --git a/src/jsonProcessing.py b/src/jsonProcessing.py
index 78e4fd7..f1d6cc0 100644
--- a/src/jsonProcessing.py
+++ b/src/jsonProcessing.py
@@ -12,7 +12,7 @@ def startUp():
filePath = 'data.json'
if os.path.exists(filePath):
- timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
+ timestamp = datetime.now().strftime('%Y-%m-%d_%H:%M:%S')
backupPath = (f'data_backup_{timestamp}.json')
shutil.copy(filePath, backupPath)
generalLogger.info(f'JSON backup was created: {backupPath}')
diff --git a/src/logger.py b/src/logger.py
index 31d633f..6730cc7 100644
--- a/src/logger.py
+++ b/src/logger.py
@@ -31,7 +31,7 @@ generalLogger = setupLogger('general', logging.INFO, generalLogPath, generalForm
tradingFormatter = logging.Formatter('%(asctime)s - %(message)s')
tradingLogger = setupLogger('trade', logging.NOTSET, tradingLogPath, tradingFormatter)
-# Максимально подробный общий лог
+# Максимально подробный общий лог, вызывает повторы в логировании. Его наличие настраивается
if options.showExtraDebugLogs:
logging.basicConfig(level=logging.DEBUG)
superGeneralLogger = logging.getLogger('superGeneral')
diff --git a/src/main.py b/src/main.py
index 0838639..3297a83 100644
--- a/src/main.py
+++ b/src/main.py
@@ -1,3 +1,5 @@
+import time
+from datetime import datetime
import asyncio
from aiogram import Bot, Dispatcher
@@ -16,6 +18,7 @@ from logger import generalLogger
from logger import tradingLogger
import bybit
+import arbus
import jsonProcessing
import credentials
@@ -54,25 +57,32 @@ stop_router = Router()
@dp.message(Command("start"))
async def commandStart(message: Message) -> None:
- # print(whitelist.chatIDs)
- # id = message.from_user.id
- # print('Got message from', id, ' with type ', type(id))
await message.answer(strings.startCommand)
@dp.message(Command("help"), F.chat.id.in_(whitelist.chatIDs))
async def commandHelp(message: Message) -> None:
await message.answer(strings.helpCommand)
+@dp.message(Command("status"), F.chat.id.in_(whitelist.chatIDs))
+async def commandStatus(message: Message) -> None:
+ currentTime = time.time()
+ timeDiff = round(currentTime - arbus.startTime) # Время работы в секундах
+ timeDiffH = round(timeDiff/60/60, 3) # Время работы в часах
+ await message.answer(strings.status + str(timeDiff) + ' секунд' \
+ + ' (' + str(timeDiffH) + ' часов)')
+
+
@dp.message(Command("info"), F.chat.id.in_(whitelist.chatIDs))
async def commandInfo(message: Message) -> None:
data = await jsonProcessing.loadJson()
- msgText = strings.foundData
+ msgText = ''
if data == {}:
msgText = strings.noData
else:
msgText = strings.foundData
for i in data:
- msgText += (f"{str(i)}: P&L - x%\n")
+ pnl = arbus.getPnL(str(i))
+ msgText += (f"{str(i)}: P&L - {pnl}%\n")
await message.answer(msgText)
@@ -88,7 +98,7 @@ async def capture_start_pair(message: Message, state: FSMContext):
t = 0
if await jsonProcessing.checkPair(data.get("pair")) == 1:
- msgText = (f'Стратегия на паре {data.get("pair")} уже запущена.\nПожалуйста остановите стратегию либо введите другую пару.')
+ msgText = strings.strategyAlreadyRunning
t = 1
else:
msgText = strings.askParams
@@ -118,16 +128,17 @@ async def capture_params(message: Message, state: FSMContext):
else:
try:
asyncio.create_task(bybit.strategy(data.get("pair"), data.get("params")))
- msgText = (f'Вы запустили стратегию на паре {data.get("pair")} с данными параметрами:\n{data.get("params")}\n')
+ msgText = (f'Вы запустили стратегию на паре {data.get("pair")} \
+ с данными параметрами:\n{data.get("params")}\n')
except:
await jsonProcessing.deletePair(pair=data.get("pair"))
- msgText = (f'Возникла ошибка в работе стратегии =( Пожалуйста сообщите об этом администратору.')
+ msgText = (strings.strategyError)
elif t == -1:
- msgText = (f'Параметры введены в неверном формате, пожалуйста начните заново.')
+ msgText = strings.wrongFormat
elif t == -2:
- msgText = (f'Стратегия на паре {data.get("pair")} уже запущена.')
+ msgText = (f"Стратегия на паре {data.get("pair")} уже запущена.")
else:
- msgText = (f'Возникла непредвиденная ошибка. =(')
+ msgText = strings.unexpectedError
await message.answer(msgText)
await state.clear()
@@ -165,7 +176,9 @@ async def main() -> None:
# Main
if __name__ == "__main__":
- generalLogger.info("Started bot!")
- tradingLogger.info("Started bot!")
+ arbus.setStartTime()
jsonProcessing.startUp()
+ currentTime = datetime.now().strftime("%H:%M:%S")
+ generalLogger.info(f"Started bot!")
+ tradingLogger.info(f"Started bot!")
asyncio.run(main())
diff --git a/src/strings.py b/src/strings.py
index a68f74a..d91daaa 100644
--- a/src/strings.py
+++ b/src/strings.py
@@ -8,6 +8,10 @@ startStrategy = "Стратегия запущена!"
stopStrategy = "Стратегия остановлена!"
+strategyAlreadyRunning = "Стратегия на данной паре уже запущена.\nПожалуйста остановите стратегию либо введите другую пару."
+
+strategyError = "Возникла ошибка в работе стратегии =( Пожалуйста сообщите об этом администратору."
+
# Bot Statuses
@@ -15,6 +19,9 @@ startBot = "Бот запущен!"
stopBot = "Бот остановлен!"
+status = "Бот работает в течение "
+
+unexpectedError = "Возникла непредвиденная ошибка. =("
# Commands
@@ -45,7 +52,9 @@ gotParams = "Параметры заданы!"
pairNotFound = "Стратегия на данную монетную пару не найдена."
-authFailed = (f'Аутентификация не удалась, пожалуйста сообщите администратору если увидете данное сообщение.')
+authFailed = (f"Аутентификация не удалась, пожалуйста сообщите администратору если увидете данное сообщение.")
+
+wrongFormat = "Параметры введены в неверном формате, пожалуйста начните заново."
# Data status
diff --git a/todo.md b/todo.md
index fad1636..675fe38 100644
--- a/todo.md
+++ b/todo.md
@@ -21,6 +21,7 @@
- - - [x] Strategy (Запуск стратегии)
- - - [x] Stop (Остановка стратегии)
- - - [x] Info (Информация о запущеных стратегиях)
+ - - - [ ] Проверка на баланс и минимальное количество
- - [x] Обеспечение безопасности и приватности бота (через chat id или пароль)
- [x] Реализация стратегии
- - [x] Основная функция для запуска стратегии