Files
stcs/app/services/Bybit/functions/functions.py
2025-08-22 16:47:33 +05:00

272 lines
11 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import asyncio
from aiogram import F, Router
from app.services.Bybit.functions import Futures, func_min_qty
from app.services.Bybit.functions.Futures import open_position, close_user_trade, get_active_positions, trading_cycle
from app.services.Bybit.functions.balance import get_balance
import app.telegram.Keyboards.inline_keyboards as inline_markup
import app.telegram.Keyboards.reply_keyboards as reply_markup
from pybit.unified_trading import HTTP
import app.telegram.database.requests as rq
from aiogram.types import Message, CallbackQuery
# FSM - Механизм состояния
from aiogram.fsm.state import State, StatesGroup
from aiogram.fsm.context import FSMContext
router_functions_bybit_trade = Router()
class state_update_symbol(StatesGroup):
symbol = State()
class state_update_entry_type(StatesGroup):
entry_type = State()
class TradeSetup(StatesGroup):
waiting_for_timer = State()
waiting_for_positive_percent = State()
@router_functions_bybit_trade.callback_query(F.data.in_(['clb_start_trading', 'clb_back_to_main', 'back_to_main']))
async def clb_start_bybit_trade_message(callback: CallbackQuery, state: FSMContext):
api = await rq.get_bybit_api_key(callback.from_user.id)
secret = await rq.get_bybit_secret_key(callback.from_user.id)
balance = await get_balance(callback.from_user.id, callback.message)
if balance:
symbol = await rq.get_symbol(callback.from_user.id)
text = f'''💎 Торговля на Bybit
⚖️ Ваш баланс (USDT): {balance}
📊 Текущая торговая пара: {symbol}
Как начать торговлю?
1⃣ Проверьте и тщательно настройте все параметры в вашем профиле.
2⃣ Нажмите ниже кнопку 'Указать торговую пару' и введите торговую пару заглавными буквами, без лишних символов (например: BTCUSDT).
'''
await callback.message.edit_text(text=text, parse_mode='html', reply_markup=inline_markup.trading_markup)
async def start_bybit_trade_message(message, state):
api = await rq.get_bybit_api_key(message.from_user.id)
secret = await rq.get_bybit_secret_key(message.from_user.id)
balance = await get_balance(message.from_user.id, message)
if balance:
symbol = await rq.get_symbol(message.from_user.id)
text = f'''💎 Торговля на Bybit
⚖️ Ваш баланс (USDT): {balance}
📊 Текущая торговая пара: {symbol}
Как начать торговлю?
1⃣ Проверьте и тщательно настройте все параметры в вашем профиле.
2⃣ Нажмите ниже кнопку 'Указать торговую пару' и введите торговую пару заглавными буквами, без лишних символов (например: BTCUSDT).
'''
await message.answer(text=text, parse_mode='html', reply_markup=inline_markup.trading_markup)
@router_functions_bybit_trade.callback_query(F.data == 'clb_update_trading_pair')
async def update_symbol_for_trade_message(callback: CallbackQuery, state: FSMContext):
await state.set_state(state_update_symbol.symbol)
await callback.message.answer(
text='Укажите торговую пару заглавными буквами без пробелов и лишних символов (пример: BTCUSDT): ')
@router_functions_bybit_trade.message(state_update_symbol.symbol)
async def update_symbol_for_trade(message: Message, state: FSMContext):
await state.update_data(symbol=message.text)
data = await state.get_data()
await message.answer('Пара была успешно обновлена')
await rq.update_symbol(message.from_user.id, data['symbol'])
await start_bybit_trade_message(message, state)
await state.clear()
@router_functions_bybit_trade.callback_query(F.data == 'clb_update_entry_type')
async def update_entry_type_message(callback: CallbackQuery, state: FSMContext):
await state.set_state(state_update_entry_type.entry_type)
await callback.message.answer("Выберите тип входа в позицию:", reply_markup=inline_markup.entry_order_type_markup)
await callback.answer()
@router_functions_bybit_trade.callback_query(lambda c: c.data and c.data.startswith('entry_order_type:'))
async def entry_order_type_callback(callback: CallbackQuery, state: FSMContext):
order_type = callback.data.split(':')[1]
if order_type not in ['Market', 'Limit']:
await callback.answer("Ошибка выбора", show_alert=True)
return
try:
await state.update_data(entry_order_type=order_type)
await rq.update_entry_order_type(callback.from_user.id, order_type)
await callback.answer("Тип входа в позицию был успешно обновлен")
except Exception as e:
await callback.message.answer("Произошла ошибка при обновлении типа входа в позицию")
await state.clear()
@router_functions_bybit_trade.callback_query(F.data == "clb_my_deals")
async def show_my_trades_callback(callback: CallbackQuery):
tg_id = callback.from_user.id
api_key = await rq.get_bybit_api_key(tg_id)
secret_key = await rq.get_bybit_secret_key(tg_id)
symbol = await rq.get_symbol(tg_id)
trades = await get_active_positions(callback.message, api_key, secret_key, symbol)
if not trades:
await callback.message.answer("Нет активных позиций.")
await callback.answer()
return
keyboard = inline_markup.create_trades_inline_keyboard(trades)
await callback.message.answer(
"Выберите сделку из списка:",
reply_markup=keyboard
)
await callback.answer()
@router_functions_bybit_trade.callback_query(lambda c: c.data and c.data.startswith('select_trade:'))
async def on_trade_selected(callback: CallbackQuery):
symbol = callback.data.split(':')[1]
tg_id = callback.from_user.id
api_key = await rq.get_bybit_api_key(tg_id)
secret_key = await rq.get_bybit_secret_key(tg_id)
positions = await get_active_positions(callback.message, api_key, secret_key, symbol)
# Если несколько позиций по символу, можно выбрать нужную или взять первую
if not positions:
await callback.message.answer("Позиция не найдена")
await callback.answer()
return
pos = positions[0]
symbol = pos.get('symbol')
side = pos.get('side')
entry_price = pos.get('entryPrice') # Цена открытия позиции
current_price = pos.get('price') # Текущая цена (если есть)
text = (f"Информация по позиции:\n"
f"Название: {symbol}\n"
f"Направление: {side}\n"
f"Цена покупки: {entry_price}\n"
f"Текущая цена: {current_price if current_price else 'N/A'}")
keyboard = inline_markup.create_close_deal_markup(symbol)
await callback.message.answer(text, reply_markup=keyboard)
await callback.answer()
@router_functions_bybit_trade.callback_query(lambda c: c.data and c.data.startswith("close_deal:"))
async def close_trade_callback(callback: CallbackQuery):
symbol = callback.data.split(':')[1]
tg_id = callback.from_user.id
result = await close_user_trade(tg_id, symbol)
if result:
await callback.message.answer(f"Сделка {symbol} успешно закрыта.")
else:
await callback.message.answer(f"Не удалось закрыть сделку {symbol}.")
await callback.answer()
@router_functions_bybit_trade.callback_query(F.data == "clb_start_chatbot_trading")
async def start_trading_process(callback: CallbackQuery, state: FSMContext):
tg_id = callback.from_user.id
message = callback.message
# Получаем настройки пользователя
data_main_stgs = await rq.get_user_main_settings(tg_id)
api_key = await rq.get_bybit_api_key(tg_id)
secret_key = await rq.get_bybit_secret_key(tg_id)
symbol = await rq.get_symbol(tg_id)
margin_mode = data_main_stgs.get('margin_type', 'Isolated')
trading_mode = data_main_stgs.get('trading_mode')
# Проверка API ключей
if not api_key or not secret_key:
await message.answer("❗️ У вас не настроены API ключи для Bybit.")
await callback.answer()
return
# Проверка режима торговли
if trading_mode not in ['Long', 'Short', 'Smart', 'Switch']:
await message.answer(f"❗️ Некорректный торговый режим: {trading_mode}")
await callback.answer()
return
# Проверка допустимости маржинального режима
if margin_mode not in ['Isolated', 'Cross']:
margin_mode = 'Isolated'
# Проверяем открытые позиции и маржинальный режим
client = HTTP(api_key=api_key, api_secret=secret_key)
try:
positions_resp = client.get_positions(category='linear', symbol=symbol)
positions = positions_resp.get('result', {}).get('list', [])
except Exception:
positions = []
for pos in positions:
size = pos.get('size')
existing_margin_mode = pos.get('margin_mode')
if size and float(size) > 0 and existing_margin_mode and existing_margin_mode != margin_mode:
await callback.answer(
f"⚠️ Маржинальный режим нельзя менять при открытой позиции "
f"(текущий режим: {existing_margin_mode})", show_alert=True)
return
# Определяем сторону для открытия позиции
if trading_mode == 'Long':
side = 'Buy'
elif trading_mode == 'Short':
side = 'Sell'
else:
await message.answer(f"Режим торговли '{trading_mode}' пока не поддерживается.")
await callback.answer()
return
# Сообщаем о начале торговли
await message.answer("Начинаю торговлю с использованием текущих настроек...")
# Открываем позицию (вызывает Futures.open_position)
await open_position(tg_id, message, side=side, margin_mode=margin_mode)
# Проверяем таймер и информируем пользователя
timer_minutes = await rq.get_user_timer(tg_id)
if timer_minutes and timer_minutes > 0:
await message.answer(f"Торговля будет работать по таймеру: {timer_minutes} мин.")
asyncio.create_task(trading_cycle(tg_id, message))
else:
await message.answer(
"Торговля начата без ограничения по времени. Для остановки нажмите кнопку 'Закрыть сделку'.",
reply_markup=inline_markup.create_close_deal_markup(symbol)
)
await callback.answer()