Updated timer functions

This commit is contained in:
algizn97
2025-08-25 17:07:41 +05:00
parent dd63f4c015
commit 39b8d17498
2 changed files with 451 additions and 205 deletions

View File

@@ -1,15 +1,18 @@
import asyncio
import logging.config
from aiogram import F, Router
from logger_helper.logger_helper import LOGGING_CONFIG
from app.services.Bybit.functions import Futures, min_qty
from app.services.Bybit.functions.Futures import open_position, close_user_trade, get_active_positions, trading_cycle
from app.services.Bybit.functions.Futures import close_user_trade, get_active_positions, close_trade_after_delay, \
active_close_tasks, active_start_tasks, trading_cycle, open_position
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
from app.services.Bybit.functions.price_symbol import get_price
# FSM - Механизм состояния
from aiogram.fsm.state import State, StatesGroup
@@ -35,16 +38,19 @@ class TradeSetup(StatesGroup):
waiting_for_timer = State()
waiting_for_positive_percent = State()
class state_limit_price(StatesGroup):
price = State()
class CloseTradeTimerState(StatesGroup):
waiting_for_delay = 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)
price = await get_price(callback.from_user.id)
if balance:
symbol = await rq.get_symbol(callback.from_user.id)
@@ -53,6 +59,7 @@ async def clb_start_bybit_trade_message(callback: CallbackQuery, state: FSMConte
⚖️ Ваш баланс (USDT): {balance}
📊 Текущая торговая пара: {symbol}
$$$ Цена: {price}
Как начать торговлю?
@@ -63,10 +70,9 @@ async def clb_start_bybit_trade_message(callback: CallbackQuery, state: FSMConte
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)
async def start_bybit_trade_message(message: Message, state: FSMContext):
balance = await get_balance(message.from_user.id, message)
price = await get_price(message.from_user.id)
if balance:
symbol = await rq.get_symbol(message.from_user.id)
@@ -75,6 +81,7 @@ async def start_bybit_trade_message(message, state):
⚖️ Ваш баланс (USDT): {balance}
📊 Текущая торговая пара: {symbol}
$$$ Цена: {price}
Как начать торговлю?
@@ -138,7 +145,7 @@ async def entry_order_type_callback(callback: CallbackQuery, state: FSMContext):
await state.update_data(entry_order_type=order_type)
await rq.update_entry_order_type(callback.from_user.id, order_type)
await callback.message.answer(f"Выбран тип входа в позицию: {order_type}",
reply_markup=inline_markup.start_trading_markup)
reply_markup=inline_markup.start_trading_markup)
await callback.answer()
except Exception as e:
logger.error(f"Произошла ошибка при обновлении типа входа в позицию: {e}")
@@ -152,14 +159,14 @@ async def set_limit_price(message: Message, state: FSMContext):
try:
price = float(message.text)
if price <= 0:
await message.answer("Цена должна быть положительным числом. Попробуйте снова.", reply_markup=inline_markup.cancel)
await message.answer("Цена должна быть положительным числом. Попробуйте снова.",
reply_markup=inline_markup.cancel)
return
except ValueError:
await message.answer("Некорректный формат цены. Введите число.", reply_markup=inline_markup.cancel)
return
await state.update_data(entry_order_type='Limit', limit_price=price)
data = await state.get_data()
await rq.update_entry_order_type(message.from_user.id, 'Limit')
await rq.update_limit_price(message.from_user.id, price)
@@ -168,82 +175,61 @@ async def set_limit_price(message: Message, state: FSMContext):
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
await callback.answer() # сразу отвечаем Telegram, освобождаем callback
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)
async def process():
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)
trades = await get_active_positions(callback.message, api_key, secret_key, symbol)
trades = await get_active_positions(callback.message, api_key, secret_key)
if not trades:
await callback.message.answer("Нет активных позиций.")
await callback.answer()
return
if not trades:
await callback.message.answer("Нет активных позиций.")
return
keyboard = inline_markup.create_trades_inline_keyboard(trades)
keyboard = inline_markup.create_trades_inline_keyboard(trades)
await callback.message.answer(
"Выберите сделку из списка:",
reply_markup=keyboard
)
await callback.answer()
await callback.message.answer(
"Выберите сделку из списка:",
reply_markup=keyboard
)
asyncio.create_task(process())
@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()
async def process():
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)
@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
positions = await get_active_positions(callback.message, api_key, secret_key)
result = await close_user_trade(tg_id, symbol)
if not positions:
await callback.message.answer("Позиция не найдена")
return
if result:
await callback.message.answer(f"Сделка {symbol} успешно закрыта.")
else:
await callback.message.answer(f"Не удалось закрыть сделку {symbol}.")
pos = positions[0]
text = (f"Информация по позиции:\n"
f"Название: {pos.get('symbol')}\n"
f"Направление: {pos.get('side')}\n"
f"Цена покупки: {pos.get('entryPrice')}\n"
f"Текущая цена: {pos.get('price', 'N/A')}")
await callback.answer()
keyboard = inline_markup.create_close_deal_markup(pos.get('symbol'))
await callback.message.answer(text, reply_markup=keyboard)
asyncio.create_task(process())
@router_functions_bybit_trade.callback_query(F.data == "clb_start_chatbot_trading")
async def start_trading_process(callback: CallbackQuery, state: FSMContext):
async def start_trading_process(callback: CallbackQuery):
tg_id = callback.from_user.id
message = callback.message
@@ -302,31 +288,122 @@ async def start_trading_process(callback: CallbackQuery, state: FSMContext):
# Сообщаем о начале торговли
await message.answer("Начинаю торговлю с использованием текущих настроек...")
# Открываем позицию (вызывает Futures.open_position)
success = await open_position(tg_id, message, side=side, margin_mode=margin_mode)
if not success:
await message.answer('⚠️ Ошибка при совершении сделки', reply_markup=inline_markup.back_to_main)
return
# Проверяем таймер и информируем пользователя
timer_data = await rq.get_user_timer(tg_id)
timer_minutes = timer_data.get('timer') if isinstance(timer_data, dict) else timer_data
if timer_minutes and timer_minutes > 0:
await message.answer(f"Торговля будет работать по таймеру: {timer_minutes} мин.")
asyncio.create_task(trading_cycle(tg_id, message))
if isinstance(timer_data, dict):
timer_minute = timer_data.get('timer_minutes', 0)
else:
await message.answer(
"Торговля начата без ограничения по времени. Для остановки нажмите кнопку 'Закрыть сделку'.",
reply_markup=inline_markup.create_close_deal_markup(symbol)
)
timer_minute = timer_data or 0
logger.info(f"Timer minutes for user {tg_id}: {timer_minute}")
symbol = await rq.get_symbol(tg_id)
if timer_minute > 0:
old_task = active_start_tasks.get(tg_id)
if old_task:
old_task.cancel()
# можно ждать завершения старой задачи, если в async функции
task = asyncio.create_task(trading_cycle(tg_id, message, symbol))
active_start_tasks[tg_id] = task
await message.answer(f"Торговля начнётся через {timer_minute} мин. Для отмены нажмите кнопку ниже.",
reply_markup=inline_markup.cancel_start_markup)
await rq.update_user_timer(tg_id, minutes=0)
else:
await open_position(tg_id, message, side=side, margin_mode=margin_mode)
await callback.answer()
@router_functions_bybit_trade.callback_query(F.data == "clb_stop_timer")
async def cancel_start_callback(callback: CallbackQuery):
tg_id = callback.from_user.id
task = active_start_tasks.get(tg_id)
if task:
task.cancel()
del active_start_tasks[tg_id]
await callback.message.answer("Торговля по таймеру отменена.", reply_markup=inline_markup.back_to_main)
else:
await callback.message.answer("Нет активности для отмены.", reply_markup=inline_markup.back_to_main)
await callback.answer()
@router_functions_bybit_trade.callback_query(F.data == "clb_stop_timer")
async def cancel_start_callback(callback: CallbackQuery):
tg_id = callback.from_user.id
task = active_close_tasks.get(tg_id)
if task:
task.cancel()
del active_close_tasks[tg_id]
await callback.message.answer("Таймер отменен.", reply_markup=inline_markup.back_to_main)
else:
await callback.message.answer("Нет активности для отмены.", reply_markup=inline_markup.back_to_main)
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} успешно закрыта.", reply_markup=inline_markup.back_to_main)
else:
await callback.message.answer(f"Не удалось закрыть сделку {symbol}.")
await callback.answer()
@router_functions_bybit_trade.callback_query(lambda c: c.data and c.data.startswith("close_deal_by_timer:"))
async def ask_close_delay(callback: CallbackQuery, state: FSMContext):
symbol = callback.data.split(":")[1]
await state.update_data(symbol=symbol)
await state.set_state(CloseTradeTimerState.waiting_for_delay)
await callback.message.answer("Введите задержку в минутах до закрытия сделки (например, 60):")
await callback.answer()
@router_functions_bybit_trade.message(CloseTradeTimerState.waiting_for_delay)
async def process_close_delay(message: Message, state: FSMContext):
try:
delay_minutes = int(message.text.strip())
if delay_minutes <= 0:
await message.answer("Введите положительное число.")
return
except ValueError:
await message.answer("Некорректный ввод. Введите число в минутах.")
return
data = await state.get_data()
symbol = data.get("symbol")
tg_id = message.from_user.id
delay = delay_minutes * 60
# Отменяем предыдущую задачу, если есть
if tg_id in active_close_tasks:
active_close_tasks[tg_id].cancel()
task = asyncio.create_task(close_trade_after_delay(tg_id, message, symbol, delay))
active_close_tasks[tg_id] = task
await message.answer(f"Закрытие сделки {symbol} запланировано через {delay} секунд.",
reply_markup=inline_markup.cancel_start_markup)
await state.clear()
@router_functions_bybit_trade.callback_query(F.data == "clb_change_martingale_reset")
async def reset_martingale(callback: CallbackQuery):
await callback.answer()
tg_id = callback.from_user.id
await rq.update_martingale_step(tg_id, 0)
await callback.message.answer("Сброс шагов мартингейла выполнен. Торговля начнется заново с начального объема.",
reply_markup=inline_markup.back_to_main)
@router_functions_bybit_trade.callback_query(F.data == "clb_cancel")
async def cancel(callback: CallbackQuery, state: FSMContext):
await state.clear()
await callback.message.answer("Отменено!", reply_markup=inline_markup.back_to_main)
await callback.answer()
await callback.answer()