diff --git a/src/bybit.py b/src/bybit.py
index 0fe4c88..0303437 100644
--- a/src/bybit.py
+++ b/src/bybit.py
@@ -12,6 +12,7 @@ import arbus
from logger import generalLogger
from logger import tradingLogger
+from logger import debugLogger
def getPrice(client, pair):
@@ -60,6 +61,7 @@ class tradingData:
self.priceDecimals, self.qtyDecimals, self.minimumQty = self.getFilters(pair)
self.previousPrice = -1
self.orderCounter = 0
+ self.closed = 0
def getBalance(self, pair):
@@ -84,13 +86,18 @@ class tradingData:
return priceDecimals, qtyDecimals, minimumQty
def close(self):
- jsonProcessing.deletePair(self.pair)
+ self.closed = 1
+ t = jsonProcessing.deletePair(self.pair)
generalLogger.info(f"Closing strategy on {self.pair}")
def checkCloseConditions(self, markPrice):
# If the price is outside of the (lowBreak; highBreak) interval then stop strategy
- if not (self.lowBreak < markPrice < self.highBreak):
- self.close()
+ if self.closed == 0:
+ if not (self.lowBreak < markPrice < self.highBreak):
+ self.close()
+ elif self.closed == 1:
+ generalLogger.error(f"Called checkCloseConditions with selfClose = 1 on {self.pair}")
+ self.closed = 2
def checkOrderConditions(self, markPrice):
@@ -173,7 +180,9 @@ class tradingData:
def handlePositionInfo(self, message):
data = message.get('data')
-
+
+ debugLogger.debug(data)
+
# Usually the 3-order response means SL + market + TP orders were placed.
if len(data) == 3:
orderType = []
@@ -252,7 +261,7 @@ async def strategy(pair: str, params):
generalLogger.info('Starting strategy with ' + pair)
tradingLogger.info('Starting strategy with ' + pair)
- paramsDict = await jsonProcessing.parseParams(params)
+ paramsDict = jsonProcessing.parseParams(params)
client = HTTP(testnet=True)
@@ -309,9 +318,9 @@ async def strategy(pair: str, params):
)
i = 0
- t = await jsonProcessing.checkPair(pair)
+ t = jsonProcessing.checkPair(pair)
while t:
- t = await jsonProcessing.checkPair(pair)
+ t = jsonProcessing.checkPair(pair)
if t != 1:
generalLogger.info(f"Closing websockets for {pair}")
ws.exit()
diff --git a/src/jsonProcessing.py b/src/jsonProcessing.py
index 0fbafb4..4e90587 100644
--- a/src/jsonProcessing.py
+++ b/src/jsonProcessing.py
@@ -16,10 +16,12 @@ def startUp():
if not(os.path.exists('data')):
os.mkdir('data')
+ if not(os.path.exists('data/backup')):
+ os.mkdir('data/backup')
if os.path.exists(filePath):
timestamp = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
- backupPath = (f'data/data_backup_{timestamp}.json')
+ backupPath = (f'data/backup/data_backup_{timestamp}.json')
shutil.copy(filePath, backupPath)
generalLogger.info(f'JSON backup was created: {backupPath}')
@@ -28,7 +30,7 @@ def startUp():
generalLogger.info(f'New {filePath} created with empty JSON.')
-async def parseParams(params):
+def parseParams(params):
# Returnes dictionary of string params as paramsLines in options
paramsList = params.split()
paramsDict = {}
@@ -39,7 +41,7 @@ async def parseParams(params):
paramsDict[options.paramsLines[i]] = paramsList[i]
return paramsDict
-async def toDictPairParams(pair: str, params):
+def toDictPairParams(pair: str, params):
# Returnes dictionary as pair:internal(params)
paramsList = params.split()
@@ -52,7 +54,7 @@ async def toDictPairParams(pair: str, params):
return paramsDict
-async def checkPair(pair: str):
+def checkPair(pair: str):
# Returnes 1 if pair exists and 0 if not
currentData = {}
try:
@@ -66,7 +68,7 @@ async def checkPair(pair: str):
else:
return 0
-async def deletePair(pair: str):
+def deletePair(pair: str):
# Returnes 0 if deleted successfully and -1 if not
currentData = {}
try:
@@ -85,9 +87,9 @@ async def deletePair(pair: str):
generalLogger.info(f'Pair {pair} was not found in the data file when trying to delete it.')
return -1
-async def savePairParams(pair: str, params):
+def savePairParams(pair: str, params):
# Saves or updates data in JSON
- newData = await toDictPairParams(pair, params)
+ newData = toDictPairParams(pair, params)
if newData == -1:
return -1
@@ -110,7 +112,7 @@ async def savePairParams(pair: str, params):
return 0
-async def loadJson():
+def loadJson():
# Returnes the contents of the JSON file as a dictionary
data = {}
try:
diff --git a/src/logger.py b/src/logger.py
index 9985bcf..ec8c761 100644
--- a/src/logger.py
+++ b/src/logger.py
@@ -7,6 +7,7 @@ import options
generalLogPath = "./data/generalLog.log"
tradingLogPath = "./data/tradingLog.log"
+debugLogPath = "./data/debugLog.log"
def setupLogger(name, level, logPath, formatter):
@@ -35,6 +36,10 @@ generalLogger = setupLogger('general', logging.INFO, generalLogPath, generalForm
tradingFormatter = logging.Formatter('%(asctime)s - %(message)s')
tradingLogger = setupLogger('trade', logging.NOTSET, tradingLogPath, tradingFormatter)
+# Дебаг лог (содержимое ответов от API, всякие мелочи)
+debugFormatter = logging.Formatter('%(asctime)s - %(message)s')
+debugLogger = setupLogger('debug', logging.DEBUG, debugLogPath, debugFormatter)
+
# Максимально подробный дебаг лог, вызывает повторы в логировании. Его наличие настраивается
if options.showExtraDebugLogs:
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
diff --git a/src/main.py b/src/main.py
index fba6f09..2351ac6 100644
--- a/src/main.py
+++ b/src/main.py
@@ -16,6 +16,7 @@ from aiogram.fsm.storage.memory import MemoryStorage
from logger import generalLogger
from logger import tradingLogger
+from logger import debugLogger
import bybit
import arbus
@@ -75,7 +76,7 @@ async def commandStatus(message: Message) -> None:
@dp.message(Command("info"), F.chat.id.in_(whitelist.chatIDs))
async def commandInfo(message: Message) -> None:
- data = await jsonProcessing.loadJson()
+ data = jsonProcessing.loadJson()
msgText = ''
if data == {}:
msgText = strings.noData
@@ -98,7 +99,7 @@ async def captureStartPair(message: Message, state: FSMContext):
data = await state.get_data()
t = 0
- if await jsonProcessing.checkPair(data.get("pair")) == 1:
+ if jsonProcessing.checkPair(data.get("pair")) == 1:
msgText = strings.strategyAlreadyRunning
t = 1
else:
@@ -115,9 +116,9 @@ async def captureParams(message: Message, state: FSMContext):
await state.update_data(params=message.text)
data = await state.get_data()
- t = await jsonProcessing.savePairParams(pair=data.get("pair"), params=data.get("params"))
+ t = jsonProcessing.savePairParams(pair=data.get("pair"), params=data.get("params"))
if t == 0:
- params = await jsonProcessing.parseParams(params=data.get("params"))
+ params = jsonProcessing.parseParams(params=data.get("params"))
client = await bybit.getClient(
credentials.api_key,
credentials.api_secret,
@@ -127,7 +128,7 @@ async def captureParams(message: Message, state: FSMContext):
if client == -1:
msgText = strings.authFailed
generalLogger.info("Auth failed. Strategy not started")
- await jsonProcessing.deletePair(pair=data.get("pair"))
+ jsonProcessing.deletePair(pair=data.get("pair"))
else:
orderSize = float(params.get('orderSize'))
minqty = bybit.getStartFilters(client, data.get("pair"))
@@ -139,17 +140,17 @@ async def captureParams(message: Message, state: FSMContext):
if qty <= minqty:
generalLogger.info("Qty < minqty. Strategy not started")
msgText = strings.orderSizeLowerThanQty
- await jsonProcessing.deletePair(pair=data.get("pair"))
+ jsonProcessing.deletePair(pair=data.get("pair"))
elif balance <= orderSize:
generalLogger.info("Balance < order size. Strategy not started")
msgText = strings.notEnoughBalance
- await jsonProcessing.deletePair(pair=data.get("pair"))
+ 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"))
+ jsonProcessing.deletePair(pair=data.get("pair"))
msgText = (strings.strategyError)
elif t == -1:
msgText = strings.wrongFormat
@@ -171,7 +172,7 @@ async def captureStopPair(message: Message, state: FSMContext):
await state.update_data(pair=message.text)
data = await state.get_data()
- t = await jsonProcessing.deletePair(data.get("pair"))
+ t = jsonProcessing.deletePair(data.get("pair"))
if t == 0:
msgText = strings.stopStrategy
else:
diff --git a/src/strings.py b/src/strings.py
index 53bf52a..793eb96 100644
--- a/src/strings.py
+++ b/src/strings.py
@@ -37,7 +37,9 @@ helpCommand = (f"При старте стратегии требуется за
"Количество уровней сетки\n" \
"Дельта для тейка\n" \
"Дельта для стопа\n" \
-"Размер позиции на каждом уровне")
+"Размер позиции на каждом уровне\n" \
+"\n"\
+"Чтобы остановаить запуск стратегии просто при запросе параметров введите не 8 строк, можно одну любую букву.")
strategyCommand = "Вы собираетесь запустить стратегию."
diff --git a/todo2.md b/todo2.md
new file mode 100644
index 0000000..341d76b
--- /dev/null
+++ b/todo2.md
@@ -0,0 +1,10 @@
+Версия 1.x.x
+### ToFix:
+ - [ ] Подсчёт P&L (активные ордера)
+
+### Новые функции:
+ - [ ] Запрос P&L за период
+ - [ ] Поддержка параллельного пользователя
+ - [ ] Мультиюзер версия
+ - [ ] Команда отмены
+ - [ ] Команда myid