2
0
forked from kodorvan/stcs

Compare commits

...

10 Commits

Author SHA1 Message Date
algizn97
72ed35ccf8 The price of the trading pair has been removed, and the trade cancellation button has been removed. The text has been corrected 2025-10-10 14:00:58 +05:00
algizn97
b890df9af8 When choosing a coin, the leverage is set to the maximum possible for this coin, also SL in accordance with the leverage. The verification range from 1 to 100 has been removed, now the verification is within the acceptable values from the exchange 2025-10-10 14:00:42 +05:00
algizn97
e40fa91125 Added the ability to summarize all commissions within a series.Minor bugs have been fixed 2025-10-10 14:00:28 +05:00
algizn97
3ec9d00650 The currency of the coin is treason on USDT, unnecessary parameters are removed 2025-10-10 14:00:01 +05:00
algizn97
e792130332 Unnecessary buttons have been removed, the buttons of the trading mode and the direction of the first transaction of the series have been moved. 2025-10-10 13:59:47 +05:00
algizn97
1a1a5a727f When adjusting the leverage, the SL changes according to the criteria. In place of the position mode, there is now a trading mode. All unnecessary functions are also removed. 2025-10-10 13:59:24 +05:00
algizn97
9f9a79bf81 The function allows you to write the number 0 2025-10-10 13:59:04 +05:00
algizn97
58397c4723 The stop trading button has been removed. 2025-10-10 13:58:23 +05:00
algizn97
6bfb816d2a Fixed percentages of TP and SL from integers to floats 2025-10-10 13:57:29 +05:00
algizn97
fb82f365f2 The trading mode has been moved to the main settings, Position mode, limit order and conditional order have been removed. The number of bids has been renamed to the base rate. The choice of the direction of the first transaction has been moved to the main settings 2025-10-10 13:57:13 +05:00
12 changed files with 91 additions and 88 deletions

View File

