Merge pull request 'devel' (#16) from Alex/stcs:devel into stable

Reviewed-on: #16
This commit is contained in:
2025-10-18 18:09:23 +07:00
6 changed files with 105 additions and 32 deletions

View File

@@ -10,6 +10,7 @@ from app.bybit.get_functions.get_tickers import get_tickers
from app.bybit.logger_bybit.logger_bybit import LOGGING_CONFIG
from app.bybit.set_functions.set_leverage import set_leverage
from app.bybit.set_functions.set_margin_mode import set_margin_mode
from app.bybit.set_functions.set_switch_position_mode import set_switch_position_mode
from app.helper_functions import get_liquidation_price, safe_float
logging.config.dictConfig(LOGGING_CONFIG)
@@ -90,6 +91,10 @@ async def start_trading_cycle(
price_for_cals = trigger_price if po_trigger_price is not None else price_symbol
total_commission = price_for_cals * qty_formatted * commission_fee_percent
await set_switch_position_mode(
tg_id=tg_id,
symbol=symbol,
mode=0)
await set_margin_mode(tg_id=tg_id, margin_mode=margin_type)
await set_leverage(
tg_id=tg_id,
@@ -291,10 +296,10 @@ async def open_positions(
if (liq_long > 0 or liq_short > 0) and price_for_cals > 0:
if side == "Buy":
base_tp = price_for_cals + (price_for_cals - liq_long)
take_profit_price = base_tp + commission_fee_percent
take_profit_price = base_tp + commission_fee_percent / qty_formatted
else:
base_tp = price_for_cals - (liq_short - price_for_cals)
take_profit_price = base_tp - commission_fee_percent
take_profit_price = base_tp - commission_fee_percent / qty_formatted
take_profit_price = max(take_profit_price, 0)
else:
take_profit_price = None
@@ -302,11 +307,11 @@ async def open_positions(
stop_loss_price = None
else:
if side == "Buy":
take_profit_price = price_for_cals * (1 + take_profit_percent / 100) + commission_fee_percent
stop_loss_price = price_for_cals * (1 - stop_loss_percent / 100) - commission_fee_percent
take_profit_price = price_for_cals * (1 + take_profit_percent / 100) + commission_fee_percent / qty_formatted
stop_loss_price = price_for_cals * (1 - stop_loss_percent / 100)
else:
take_profit_price = price_for_cals * (1 - take_profit_percent / 100) - commission_fee_percent
stop_loss_price = price_for_cals * (1 + stop_loss_percent / 100) + commission_fee_percent
take_profit_price = price_for_cals * (1 - take_profit_percent / 100) - commission_fee_percent / qty_formatted
stop_loss_price = price_for_cals * (1 + stop_loss_percent / 100)
take_profit_price = max(take_profit_price, 0)
stop_loss_price = max(stop_loss_price, 0)

View File

@@ -41,11 +41,26 @@ class TelegramMessageHandler:
if order_status == "Filled" or order_status not in status_map:
return None
user_auto_trading = await rq.get_user_auto_trading(
tg_id=tg_id, symbol=symbol
)
auto_trading = (
user_auto_trading.auto_trading if user_auto_trading else False
)
user_deals_data = await rq.get_user_deal_by_symbol(
tg_id=tg_id, symbol=symbol
)
text = (
f"Торговая пара: {symbol}\n"
f"Количество: {qty}\n"
f"Движение: {side_rus}\n"
)
if user_deals_data is not None and auto_trading:
text += f"Текущая ставка: {user_deals_data.order_quantity}\n"
else:
text += f"Количество: {qty}\n"
if price and price != "0":
text += f"Цена: {price}\n"
if take_profit and take_profit != "Нет данных":
@@ -67,13 +82,21 @@ class TelegramMessageHandler:
closed_size = format_value(execution.get("closedSize"))
symbol = format_value(execution.get("symbol"))
exec_price = format_value(execution.get("execPrice"))
exec_fee = format_value(execution.get("execFee"))
exec_qty = format_value(execution.get("execQty"))
exec_fees = format_value(execution.get("execFee"))
fee_rate = format_value(execution.get("feeRate"))
side = format_value(execution.get("side"))
side_rus = (
"Покупка"
if side == "Buy"
else "Продажа" if side == "Sell" else "Нет данных"
)
if safe_float(exec_fees) == 0:
exec_fee = safe_float(exec_price) * safe_float(exec_qty) * safe_float(
fee_rate
)
else:
exec_fee = safe_float(exec_fees)
if safe_float(closed_size) == 0:
await rq.set_fee_user_auto_trading(
@@ -86,9 +109,7 @@ class TelegramMessageHandler:
get_total_fee = user_auto_trading.total_fee
total_fee = safe_float(exec_fee) + safe_float(get_total_fee)
await rq.set_total_fee_user_auto_trading(
tg_id=tg_id, symbol=symbol, total_fee=total_fee
)
if user_auto_trading is not None and user_auto_trading.fee is not None:
fee = user_auto_trading.fee
@@ -109,17 +130,24 @@ class TelegramMessageHandler:
)
text = f"{header}\n" f"Торговая пара: {symbol}\n"
auto_trading = (
user_auto_trading.auto_trading if user_auto_trading else False
)
user_deals_data = await rq.get_user_deal_by_symbol(
tg_id=tg_id, symbol=symbol
)
exec_bet = user_deals_data.order_quantity
base_quantity = user_deals_data.base_quantity
if user_deals_data is not None and auto_trading:
await rq.set_total_fee_user_auto_trading(
tg_id=tg_id, symbol=symbol, total_fee=total_fee
)
text += f"Текущая ставка: {user_deals_data.order_quantity}\n"
else:
text += f"Количество: {exec_qty}\n"
text += (
f"Цена исполнения: {exec_price}\n"
f"Текущая ставка: {exec_bet}\n"
f"Движение: {side_rus}\n"
f"Комиссия за сделку: {exec_fee}\n"
f"Комиссия: {exec_fee:.8f}\n"
)
if safe_float(closed_size) > 0:
@@ -129,9 +157,6 @@ class TelegramMessageHandler:
chat_id=tg_id, text=text, reply_markup=kbi.profile_bybit
)
auto_trading = (
user_auto_trading.auto_trading if user_auto_trading else False
)
user_symbols = user_auto_trading.symbol if user_auto_trading else None
if (
@@ -154,6 +179,7 @@ class TelegramMessageHandler:
await rq.set_fee_user_auto_trading(
tg_id=tg_id, symbol=symbol, fee=0
)
base_quantity = user_deals_data.base_quantity
await rq.set_order_quantity(
tg_id=tg_id, order_quantity=base_quantity
)
@@ -174,7 +200,7 @@ class TelegramMessageHandler:
"Risk is too high for this trade": "❗️ Риск сделки слишком высок для продолжения",
"ab not enough for new order": "❗️ Недостаточно средств для продолжения торговли",
"InvalidRequestError": "❗️ Недостаточно средств для размещения нового ордера с заданным количеством и плечом.",
"The number of contracts exceeds maximum limit allowed": "❗️ Количество контрактов превышает допустимое максимальное количество контрактов",
"The number of contracts exceeds maximum limit allowed": "❗️ Превышен максимальный лимит ставки",
}
error_text = errors.get(
res, "❗️ Не удалось открыть новую сделку"

View File

@@ -7,6 +7,7 @@ from aiogram.types import CallbackQuery, Message
import app.telegram.keyboards.inline as kbi
import database.request as rq
from app.bybit.get_functions.get_instruments_info import get_instruments_info
from app.bybit.get_functions.get_positions import get_active_positions_by_symbol, get_active_orders_by_symbol
from app.bybit.set_functions.set_leverage import set_leverage
from app.bybit.set_functions.set_margin_mode import set_margin_mode
from app.helper_functions import is_int, is_number, safe_float
@@ -211,6 +212,31 @@ async def settings_for_margin_type(
"""
try:
await state.clear()
symbol = await rq.get_user_symbol(tg_id=callback_query.from_user.id)
deals = await get_active_positions_by_symbol(
tg_id=callback_query.from_user.id, symbol=symbol
)
position = next((d for d in deals if d.get("symbol") == symbol), None)
if position:
size = position.get("size", 0)
else:
size = 0
if safe_float(size) > 0:
await callback_query.answer(
text="У вас есть активная позиция по текущей паре",
)
return
orders = await get_active_orders_by_symbol(
tg_id=callback_query.from_user.id, symbol=symbol)
if orders is not None:
await callback_query.answer(
text="У вас есть активный ордер по текущей паре",
)
return
await callback_query.message.edit_text(
text="Выберите тип маржи:\n\n"
"Примечание: Если у вас есть открытые позиции, то маржа примениться ко всем позициям",

View File

@@ -123,8 +123,8 @@ async def risk_management(callback_query: CallbackQuery, state: FSMContext) -> N
await callback_query.message.edit_text(
text=f"Риск-менеджмент:\n\n"
f"- Процент изменения цены для фиксации прибыли: {take_profit_percent}%\n"
f"- Процент изменения цены для фиксации убытка: {stop_loss_percent}%\n\n"
f"- Процент изменения цены для фиксации прибыли: {take_profit_percent:.2f}%\n"
f"- Процент изменения цены для фиксации убытка: {stop_loss_percent:.2f}%\n\n"
f"- Комиссия биржи для расчета прибыли: {commission_fee_rus}\n\n",
reply_markup=kbi.risk_management,
)

View File

@@ -7,7 +7,7 @@ from aiogram.types import CallbackQuery
import app.telegram.keyboards.inline as kbi
import database.request as rq
from app.bybit.get_functions.get_positions import get_active_positions_by_symbol
from app.bybit.get_functions.get_positions import get_active_positions_by_symbol, get_active_orders_by_symbol
from app.bybit.open_positions import start_trading_cycle
from app.helper_functions import safe_float
from app.telegram.tasks.tasks import (
@@ -33,6 +33,7 @@ async def start_trading(callback_query: CallbackQuery, state: FSMContext) -> Non
"""
try:
await state.clear()
tg_id = callback_query.from_user.id
symbol = await rq.get_user_symbol(tg_id=callback_query.from_user.id)
deals = await get_active_positions_by_symbol(
tg_id=callback_query.from_user.id, symbol=symbol
@@ -46,7 +47,16 @@ async def start_trading(callback_query: CallbackQuery, state: FSMContext) -> Non
if safe_float(size) > 0:
await callback_query.answer(
text="У вас есть активная позиция",
text="У вас есть активная позиция по текущей паре",
)
return
orders = await get_active_orders_by_symbol(
tg_id=callback_query.from_user.id, symbol=symbol)
if orders is not None:
await callback_query.answer(
text="У вас есть активный ордер по текущей паре",
)
return
@@ -73,22 +83,29 @@ async def start_trading(callback_query: CallbackQuery, state: FSMContext) -> Non
symbol=symbol,
auto_trading=True,
)
await rq.set_total_fee_user_auto_trading(
tg_id=tg_id, symbol=symbol, total_fee=0
)
await rq.set_fee_user_auto_trading(
tg_id=tg_id, symbol=symbol, fee=0
)
res = await start_trading_cycle(
tg_id=callback_query.from_user.id,
)
error_messages = {
"Limit price is out min price": "Цена лимитного ордера меньше минимального",
"Limit price is out max price": "Цена лимитного ордера больше максимального",
"Limit price is out min price": "Цена лимитного ордера меньше допустимого",
"Limit price is out max price": "Цена лимитного ордера больше допустимого",
"Risk is too high for this trade": "Риск сделки превышает допустимый убыток",
"estimated will trigger liq": "Лимитный ордер может вызвать мгновенную ликвидацию. Проверьте параметры ордера.",
"ab not enough for new order": "Недостаточно средств для создания нового ордера",
"InvalidRequestError": "Произошла ошибка при запуске торговли.",
"Order does not meet minimum order value": "Сумма ордера не достаточна для запуска торговли",
"position idx not match position mode": "Ошибка режима позиции для данного инструмента",
"Qty invalid": "Некорректное значение ордера для данного инструмента",
"The number of contracts exceeds maximum limit allowed": "️️Количество контрактов превышает допустимое максимальное количество контрактов",
"The number of contracts exceeds minimum limit allowed": "Количество контрактов превышает допустимое минимальное количество контрактов",
"Order does not meet minimum order value": "Сумма ставки меньше допустимого для запуска торговли. "
"Увеличьте ставку, чтобы запустить торговлю",
"position idx not match position mode": "Измените режим позиции, чтобы запустить торговлю",
"Qty invalid": "Некорректное значение ставки для данного инструмента",
"The number of contracts exceeds maximum limit allowed": "Превышен максимальный лимит ставки",
"The number of contracts exceeds minimum limit allowed": "️️Лимит ставки меньше минимально допустимого",
}
if res == "OK":
@@ -112,7 +129,7 @@ async def start_trading(callback_query: CallbackQuery, state: FSMContext) -> Non
except Exception as e:
await callback_query.answer(text="Произошла ошибка при запуске торговли")
logger.error(
"Error processing command long for user %s: %s",
"Error processing command start_trading for user %s: %s",
callback_query.from_user.id,
e,
)

1
run.py
View File

@@ -46,7 +46,6 @@ async def main():
with contextlib.suppress(asyncio.CancelledError):
await ws_task
await tg_task
await web_socket.clear_user_sockets()
except Exception as e:
logger.error("Bot stopped with error: %s", e)