@@ -16,9 +16,7 @@ logging.config.dictConfig(LOGGING_CONFIG)
logger = logging.getLogger("open_positions")
async def start_trading_cycle(
tg_id: int
) -> str | None:
async def start_trading_cycle(tg_id: int) -> str | None:
"""
Start trading cycle
:param tg_id: Telegram user ID
@@ -29,9 +27,7 @@ async def start_trading_cycle(
additional_data = await rq.get_user_additional_settings(tg_id=tg_id)
risk_management_data = await rq.get_user_risk_management(tg_id=tg_id)
commission_fee = risk_management_data.commission_fee
user_deals_data = await rq.get_user_deal_by_symbol(
tg_id=tg_id, symbol=symbol
)
user_deals_data = await rq.get_user_deal_by_symbol(tg_id=tg_id, symbol=symbol)
trade_mode = additional_data.trade_mode
switch_side = additional_data.switch_side
margin_type = additional_data.margin_type
@@ -107,7 +103,7 @@ async def start_trading_cycle(
leverage=leverage,
take_profit_percent=take_profit_percent,
stop_loss_percent=stop_loss_percent,
commission_fee_percent=total_commission
commission_fee_percent=total_commission,
)
if res == "OK":
@@ -125,7 +121,7 @@ async def start_trading_cycle(
max_bets_in_series=max_bets_in_series,
take_profit_percent=take_profit_percent,
stop_loss_percent=stop_loss_percent,
base_quantity=order_quantity
base_quantity=order_quantity,
)
return "OK"
return (
@@ -142,7 +138,7 @@ async def start_trading_cycle(
"position idx not match position mode",
"Qty invalid",
"The number of contracts exceeds maximum limit allowed",
"The number of contracts exceeds minimum limit allowed"
"The number of contracts exceeds minimum limit allowed",
}
else None
)
@@ -152,12 +148,12 @@ async def start_trading_cycle(
return None
async def trading_cycle(
tg_id: int, symbol: str, reverse_side: str
) -> str | None:
async def trading_cycle(tg_id: int, symbol: str, reverse_side: str) -> str | None:
try:
user_deals_data = await rq.get_user_deal_by_symbol(tg_id=tg_id, symbol=symbol)
user_auto_trading_data = await rq.get_user_auto_trading(tg_id=tg_id, symbol=symbol)
user_auto_trading_data = await rq.get_user_auto_trading(
tg_id=tg_id, symbol=symbol
)
total_fee = user_auto_trading_data.total_fee
trade_mode = user_deals_data.trade_mode
margin_type = user_deals_data.margin_type
@@ -188,9 +184,7 @@ async def trading_cycle(
if trade_mode == "Switch":
side = "Sell" if real_side == "Buy" else "Buy"
next_quantity = safe_float(order_quantity) * (
safe_float(martingale_factor)
)
next_quantity = safe_float(order_quantity) * (safe_float(martingale_factor))
current_step += 1
if max_bets_in_series < current_step:
@@ -206,7 +200,7 @@ async def trading_cycle(
leverage=leverage,
take_profit_percent=take_profit_percent,
stop_loss_percent=stop_loss_percent,
commission_fee_percent=total_fee
commission_fee_percent=total_fee,
)
if res == "OK":
@@ -224,7 +218,7 @@ async def trading_cycle(
max_bets_in_series=max_bets_in_series,
take_profit_percent=take_profit_percent,
stop_loss_percent=stop_loss_percent,
base_quantity=base_quantity
base_quantity=base_quantity,
)
return "OK"
@@ -255,7 +249,7 @@ async def open_positions(
leverage: str,
take_profit_percent: float,
stop_loss_percent: float,
commission_fee_percent: float
commission_fee_percent: float,
) -> str | None:
try:
client = await get_bybit_client(tg_id=tg_id)

View File

@@ -23,12 +23,12 @@ async def user_profile_bybit(tg_id: int, message: Message, state: FSMContext) ->
symbol = await rq.get_user_symbol(tg_id=tg_id)
await message.answer(
text=f"💎Ваш профиль:\n\n"
f"⚖️ Баланс: {float(balance):,.2f} USD\n"
f"📊Торговая пара: {symbol}\n\n"
f"Краткая инструкция:\n"
f"1. Укажите торговую пару (например: BTCUSDT).\n"
f"2. В настройках выставьте все необходимые параметры.\n"
f"3. Нажмите кнопку 'Начать торговлю'.\n",
f"⚖️ Баланс: {float(balance):,.2f} USD\n"
f"📊Торговая пара: {symbol}\n\n"
f"Краткая инструкция:\n"
f"1. Укажите торговую пару (например: BTCUSDT).\n"
f"2. В настройках выставьте все необходимые параметры.\n"
f"3. Нажмите кнопку 'Начать торговлю'.\n",
reply_markup=kbi.main_menu,
)
else:

View File

@@ -135,9 +135,9 @@ class TelegramMessageHandler:
user_symbols = user_auto_trading.symbol if user_auto_trading else None
if (
auto_trading
and safe_float(closed_size) > 0
and user_symbols is not None
auto_trading
and safe_float(closed_size) > 0
and user_symbols is not None
):
if safe_float(total_pnl) > 0:
profit_text = "📈 Прибыль достигнута\n"

View File

@@ -134,7 +134,7 @@ def check_limit_price(limit_price, min_price, max_price) -> str | None:
async def get_liquidation_price(
tg_id: int, symbol: str, entry_price: float, leverage: float
tg_id: int, symbol: str, entry_price: float, leverage: float
) -> tuple[float, float]:
"""
Function to get liquidation price
@@ -158,7 +158,7 @@ async def get_liquidation_price(
async def calculate_total_budget(
quantity, martingale_factor, max_steps, commission_fee_percent
quantity, martingale_factor, max_steps, commission_fee_percent
) -> float:
"""
Calculate the total budget for a series of trading steps.
@@ -174,7 +174,7 @@ async def calculate_total_budget(
"""
total = 0
for step in range(max_steps):
set_quantity = quantity * (martingale_factor**step)
set_quantity = quantity * (martingale_factor ** step)
if commission_fee_percent == 0:
# Commission fee is not added to the position size
r_quantity = set_quantity

View File

@@ -6,11 +6,10 @@ from aiogram.types import CallbackQuery, Message
import app.telegram.keyboards.inline as kbi
import database.request as rq
from app.bybit.get_functions.get_tickers import get_tickers
from app.bybit.get_functions.get_instruments_info import get_instruments_info
from app.bybit.get_functions.get_tickers import get_tickers
from app.bybit.profile_bybit import user_profile_bybit
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 safe_float
from app.telegram.states.states import ChangingTheSymbolState
@@ -99,7 +98,9 @@ async def set_symbol(message: Message, state: FSMContext) -> None:
)
return
instruments_info = await get_instruments_info(tg_id=message.from_user.id, symbol=symbol)
instruments_info = await get_instruments_info(
tg_id=message.from_user.id, symbol=symbol
)
max_leverage = instruments_info.get("leverageFilter").get("maxLeverage")
req = await rq.set_user_symbol(tg_id=message.from_user.id, symbol=symbol)
@@ -123,7 +124,8 @@ async def set_symbol(message: Message, state: FSMContext) -> None:
await rq.set_leverage(tg_id=message.from_user.id, leverage=str(max_leverage))
risk_percent = 100 / safe_float(max_leverage)
await rq.set_stop_loss_percent(
tg_id=message.from_user.id, stop_loss_percent=risk_percent)
tg_id=message.from_user.id, stop_loss_percent=risk_percent
)
await rq.set_trigger_price(tg_id=message.from_user.id, trigger_price=0)
await rq.set_order_quantity(tg_id=message.from_user.id, order_quantity=1.0)

View File

@@ -53,7 +53,7 @@ async def cmd_start(message: Message, state: FSMContext) -> None:
await rq.create_user_conditional_settings(tg_id=tg_id)
await message.answer(
text=f"Добро пожаловать, {full_name}!\n\n"
"Чат-робот для трейдинга - ваш надежный помощник для анализа рынка и принятия взвешенных решений.😉",
"Чат-робот для трейдинга - ваш надежный помощник для анализа рынка и принятия взвешенных решений.😉",
reply_markup=kbi.connect_the_platform,
)
logger.debug(
@@ -134,7 +134,7 @@ async def profile_bybit(message: Message, state: FSMContext) -> None:
@router_handlers_main.callback_query(F.data == "profile_bybit")
async def profile_bybit_callback(
callback_query: CallbackQuery, state: FSMContext
callback_query: CallbackQuery, state: FSMContext
) -> None:
"""
Handle callback query with data "profile_bybit".
@@ -279,10 +279,10 @@ async def cmd_help(message: Message, state: FSMContext) -> None:
await state.clear()
await message.answer(
text="Используйте одну из следующих команд:\n"
"/start - Запустить бота\n"
"/profile - Профиль\n"
"/bybit - Панель Bybit\n"
"/connect - Подключиться к платформе\n",
"/start - Запустить бота\n"
"/profile - Профиль\n"
"/bybit - Панель Bybit\n"
"/connect - Подключиться к платформе\n",
reply_markup=kbr.profile,
)
logger.debug(
@@ -378,4 +378,4 @@ async def cmd_cancel(callback_query: CallbackQuery, state: FSMContext) -> None:
e,
)
finally:
await state.clear()
await state.clear()

View File

@@ -21,7 +21,7 @@ router_additional_settings = Router(name="additional_settings")
@router_additional_settings.callback_query(F.data == "trade_mode")
async def settings_for_trade_mode(
callback_query: CallbackQuery, state: FSMContext
callback_query: CallbackQuery, state: FSMContext
) -> None:
"""
Handles the 'trade_mode' callback query.
@@ -40,9 +40,9 @@ async def settings_for_trade_mode(
await state.clear()
await callback_query.message.edit_text(
text="Выберите режим торговли:\n\n"
"Лонг - все сделки серии открываются на покупку.\n"
"Шорт - все сделки серии открываются на продажу.\n"
"Свитч - направление каждой сделки серии меняется по переменно.\n",
"Лонг - все сделки серии открываются на покупку.\n"
"Шорт - все сделки серии открываются на продажу.\n"
"Свитч - направление каждой сделки серии меняется по переменно.\n",
reply_markup=kbi.trade_mode,
)
logger.debug(
@@ -123,8 +123,8 @@ async def switch_side_start(callback_query: CallbackQuery, state: FSMContext) ->
await state.clear()
await callback_query.message.edit_text(
text="Выберите направление первой сделки серии:\n\n"
"По направлению - сделка открывается в направлении последней сделки предыдущей серии.\n"
"Противоположно - сделка открывается в противоположном направлении последней сделки предыдущей серии.\n",
"По направлению - сделка открывается в направлении последней сделки предыдущей серии.\n"
"Противоположно - сделка открывается в противоположном направлении последней сделки предыдущей серии.\n",
reply_markup=kbi.switch_side,
)
logger.debug(
@@ -142,7 +142,9 @@ async def switch_side_start(callback_query: CallbackQuery, state: FSMContext) ->
)
@router_additional_settings.callback_query(lambda c: c.data == "switch_direction" or c.data == "switch_opposite")
@router_additional_settings.callback_query(
lambda c: c.data == "switch_direction" or c.data == "switch_opposite"
)
async def switch_side_handler(callback_query: CallbackQuery, state: FSMContext) -> None:
"""
Handles callback queries related to switch side selection.
@@ -194,7 +196,7 @@ async def switch_side_handler(callback_query: CallbackQuery, state: FSMContext)
@router_additional_settings.callback_query(F.data == "margin_type")
async def settings_for_margin_type(
callback_query: CallbackQuery, state: FSMContext
callback_query: CallbackQuery, state: FSMContext
) -> None:
"""
Handles the 'margin_type' callback query.
@@ -213,8 +215,8 @@ async def settings_for_margin_type(
await state.clear()
await callback_query.message.edit_text(
text="Выберите тип маржи:\n\n"
"Примечание: Если у вас есть открытые позиции, то маржа примениться ко всем позициям",
reply_markup=kbi.margin_type
"Примечание: Если у вас есть открытые позиции, то маржа примениться ко всем позициям",
reply_markup=kbi.margin_type,
)
logger.debug(
"Command margin_type processed successfully for user: %s",
@@ -503,12 +505,12 @@ async def set_leverage_handler(message: Message, state: FSMContext) -> None:
if instruments_info is not None:
min_leverage = (
safe_float(instruments_info.get("leverageFilter").get("minLeverage"))
or 1
safe_float(instruments_info.get("leverageFilter").get("minLeverage"))
or 1
)
max_leverage = (
safe_float(instruments_info.get("leverageFilter").get("maxLeverage"))
or 100
safe_float(instruments_info.get("leverageFilter").get("maxLeverage"))
or 100
)
if leverage_float > max_leverage or leverage_float < min_leverage:
@@ -553,7 +555,8 @@ async def set_leverage_handler(message: Message, state: FSMContext) -> None:
)
risk_percent = 100 / safe_float(leverage_float)
await rq.set_stop_loss_percent(
tg_id=message.from_user.id, stop_loss_percent=risk_percent)
tg_id=message.from_user.id, stop_loss_percent=risk_percent
)
logger.info(
"User %s set leverage: %s", message.from_user.id, leverage_float
)
@@ -767,9 +770,14 @@ async def set_martingale_factor(message: Message, state: FSMContext) -> None:
martingale_factor_value_float = safe_float(martingale_factor_value)
if martingale_factor_value_float < 0.1 or martingale_factor_value_float > 10:
await message.answer(text="Ошибка: коэффициент мартингейла должен быть в диапазоне от 0.1 до 10")
logger.debug("User %s input invalid (not in range 0.1 to 10): %s", message.from_user.id,
martingale_factor_value_float)
await message.answer(
text="Ошибка: коэффициент мартингейла должен быть в диапазоне от 0.1 до 10"
)
logger.debug(
"User %s input invalid (not in range 0.1 to 10): %s",
message.from_user.id,
martingale_factor_value_float,
)
return
req = await rq.set_martingale_factor(
@@ -878,7 +886,10 @@ async def set_max_bets_in_series(message: Message, state: FSMContext) -> None:
)
return
if safe_float(max_bets_in_series_value) < 1 or safe_float(max_bets_in_series_value) > 100:
if (
safe_float(max_bets_in_series_value) < 1
or safe_float(max_bets_in_series_value) > 100
):
await message.answer(
"Ошибка: число должно быть в диапазоне от 1 до 100.",
reply_markup=kbi.back_to_additional_settings,

View File

@@ -98,7 +98,10 @@ async def set_take_profit_percent(message: Message, state: FSMContext) -> None:
)
return
if safe_float(take_profit_percent_value) < 1 or safe_float(take_profit_percent_value) > 100:
if (
safe_float(take_profit_percent_value) < 1
or safe_float(take_profit_percent_value) > 100
):
await message.answer(
text="Ошибка: введите число от 1 до 100.",
reply_markup=kbi.back_to_risk_management,
@@ -219,7 +222,10 @@ async def set_stop_loss_percent(message: Message, state: FSMContext) -> None:
)
return
if safe_float(stop_loss_percent_value) < 1 or safe_float(stop_loss_percent_value) > 100:
if (
safe_float(stop_loss_percent_value) < 1
or safe_float(stop_loss_percent_value) > 100
):
await message.answer(
text="Ошибка: введите число от 1 до 100.",
reply_markup=kbi.back_to_risk_management,
@@ -232,7 +238,8 @@ async def set_stop_loss_percent(message: Message, state: FSMContext) -> None:
return
req = await rq.set_stop_loss_percent(
tg_id=message.from_user.id, stop_loss_percent=safe_float(stop_loss_percent_value)
tg_id=message.from_user.id,
stop_loss_percent=safe_float(stop_loss_percent_value),
)
if req:

View File

@@ -7,7 +7,6 @@ from aiogram.types import CallbackQuery
import app.telegram.keyboards.inline as kbi
import database.request as rq
from app.bybit import get_bybit_client
from app.helper_functions import calculate_total_budget, safe_float
from logger_helper.logger_helper import LOGGING_CONFIG

View File

@@ -10,10 +10,7 @@ import database.request as rq
from app.bybit.get_functions.get_positions import get_active_positions_by_symbol
from app.bybit.open_positions import start_trading_cycle
from app.helper_functions import safe_float
from app.telegram.tasks.tasks import (
add_start_task_merged,
cancel_start_task_merged
)
from app.telegram.tasks.tasks import add_start_task_merged, cancel_start_task_merged
from logger_helper.logger_helper import LOGGING_CONFIG
logging.config.dictConfig(LOGGING_CONFIG)
@@ -120,9 +117,7 @@ async def start_trading(callback_query: CallbackQuery, state: FSMContext) -> Non
logger.error("Cancelled timer for user %s", callback_query.from_user.id)
@router_start_trading.callback_query(
lambda c: c.data == "cancel_timer_merged"
)
@router_start_trading.callback_query(lambda c: c.data == "cancel_timer_merged")
async def cancel_start_trading(
callback_query: CallbackQuery, state: FSMContext
) -> None:

View File

@@ -94,4 +94,4 @@ async def cancel_stop_trading(callback_query: CallbackQuery, state: FSMContext):
"Error processing command cancel_timer_stop for user %s: %s",
callback_query.from_user.id,
e,
)
)

View File

@@ -61,8 +61,7 @@ main_settings = InlineKeyboardMarkup(
# additional_settings
def get_additional_settings_keyboard(mode: str
) -> InlineKeyboardMarkup:
def get_additional_settings_keyboard(mode: str) -> InlineKeyboardMarkup:
"""
Create keyboard for additional settings
:param mode: Trade mode
@@ -77,23 +76,23 @@ def get_additional_settings_keyboard(mode: str
InlineKeyboardButton(
text="Размер кредитного плеча", callback_data="leverage"
),
InlineKeyboardButton(
text="Базовая ставка", callback_data="order_quantity"),
InlineKeyboardButton(text="Базовая ставка", callback_data="order_quantity"),
],
[
InlineKeyboardButton(
text="Коэффициент мартингейла", callback_data="martingale_factor"
),
InlineKeyboardButton(text="Триггер цена", callback_data="trigger_price"
),
InlineKeyboardButton(text="Триггер цена", callback_data="trigger_price"),
],
]
if mode == "Switch":
buttons.append(
[InlineKeyboardButton(text="Направление первой сделки", callback_data="switch_side_start")]
[
InlineKeyboardButton(
text="Направление первой сделки", callback_data="switch_side_start"
)
]
)
buttons.append(
@@ -117,9 +116,7 @@ def get_additional_settings_keyboard(mode: str
trade_mode = InlineKeyboardMarkup(
inline_keyboard=[
[
InlineKeyboardButton(
text="Лонг", callback_data="Long"
),
InlineKeyboardButton(text="Лонг", callback_data="Long"),
InlineKeyboardButton(text="Шорт", callback_data="Short"),
InlineKeyboardButton(text="Свитч", callback_data="Switch"),
],
@@ -188,9 +185,7 @@ risk_management = InlineKeyboardMarkup(
InlineKeyboardButton(
text="Тейк-профит", callback_data="take_profit_percent"
),
InlineKeyboardButton(
text="Стоп-лосс", callback_data="stop_loss_percent"
),
InlineKeyboardButton(text="Стоп-лосс", callback_data="stop_loss_percent"),
],
[InlineKeyboardButton(text="Комиссия биржи", callback_data="commission_fee")],
[