2
0
forked from kodorvan/stcs

Compare commits

...

10 Commits

Author SHA1 Message Date
algizn97
42c4660fe3 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 13:31:52 +05:00
algizn97
fe030baef5 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 13:30:32 +05:00
algizn97
9d06412605 Added the ability to summarize all commissions within a series.Minor bugs have been fixed 2025-10-10 13:28:12 +05:00
algizn97
9c1f289870 The currency of the coin is treason on USDT, unnecessary parameters are removed 2025-10-10 13:26:56 +05:00
algizn97
3533e7e99a 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:25:30 +05:00
algizn97
8114533475 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:24:32 +05:00
algizn97
fcdc9d7483 The function allows you to write the number 0 2025-10-10 13:22:13 +05:00
algizn97
aa9f04c27e The stop trading button has been removed. 2025-10-10 13:21:15 +05:00
algizn97
89ab106992 Fixed percentages of TP and SL from integers to floats 2025-10-10 13:20:24 +05:00
algizn97
ebe2d58975 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:18:43 +05:00
12 changed files with 381 additions and 1727 deletions

View File

@@ -1,89 +1,113 @@
import logging.config import logging.config
import math
from pybit.exceptions import InvalidRequestError from pybit.exceptions import InvalidRequestError
import database.request as rq import database.request as rq
from app.bybit import get_bybit_client from app.bybit import get_bybit_client
from app.bybit.get_functions.get_balance import get_balance
from app.bybit.get_functions.get_instruments_info import get_instruments_info 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.get_functions.get_tickers import get_tickers
from app.bybit.logger_bybit.logger_bybit import LOGGING_CONFIG from app.bybit.logger_bybit.logger_bybit import LOGGING_CONFIG
from app.bybit.set_functions.set_leverage import ( from app.bybit.set_functions.set_leverage import set_leverage
set_leverage,
set_leverage_to_buy_and_sell,
)
from app.bybit.set_functions.set_margin_mode import set_margin_mode 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
from app.helper_functions import check_limit_price, get_liquidation_price, safe_float
logging.config.dictConfig(LOGGING_CONFIG) logging.config.dictConfig(LOGGING_CONFIG)
logger = logging.getLogger("open_positions") logger = logging.getLogger("open_positions")
async def start_trading_cycle( async def start_trading_cycle(
tg_id: int, side: str, switch_side_mode: bool tg_id: int
) -> str | None: ) -> str | None:
""" """
Start trading cycle Start trading cycle
:param tg_id: Telegram user ID :param tg_id: Telegram user ID
:param side: Buy or Sell
:param switch_side_mode: switch_side_mode
""" """
try: try:
client = await get_bybit_client(tg_id=tg_id)
symbol = await rq.get_user_symbol(tg_id=tg_id) symbol = await rq.get_user_symbol(tg_id=tg_id)
additional_data = await rq.get_user_additional_settings(tg_id=tg_id) 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) 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
)
trade_mode = additional_data.trade_mode trade_mode = additional_data.trade_mode
switch_side = additional_data.switch_side
margin_type = additional_data.margin_type margin_type = additional_data.margin_type
leverage = additional_data.leverage leverage = additional_data.leverage
leverage_to_buy = additional_data.leverage_to_buy
leverage_to_sell = additional_data.leverage_to_sell
order_type = additional_data.order_type
conditional_order_type = additional_data.conditional_order_type
order_quantity = additional_data.order_quantity order_quantity = additional_data.order_quantity
limit_price = additional_data.limit_price
trigger_price = additional_data.trigger_price trigger_price = additional_data.trigger_price
martingale_factor = additional_data.martingale_factor martingale_factor = additional_data.martingale_factor
max_bets_in_series = additional_data.max_bets_in_series max_bets_in_series = additional_data.max_bets_in_series
take_profit_percent = risk_management_data.take_profit_percent take_profit_percent = risk_management_data.take_profit_percent
stop_loss_percent = risk_management_data.stop_loss_percent stop_loss_percent = risk_management_data.stop_loss_percent
max_risk_percent = risk_management_data.max_risk_percent
mode = 3 if trade_mode == "Both_Sides" else 0 get_side = "Buy"
await set_switch_position_mode(tg_id=tg_id, symbol=symbol, mode=mode)
await set_margin_mode(tg_id=tg_id, margin_mode=margin_type) if user_deals_data:
if trade_mode == "Both_Sides" and margin_type == "ISOLATED_MARGIN": get_side = user_deals_data.last_side or "Buy"
await set_leverage_to_buy_and_sell(
tg_id=tg_id, if trade_mode == "Switch":
symbol=symbol, if switch_side == "По направлению":
leverage_to_buy=leverage_to_buy, side = get_side
leverage_to_sell=leverage_to_sell, else:
) if get_side == "Buy":
side = "Sell"
else:
side = "Buy"
else: else:
await set_leverage( if trade_mode == "Long":
tg_id=tg_id, side = "Buy"
symbol=symbol, else:
leverage=leverage, side = "Sell"
# Get fee rates
fee_info = client.get_fee_rates(category="linear", symbol=symbol)
# Check if commission fee is enabled
commission_fee_percent = 0.0
if commission_fee == "Yes_commission_fee":
commission_fee_percent = safe_float(
fee_info["result"]["list"][0]["takerFeeRate"]
) )
get_ticker = await get_tickers(tg_id, symbol=symbol)
price_symbol = safe_float(get_ticker.get("lastPrice")) or 0
instruments_info = await get_instruments_info(tg_id=tg_id, symbol=symbol)
qty_step_str = instruments_info.get("lotSizeFilter").get("qtyStep")
qty_step = safe_float(qty_step_str)
qty = safe_float(order_quantity) / safe_float(price_symbol)
decimals = abs(int(round(math.log10(qty_step))))
qty_formatted = math.floor(qty / qty_step) * qty_step
qty_formatted = round(qty_formatted, decimals)
if trigger_price > 0:
po_trigger_price = str(trigger_price)
else:
po_trigger_price = None
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_margin_mode(tg_id=tg_id, margin_mode=margin_type)
await set_leverage(
tg_id=tg_id,
symbol=symbol,
leverage=leverage,
)
res = await open_positions( res = await open_positions(
tg_id=tg_id, tg_id=tg_id,
symbol=symbol, symbol=symbol,
side=side, side=side,
order_type=order_type,
conditional_order_type=conditional_order_type,
order_quantity=order_quantity, order_quantity=order_quantity,
limit_price=limit_price,
trigger_price=trigger_price, trigger_price=trigger_price,
trade_mode=trade_mode,
margin_type=margin_type, margin_type=margin_type,
leverage=leverage, leverage=leverage,
leverage_to_buy=leverage_to_buy,
leverage_to_sell=leverage_to_sell,
take_profit_percent=take_profit_percent, take_profit_percent=take_profit_percent,
stop_loss_percent=stop_loss_percent, stop_loss_percent=stop_loss_percent,
max_risk_percent=max_risk_percent, commission_fee_percent=total_commission
) )
if res == "OK": if res == "OK":
@@ -95,19 +119,13 @@ async def start_trading_cycle(
trade_mode=trade_mode, trade_mode=trade_mode,
margin_type=margin_type, margin_type=margin_type,
leverage=leverage, leverage=leverage,
leverage_to_buy=leverage_to_buy,
leverage_to_sell=leverage_to_sell,
order_type="Market",
conditional_order_type=conditional_order_type,
order_quantity=order_quantity, order_quantity=order_quantity,
limit_price=limit_price,
trigger_price=trigger_price, trigger_price=trigger_price,
martingale_factor=martingale_factor, martingale_factor=martingale_factor,
max_bets_in_series=max_bets_in_series, max_bets_in_series=max_bets_in_series,
take_profit_percent=take_profit_percent, take_profit_percent=take_profit_percent,
stop_loss_percent=stop_loss_percent, stop_loss_percent=stop_loss_percent,
max_risk_percent=max_risk_percent, base_quantity=order_quantity
switch_side_mode=switch_side_mode,
) )
return "OK" return "OK"
return ( return (
@@ -124,6 +142,7 @@ async def start_trading_cycle(
"position idx not match position mode", "position idx not match position mode",
"Qty invalid", "Qty invalid",
"The number of contracts exceeds maximum limit allowed", "The number of contracts exceeds maximum limit allowed",
"The number of contracts exceeds minimum limit allowed"
} }
else None else None
) )
@@ -134,43 +153,30 @@ async def start_trading_cycle(
async def trading_cycle( async def trading_cycle(
tg_id: int, symbol: str, reverse_side: str, size: str tg_id: int, symbol: str, reverse_side: str
) -> str | None: ) -> str | None:
try: try:
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)
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 trade_mode = user_deals_data.trade_mode
order_type = user_deals_data.order_type
conditional_order_type = user_deals_data.conditional_order_type
margin_type = user_deals_data.margin_type margin_type = user_deals_data.margin_type
leverage = user_deals_data.leverage leverage = user_deals_data.leverage
leverage_to_buy = user_deals_data.leverage_to_buy trigger_price = 0
leverage_to_sell = user_deals_data.leverage_to_sell
limit_price = user_deals_data.limit_price
trigger_price = user_deals_data.trigger_price
take_profit_percent = user_deals_data.take_profit_percent take_profit_percent = user_deals_data.take_profit_percent
stop_loss_percent = user_deals_data.stop_loss_percent stop_loss_percent = user_deals_data.stop_loss_percent
max_risk_percent = user_deals_data.max_risk_percent
max_bets_in_series = user_deals_data.max_bets_in_series max_bets_in_series = user_deals_data.max_bets_in_series
martingale_factor = user_deals_data.martingale_factor martingale_factor = user_deals_data.martingale_factor
current_step = user_deals_data.current_step current_step = user_deals_data.current_step
switch_side_mode = user_deals_data.switch_side_mode order_quantity = user_deals_data.order_quantity
base_quantity = user_deals_data.base_quantity
mode = 3 if trade_mode == "Both_Sides" else 0
await set_switch_position_mode(tg_id=tg_id, symbol=symbol, mode=mode)
await set_margin_mode(tg_id=tg_id, margin_mode=margin_type) await set_margin_mode(tg_id=tg_id, margin_mode=margin_type)
if trade_mode == "Both_Sides" and margin_type == "ISOLATED_MARGIN": await set_leverage(
await set_leverage_to_buy_and_sell( tg_id=tg_id,
tg_id=tg_id, symbol=symbol,
symbol=symbol, leverage=leverage,
leverage_to_buy=leverage_to_buy, )
leverage_to_sell=leverage_to_sell,
)
else:
await set_leverage(
tg_id=tg_id,
symbol=symbol,
leverage=leverage,
)
if reverse_side == "Buy": if reverse_side == "Buy":
real_side = "Sell" real_side = "Sell"
@@ -179,11 +185,11 @@ async def trading_cycle(
side = real_side side = real_side
if switch_side_mode: if trade_mode == "Switch":
side = "Sell" if real_side == "Buy" else "Buy" side = "Sell" if real_side == "Buy" else "Buy"
next_quantity = safe_float(size) * ( next_quantity = safe_float(order_quantity) * (
safe_float(martingale_factor) ** current_step safe_float(martingale_factor)
) )
current_step += 1 current_step += 1
@@ -194,19 +200,13 @@ async def trading_cycle(
tg_id=tg_id, tg_id=tg_id,
symbol=symbol, symbol=symbol,
side=side, side=side,
order_type="Market",
conditional_order_type=conditional_order_type,
order_quantity=next_quantity, order_quantity=next_quantity,
limit_price=limit_price,
trigger_price=trigger_price, trigger_price=trigger_price,
trade_mode=trade_mode,
margin_type=margin_type, margin_type=margin_type,
leverage=leverage, leverage=leverage,
leverage_to_buy=leverage_to_buy,
leverage_to_sell=leverage_to_sell,
take_profit_percent=take_profit_percent, take_profit_percent=take_profit_percent,
stop_loss_percent=stop_loss_percent, stop_loss_percent=stop_loss_percent,
max_risk_percent=max_risk_percent, commission_fee_percent=total_fee
) )
if res == "OK": if res == "OK":
@@ -218,19 +218,13 @@ async def trading_cycle(
trade_mode=trade_mode, trade_mode=trade_mode,
margin_type=margin_type, margin_type=margin_type,
leverage=leverage, leverage=leverage,
leverage_to_buy=leverage_to_buy,
leverage_to_sell=leverage_to_sell,
order_type=order_type,
conditional_order_type=conditional_order_type,
order_quantity=next_quantity, order_quantity=next_quantity,
limit_price=limit_price,
trigger_price=trigger_price, trigger_price=trigger_price,
martingale_factor=martingale_factor, martingale_factor=martingale_factor,
max_bets_in_series=max_bets_in_series, max_bets_in_series=max_bets_in_series,
take_profit_percent=take_profit_percent, take_profit_percent=take_profit_percent,
stop_loss_percent=stop_loss_percent, stop_loss_percent=stop_loss_percent,
max_risk_percent=max_risk_percent, base_quantity=base_quantity
switch_side_mode=switch_side_mode,
) )
return "OK" return "OK"
@@ -255,123 +249,56 @@ async def open_positions(
tg_id: int, tg_id: int,
side: str, side: str,
symbol: str, symbol: str,
order_type: str,
conditional_order_type: str,
order_quantity: float, order_quantity: float,
limit_price: float,
trigger_price: float, trigger_price: float,
trade_mode: str,
margin_type: str, margin_type: str,
leverage: str, leverage: str,
leverage_to_buy: str,
leverage_to_sell: str,
take_profit_percent: float, take_profit_percent: float,
stop_loss_percent: float, stop_loss_percent: float,
max_risk_percent: float, commission_fee_percent: float
) -> str | None: ) -> str | None:
try: try:
client = await get_bybit_client(tg_id=tg_id) client = await get_bybit_client(tg_id=tg_id)
risk_management_data = await rq.get_user_risk_management(tg_id=tg_id)
commission_fee = risk_management_data.commission_fee
wallet = await get_balance(tg_id=tg_id)
user_balance = wallet.get("totalWalletBalance", 0)
instruments_resp = await get_instruments_info(tg_id=tg_id, symbol=symbol)
get_order_prices = instruments_resp.get("priceFilter")
min_price = safe_float(get_order_prices.get("minPrice"))
max_price = safe_float(get_order_prices.get("maxPrice"))
get_ticker = await get_tickers(tg_id, symbol=symbol) get_ticker = await get_tickers(tg_id, symbol=symbol)
price_symbol = safe_float(get_ticker.get("lastPrice")) or 0 price_symbol = safe_float(get_ticker.get("lastPrice")) or 0
instruments_info = await get_instruments_info(tg_id=tg_id, symbol=symbol)
qty_step_str = instruments_info.get("lotSizeFilter").get("qtyStep")
qty_step = safe_float(qty_step_str)
qty = safe_float(order_quantity) / safe_float(price_symbol)
decimals = abs(int(round(math.log10(qty_step))))
qty_formatted = math.floor(qty / qty_step) * qty_step
qty_formatted = round(qty_formatted, decimals)
if order_type == "Conditional": if trigger_price > 0:
po_trigger_price = str(trigger_price) po_trigger_price = str(trigger_price)
trigger_direction = 1 if trigger_price > price_symbol else 2 trigger_direction = 1 if trigger_price > price_symbol else 2
if conditional_order_type == "Limit":
error = check_limit_price(limit_price, min_price, max_price)
if error in {
"Limit price is out min price",
"Limit price is out max price",
}:
return error
order_type = "Limit"
price_for_calc = limit_price
tpsl_mode = "Partial"
else:
order_type = "Market"
price_for_calc = trigger_price
tpsl_mode = "Full"
else: else:
if order_type == "Limit":
error = check_limit_price(limit_price, min_price, max_price)
if error in {
"Limit price is out min price",
"Limit price is out max price",
}:
return error
price_for_calc = limit_price
tpsl_mode = "Partial"
else:
order_type = "Market"
price_for_calc = price_symbol
tpsl_mode = "Full"
po_trigger_price = None po_trigger_price = None
trigger_direction = None trigger_direction = None
if trade_mode == "Both_Sides": get_leverage = safe_float(leverage)
po_position_idx = 1 if side == "Buy" else 2
if margin_type == "ISOLATED_MARGIN":
get_leverage = safe_float(
leverage_to_buy if side == "Buy" else leverage_to_sell
)
else:
get_leverage = safe_float(leverage)
else:
po_position_idx = 0
get_leverage = safe_float(leverage)
potential_loss = ( price_for_cals = trigger_price if po_trigger_price is not None else price_symbol
safe_float(order_quantity)
* safe_float(price_for_calc)
* (stop_loss_percent / 100)
)
adjusted_loss = potential_loss / get_leverage
allowed_loss = safe_float(user_balance) * (max_risk_percent / 100)
if adjusted_loss > allowed_loss:
return "Risk is too high for this trade"
# Get fee rates
fee_info = client.get_fee_rates(category="linear", symbol=symbol)
# Check if commission fee is enabled
commission_fee_percent = 0.0
if commission_fee == "Yes_commission_fee":
commission_fee_percent = safe_float(
fee_info["result"]["list"][0]["takerFeeRate"]
)
total_commission = price_for_calc * order_quantity * commission_fee_percent
tp_multiplier = 1 + (take_profit_percent / 100) tp_multiplier = 1 + (take_profit_percent / 100)
if total_commission > 0: if commission_fee_percent > 0:
tp_multiplier += total_commission tp_multiplier += commission_fee_percent
if margin_type == "ISOLATED_MARGIN": if margin_type == "ISOLATED_MARGIN":
liq_long, liq_short = await get_liquidation_price( liq_long, liq_short = await get_liquidation_price(
tg_id=tg_id, tg_id=tg_id,
entry_price=price_for_calc, entry_price=price_for_cals,
symbol=symbol, symbol=symbol,
leverage=get_leverage, leverage=get_leverage,
) )
if (liq_long > 0 or liq_short > 0) and price_for_calc > 0: if (liq_long > 0 or liq_short > 0) and price_for_cals > 0:
if side == "Buy": if side == "Buy":
base_tp = price_for_calc + (price_for_calc - liq_long) base_tp = price_for_cals + (price_for_cals - liq_long)
take_profit_price = base_tp + total_commission take_profit_price = base_tp + commission_fee_percent
else: else:
base_tp = price_for_calc - (liq_short - price_for_calc) base_tp = price_for_cals - (liq_short - price_for_cals)
take_profit_price = base_tp - total_commission take_profit_price = base_tp - commission_fee_percent
take_profit_price = max(take_profit_price, 0) take_profit_price = max(take_profit_price, 0)
else: else:
take_profit_price = None take_profit_price = None
@@ -379,40 +306,38 @@ async def open_positions(
stop_loss_price = None stop_loss_price = None
else: else:
if side == "Buy": if side == "Buy":
take_profit_price = price_for_calc * tp_multiplier take_profit_price = price_for_cals * tp_multiplier
stop_loss_price = price_for_calc * (1 - stop_loss_percent / 100) stop_loss_price = price_for_cals * (1 - stop_loss_percent / 100)
else: else:
take_profit_price = price_for_calc * ( take_profit_price = price_for_cals * (
1 - (take_profit_percent / 100) - total_commission 1 - (take_profit_percent / 100) - commission_fee_percent
) )
stop_loss_price = price_for_calc * (1 + stop_loss_percent / 100) stop_loss_price = trigger_price * (1 + stop_loss_percent / 100)
take_profit_price = max(take_profit_price, 0) take_profit_price = max(take_profit_price, 0)
stop_loss_price = max(stop_loss_price, 0) stop_loss_price = max(stop_loss_price, 0)
logger.info("Take profit price: %s", take_profit_price)
logger.info("Stop loss price: %s", stop_loss_price)
logger.info("Commission fee percent: %s", commission_fee_percent)
# Place order # Place order
order_params = { order_params = {
"category": "linear", "category": "linear",
"symbol": symbol, "symbol": symbol,
"side": side, "side": side,
"orderType": order_type, "orderType": "Market",
"qty": str(order_quantity), "qty": str(qty_formatted),
"triggerDirection": trigger_direction, "triggerDirection": trigger_direction,
"triggerPrice": po_trigger_price, "triggerPrice": po_trigger_price,
"triggerBy": "LastPrice", "triggerBy": "LastPrice",
"timeInForce": "GTC", "timeInForce": "GTC",
"positionIdx": po_position_idx, "positionIdx": 0,
"tpslMode": tpsl_mode, "tpslMode": "Full",
"takeProfit": str(take_profit_price) if take_profit_price else None, "takeProfit": str(take_profit_price) if take_profit_price else None,
"stopLoss": str(stop_loss_price) if stop_loss_price else None, "stopLoss": str(stop_loss_price) if stop_loss_price else None,
} }
if order_type == "Conditional":
if conditional_order_type == "Limit":
order_params["price"] = str(limit_price)
if order_type == "Limit":
order_params["price"] = str(limit_price)
response = client.place_order(**order_params) response = client.place_order(**order_params)
if response["retCode"] == 0: if response["retCode"] == 0:
@@ -430,6 +355,8 @@ async def open_positions(
"ab not enough for new order": "ab not enough for new order", "ab not enough for new order": "ab not enough for new order",
"position idx not match position mode": "position idx not match position mode", "position idx not match position mode": "position idx not match position mode",
"Qty invalid": "Qty invalid", "Qty invalid": "Qty invalid",
"The number of contracts exceeds maximum limit allowed": "The number of contracts exceeds maximum limit allowed",
"The number of contracts exceeds minimum limit allowed": "The number of contracts exceeds minimum limit allowed",
} }
for key, msg in known_errors.items(): for key, msg in known_errors.items():
if key in error_text: if key in error_text:

View File

@@ -6,7 +6,6 @@ from aiogram.types import Message
import app.telegram.keyboards.inline as kbi import app.telegram.keyboards.inline as kbi
import database.request as rq import database.request as rq
from app.bybit.get_functions.get_balance import get_balance from app.bybit.get_functions.get_balance import get_balance
from app.bybit.get_functions.get_tickers import get_tickers
from app.bybit.logger_bybit.logger_bybit import LOGGING_CONFIG from app.bybit.logger_bybit.logger_bybit import LOGGING_CONFIG
logging.config.dictConfig(LOGGING_CONFIG) logging.config.dictConfig(LOGGING_CONFIG)
@@ -22,17 +21,14 @@ async def user_profile_bybit(tg_id: int, message: Message, state: FSMContext) ->
if wallet: if wallet:
balance = wallet.get("totalWalletBalance", "0") balance = wallet.get("totalWalletBalance", "0")
symbol = await rq.get_user_symbol(tg_id=tg_id) symbol = await rq.get_user_symbol(tg_id=tg_id)
get_tickers_info = await get_tickers(tg_id=tg_id, symbol=symbol)
price_symbol = get_tickers_info.get("lastPrice") or 0
await message.answer( await message.answer(
text=f"💎Ваш профиль Bybit:\n\n" text=f"💎Ваш профиль:\n\n"
f"⚖️ Баланс: {float(balance):,.2f} USD\n" f"⚖️ Баланс: {float(balance):,.2f} USD\n"
f"📊Торговая пара: {symbol}\n" f"📊Торговая пара: {symbol}\n\n"
f"$$$ Цена: {float(price_symbol):,.4f}\n\n"
f"Краткая инструкция:\n" f"Краткая инструкция:\n"
f"1. Укажите торговую пару (например: BTCUSDT).\n" f"1. Укажите торговую пару (например: BTCUSDT).\n"
f"2. В настройках выставьте все необходимые параметры.\n" f"2. В настройках выставьте все необходимые параметры.\n"
f"3. Нажмите кнопку 'Начать торговлю' и выберите режим торговли.\n", f"3. Нажмите кнопку 'Начать торговлю'.\n",
reply_markup=kbi.main_menu, reply_markup=kbi.main_menu,
) )
else: else:

View File

@@ -4,7 +4,7 @@ import app.telegram.keyboards.inline as kbi
import database.request as rq import database.request as rq
from app.bybit.logger_bybit.logger_bybit import LOGGING_CONFIG from app.bybit.logger_bybit.logger_bybit import LOGGING_CONFIG
from app.bybit.open_positions import trading_cycle from app.bybit.open_positions import trading_cycle
from app.helper_functions import format_value, safe_float, safe_int from app.helper_functions import format_value, safe_float
logging.config.dictConfig(LOGGING_CONFIG) logging.config.dictConfig(LOGGING_CONFIG)
logger = logging.getLogger("telegram_message_handler") logger = logging.getLogger("telegram_message_handler")
@@ -22,12 +22,6 @@ class TelegramMessageHandler:
order_data = message.get("data", [{}])[0] order_data = message.get("data", [{}])[0]
symbol = format_value(order_data.get("symbol")) symbol = format_value(order_data.get("symbol"))
qty = format_value(order_data.get("qty")) qty = format_value(order_data.get("qty"))
order_type = format_value(order_data.get("orderType"))
order_type_rus = (
"Рыночный"
if order_type == "Market"
else "Лимитный" if order_type == "Limit" else "Нет данных"
)
side = format_value(order_data.get("side")) side = format_value(order_data.get("side"))
side_rus = ( side_rus = (
"Покупка" "Покупка"
@@ -40,39 +34,16 @@ class TelegramMessageHandler:
take_profit = format_value(order_data.get("takeProfit")) take_profit = format_value(order_data.get("takeProfit"))
stop_loss = format_value(order_data.get("stopLoss")) stop_loss = format_value(order_data.get("stopLoss"))
position_idx = safe_int(order_data.get("positionIdx"))
position_idx_rus = (
"Односторонний"
if position_idx == 0
else (
"Покупка в режиме хеджирования"
if position_idx == 1
else (
"Продажа в режиме хеджирования"
if position_idx == 2
else "Нет данных"
)
)
)
status_map = { status_map = {
"New": "Ордер создан",
"Cancelled": "Ордер отменен",
"Deactivated": "Ордер деактивирован",
"Untriggered": "Условный ордер выставлен", "Untriggered": "Условный ордер выставлен",
} }
if order_status == "Filled" or order_status not in status_map: if order_status == "Filled" or order_status not in status_map:
return None return None
status_text = status_map[order_status]
text = ( text = (
f"{status_text}:\n"
f"Торговая пара: {symbol}\n" f"Торговая пара: {symbol}\n"
f"Режим позиции: {position_idx_rus}\n"
f"Количество: {qty}\n" f"Количество: {qty}\n"
f"Тип ордера: {order_type_rus}\n"
f"Движение: {side_rus}\n" f"Движение: {side_rus}\n"
) )
if price and price != "0": if price and price != "0":
@@ -97,13 +68,6 @@ class TelegramMessageHandler:
symbol = format_value(execution.get("symbol")) symbol = format_value(execution.get("symbol"))
exec_price = format_value(execution.get("execPrice")) exec_price = format_value(execution.get("execPrice"))
exec_fee = format_value(execution.get("execFee")) exec_fee = format_value(execution.get("execFee"))
exec_qty = format_value(execution.get("execQty"))
order_type = format_value(execution.get("orderType"))
order_type_rus = (
"Рыночный"
if order_type == "Market"
else "Лимитный" if order_type == "Limit" else "Нет данных"
)
side = format_value(execution.get("side")) side = format_value(execution.get("side"))
side_rus = ( side_rus = (
"Покупка" "Покупка"
@@ -113,14 +77,17 @@ class TelegramMessageHandler:
if safe_float(closed_size) == 0: if safe_float(closed_size) == 0:
await rq.set_fee_user_auto_trading( await rq.set_fee_user_auto_trading(
tg_id=tg_id, symbol=symbol, side=side, fee=safe_float(exec_fee) tg_id=tg_id, symbol=symbol, fee=safe_float(exec_fee)
) )
if side == "Buy":
res_side = "Sell"
else:
res_side = "Buy"
user_auto_trading = await rq.get_user_auto_trading( user_auto_trading = await rq.get_user_auto_trading(
tg_id=tg_id, symbol=symbol, side=res_side tg_id=tg_id, symbol=symbol
)
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: if user_auto_trading is not None and user_auto_trading.fee is not None:
@@ -142,13 +109,15 @@ class TelegramMessageHandler:
) )
text = f"{header}\n" f"Торговая пара: {symbol}\n" text = f"{header}\n" f"Торговая пара: {symbol}\n"
if safe_float(closed_size) > 0: user_deals_data = await rq.get_user_deal_by_symbol(
text += f"Количество закрытых сделок: {closed_size}\n" tg_id=tg_id, symbol=symbol
)
exec_bet = user_deals_data.order_quantity
base_quantity = user_deals_data.base_quantity
text += ( text += (
f"Цена исполнения: {exec_price}\n" f"Цена исполнения: {exec_price}\n"
f"Количество исполненных сделок: {exec_qty}\n" f"Текущая ставка: {exec_bet}\n"
f"Тип ордера: {order_type_rus}\n"
f"Движение: {side_rus}\n" f"Движение: {side_rus}\n"
f"Комиссия за сделку: {exec_fee}\n" f"Комиссия за сделку: {exec_fee}\n"
) )
@@ -175,27 +144,26 @@ class TelegramMessageHandler:
await self.telegram_bot.send_message( await self.telegram_bot.send_message(
chat_id=tg_id, text=profit_text, reply_markup=kbi.profile_bybit chat_id=tg_id, text=profit_text, reply_markup=kbi.profile_bybit
) )
if side == "Buy":
r_side = "Sell"
else:
r_side = "Buy"
await rq.set_auto_trading( await rq.set_auto_trading(
tg_id=tg_id, symbol=symbol, auto_trading=False, side=r_side tg_id=tg_id, symbol=symbol, auto_trading=False
) )
user_deals_data = await rq.get_user_deal_by_symbol(
tg_id=tg_id, symbol=symbol 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
)
await rq.set_order_quantity(
tg_id=message.from_user.id, order_quantity=base_quantity
) )
if user_deals_data and user_deals_data.switch_side_mode:
await rq.set_auto_trading(
tg_id=tg_id, symbol=symbol, auto_trading=False, side=side
)
else: else:
open_order_text = "\n❗️ Сделка закрылась в минус, открываю новую сделку с увеличенной ставкой.\n" open_order_text = "\n❗️ Сделка закрылась в минус, открываю новую сделку с увеличенной ставкой.\n"
await self.telegram_bot.send_message( await self.telegram_bot.send_message(
chat_id=tg_id, text=open_order_text chat_id=tg_id, text=open_order_text
) )
res = await trading_cycle( res = await trading_cycle(
tg_id=tg_id, symbol=symbol, reverse_side=side, size=closed_size tg_id=tg_id, symbol=symbol, reverse_side=side
) )
if res == "OK": if res == "OK":
@@ -211,27 +179,21 @@ class TelegramMessageHandler:
error_text = errors.get( error_text = errors.get(
res, "❗️ Не удалось открыть новую сделку" res, "❗️ Не удалось открыть новую сделку"
) )
if side == "Buy":
r_side = "Sell"
else:
r_side = "Buy"
await rq.set_auto_trading( await rq.set_auto_trading(
tg_id=tg_id, symbol=symbol, auto_trading=False, side=r_side tg_id=tg_id, symbol=symbol, auto_trading=False
) )
user_deals_data = await rq.get_user_deal_by_symbol(
tg_id=tg_id, symbol=symbol 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
) )
if user_deals_data and user_deals_data.switch_side_mode:
await rq.set_auto_trading(
tg_id=tg_id,
symbol=symbol,
auto_trading=False,
side=side,
)
await self.telegram_bot.send_message( await self.telegram_bot.send_message(
chat_id=tg_id, chat_id=tg_id,
text=error_text, text=error_text,
reply_markup=kbi.profile_bybit, reply_markup=kbi.profile_bybit,
) )
except Exception as e: except Exception as e:
logger.error("Error in telegram_message_handler: %s", e) logger.error("Error in telegram_message_handler: %s", e)

View File

@@ -34,7 +34,7 @@ def is_number(value: str) -> bool:
# Convert the string to a float # Convert the string to a float
num = float(value) num = float(value)
# Check if the number is positive # Check if the number is positive
if num <= 0: if num < 0:
return False return False
# Check if the string contains "+" or "-" # Check if the string contains "+" or "-"
if "+" in value or "-" in value: if "+" in value or "-" in value:

View File

@@ -7,13 +7,12 @@ from aiogram.types import CallbackQuery, Message
import app.telegram.keyboards.inline as kbi import app.telegram.keyboards.inline as kbi
import database.request as rq import database.request as rq
from app.bybit.get_functions.get_tickers import get_tickers 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.profile_bybit import user_profile_bybit from app.bybit.profile_bybit import user_profile_bybit
from app.bybit.set_functions.set_leverage import ( from app.bybit.set_functions.set_leverage import set_leverage
set_leverage,
set_leverage_to_buy_and_sell,
)
from app.bybit.set_functions.set_margin_mode import set_margin_mode 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 safe_float
from app.telegram.states.states import ChangingTheSymbolState from app.telegram.states.states import ChangingTheSymbolState
from logger_helper.logger_helper import LOGGING_CONFIG from logger_helper.logger_helper import LOGGING_CONFIG
@@ -91,12 +90,7 @@ async def set_symbol(message: Message, state: FSMContext) -> None:
await rq.create_user_additional_settings(tg_id=message.from_user.id) await rq.create_user_additional_settings(tg_id=message.from_user.id)
return return
trade_mode = additional_settings.trade_mode or "Merged_Single"
mode = 0 if trade_mode == "Merged_Single" else 3
margin_type = additional_settings.margin_type or "ISOLATED_MARGIN" margin_type = additional_settings.margin_type or "ISOLATED_MARGIN"
leverage = "10"
leverage_to_buy = "10"
leverage_to_sell = "10"
ticker = await get_tickers(tg_id=message.from_user.id, symbol=symbol) ticker = await get_tickers(tg_id=message.from_user.id, symbol=symbol)
if ticker is None: if ticker is None:
@@ -105,6 +99,8 @@ async def set_symbol(message: Message, state: FSMContext) -> None:
) )
return return
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) req = await rq.set_user_symbol(tg_id=message.from_user.id, symbol=symbol)
if not req: if not req:
@@ -118,45 +114,18 @@ async def set_symbol(message: Message, state: FSMContext) -> None:
tg_id=message.from_user.id, message=message, state=state tg_id=message.from_user.id, message=message, state=state
) )
res = await set_switch_position_mode(
tg_id=message.from_user.id, symbol=symbol, mode=mode
)
if res == "You have an existing position, so position mode cannot be switched":
if mode == 0:
mode = 3
else:
mode = 0
await set_switch_position_mode(
tg_id=message.from_user.id, symbol=symbol, mode=mode
)
if trade_mode == "Merged_Single":
trade_mode = "Both_Sides"
else:
trade_mode = "Merged_Single"
await rq.set_trade_mode(tg_id=message.from_user.id, trade_mode=trade_mode)
await set_margin_mode(tg_id=message.from_user.id, margin_mode=margin_type) await set_margin_mode(tg_id=message.from_user.id, margin_mode=margin_type)
if margin_type == "ISOLATED_MARGIN": await set_leverage(
await set_leverage_to_buy_and_sell( tg_id=message.from_user.id, symbol=symbol, leverage=str(max_leverage)
tg_id=message.from_user.id,
symbol=symbol,
leverage_to_buy=str(leverage_to_buy),
leverage_to_sell=str(leverage_to_sell),
)
else:
await set_leverage(
tg_id=message.from_user.id, symbol=symbol, leverage=str(leverage)
)
await rq.set_leverage(tg_id=message.from_user.id, leverage=str(leverage))
await rq.set_leverage_to_buy_and_sell(
tg_id=message.from_user.id,
leverage_to_buy=str(leverage_to_buy),
leverage_to_sell=str(leverage_to_sell),
) )
await rq.set_limit_price(tg_id=message.from_user.id, limit_price=0)
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)
await rq.set_trigger_price(tg_id=message.from_user.id, trigger_price=0) 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)
await state.clear() await state.clear()
except Exception as e: except Exception as e:

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 rq.create_user_conditional_settings(tg_id=tg_id)
await message.answer( await message.answer(
text=f"Добро пожаловать, {full_name}!\n\n" text=f"Добро пожаловать, {full_name}!\n\n"
"PHANTOM TRADING - ваш надежный помощник для автоматизации трейдинга😉", "Чат-робот для трейдинга - ваш надежный помощник для анализа рынка и принятия взвешенных решений.😉",
reply_markup=kbi.connect_the_platform, reply_markup=kbi.connect_the_platform,
) )
logger.debug( logger.debug(

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@ from aiogram.types import CallbackQuery, Message
import app.telegram.keyboards.inline as kbi import app.telegram.keyboards.inline as kbi
import database.request as rq import database.request as rq
from app.helper_functions import is_int from app.helper_functions import is_number, safe_float
from app.telegram.states.states import RiskManagementState from app.telegram.states.states import RiskManagementState
from logger_helper.logger_helper import LOGGING_CONFIG from logger_helper.logger_helper import LOGGING_CONFIG
@@ -86,7 +86,7 @@ async def set_take_profit_percent(message: Message, state: FSMContext) -> None:
take_profit_percent_value = message.text take_profit_percent_value = message.text
if not is_int(take_profit_percent_value): if not is_number(take_profit_percent_value):
await message.answer( await message.answer(
text="Ошибка: введите валидное число.", text="Ошибка: введите валидное число.",
reply_markup=kbi.back_to_risk_management, reply_markup=kbi.back_to_risk_management,
@@ -98,7 +98,7 @@ async def set_take_profit_percent(message: Message, state: FSMContext) -> None:
) )
return return
if int(take_profit_percent_value) < 1 or int(take_profit_percent_value) > 100: if safe_float(take_profit_percent_value) < 1 or safe_float(take_profit_percent_value) > 100:
await message.answer( await message.answer(
text="Ошибка: введите число от 1 до 100.", text="Ошибка: введите число от 1 до 100.",
reply_markup=kbi.back_to_risk_management, reply_markup=kbi.back_to_risk_management,
@@ -112,7 +112,7 @@ async def set_take_profit_percent(message: Message, state: FSMContext) -> None:
req = await rq.set_take_profit_percent( req = await rq.set_take_profit_percent(
tg_id=message.from_user.id, tg_id=message.from_user.id,
take_profit_percent=int(take_profit_percent_value), take_profit_percent=safe_float(take_profit_percent_value),
) )
if req: if req:
@@ -207,7 +207,7 @@ async def set_stop_loss_percent(message: Message, state: FSMContext) -> None:
stop_loss_percent_value = message.text stop_loss_percent_value = message.text
if not is_int(stop_loss_percent_value): if not is_number(stop_loss_percent_value):
await message.answer( await message.answer(
text="Ошибка: введите валидное число.", text="Ошибка: введите валидное число.",
reply_markup=kbi.back_to_risk_management, reply_markup=kbi.back_to_risk_management,
@@ -219,7 +219,7 @@ async def set_stop_loss_percent(message: Message, state: FSMContext) -> None:
) )
return return
if int(stop_loss_percent_value) < 1 or int(stop_loss_percent_value) > 100: if safe_float(stop_loss_percent_value) < 1 or safe_float(stop_loss_percent_value) > 100:
await message.answer( await message.answer(
text="Ошибка: введите число от 1 до 100.", text="Ошибка: введите число от 1 до 100.",
reply_markup=kbi.back_to_risk_management, reply_markup=kbi.back_to_risk_management,
@@ -232,7 +232,7 @@ async def set_stop_loss_percent(message: Message, state: FSMContext) -> None:
return return
req = await rq.set_stop_loss_percent( req = await rq.set_stop_loss_percent(
tg_id=message.from_user.id, stop_loss_percent=int(stop_loss_percent_value) tg_id=message.from_user.id, stop_loss_percent=safe_float(stop_loss_percent_value)
) )
if req: if req:
@@ -262,130 +262,6 @@ async def set_stop_loss_percent(message: Message, state: FSMContext) -> None:
) )
@router_risk_management.callback_query(F.data == "max_risk_percent")
async def max_risk_percent(callback_query: CallbackQuery, state: FSMContext) -> None:
"""
Handles the 'max_risk_percent' callback query.
Clears the current FSM state, edits the message text to display the maximum risk percentage options,
and shows an inline keyboard for selection.
Args:
callback_query (CallbackQuery): Incoming callback query from Telegram inline keyboard.
state (FSMContext): Finite State Machine context for the current user session.
Logs:
Success or error messages with user identification.
"""
try:
await state.clear()
await state.set_state(RiskManagementState.max_risk_percent_state)
msg = await callback_query.message.edit_text(
text="Введите максимальный процент риска: ",
reply_markup=kbi.back_to_risk_management,
)
await state.update_data(prompt_message_id=msg.message_id)
logger.debug(
"Command max_risk_percent processed successfully for user: %s",
callback_query.from_user.id,
)
except Exception as e:
await callback_query.answer(
text="Произошла ошибка. Пожалуйста, попробуйте позже."
)
logger.error(
"Error processing command max_risk_percent for user %s: %s",
callback_query.from_user.id,
e,
)
@router_risk_management.message(RiskManagementState.max_risk_percent_state)
async def set_max_risk_percent(message: Message, state: FSMContext) -> None:
"""
Handles user input for setting the maximum risk percentage.
Updates FSM context with the selected percentage and persists the choice in database.
Sends an acknowledgement to user and clears FSM state afterward.
Args:
message (Message): Incoming message from user containing the maximum risk percentage.
state (FSMContext): Finite State Machine context for the current user session.
Logs:
Success or error messages with user identification.
"""
try:
try:
data = await state.get_data()
if "prompt_message_id" in data:
prompt_message_id = data["prompt_message_id"]
await message.bot.delete_message(
chat_id=message.chat.id, message_id=prompt_message_id
)
await message.delete()
except Exception as e:
if "message to delete not found" in str(e).lower():
pass # Ignore this error
else:
raise e
max_risk_percent_value = message.text
if not is_int(max_risk_percent_value):
await message.answer(
text="Ошибка: введите валидное число.",
reply_markup=kbi.back_to_risk_management,
)
logger.debug(
"User %s input invalid (not an valid number): %s",
message.from_user.id,
max_risk_percent_value,
)
return
if int(max_risk_percent_value) < 1 or int(max_risk_percent_value) > 100:
await message.answer(
text="Ошибка: введите число от 1 до 100.",
reply_markup=kbi.back_to_risk_management,
)
logger.debug(
"User %s input invalid (not an valid number): %s",
message.from_user.id,
max_risk_percent_value,
)
return
req = await rq.set_max_risk_percent(
tg_id=message.from_user.id, max_risk_percent=int(max_risk_percent_value)
)
if req:
await message.answer(
text=f"Максимальный процент риска установлен на {max_risk_percent_value}%.",
reply_markup=kbi.back_to_risk_management,
)
else:
await message.answer(
text="Произошла ошибка при установке максимального процента риска. "
"Пожалуйста, попробуйте позже.",
reply_markup=kbi.back_to_risk_management,
)
await state.clear()
except Exception as e:
await message.answer(
text="Произошла ошибка при установке максимального процента риска. "
"Пожалуйста, попробуйте позже.",
reply_markup=kbi.back_to_risk_management,
)
logger.error(
"Error processing command max_risk_percent for user %s: %s",
message.from_user.id,
e,
)
@router_risk_management.callback_query(F.data == "commission_fee") @router_risk_management.callback_query(F.data == "commission_fee")
async def commission_fee(callback_query: CallbackQuery, state: FSMContext) -> None: async def commission_fee(callback_query: CallbackQuery, state: FSMContext) -> None:
""" """

View File

@@ -7,8 +7,8 @@ from aiogram.types import CallbackQuery
import app.telegram.keyboards.inline as kbi import app.telegram.keyboards.inline as kbi
import database.request as rq import database.request as rq
from app.bybit import get_bybit_client from app.bybit import get_bybit_client
from app.bybit.get_functions.get_tickers import get_tickers
from app.helper_functions import calculate_total_budget, get_base_currency, safe_float from app.helper_functions import calculate_total_budget, safe_float
from logger_helper.logger_helper import LOGGING_CONFIG from logger_helper.logger_helper import LOGGING_CONFIG
logging.config.dictConfig(LOGGING_CONFIG) logging.config.dictConfig(LOGGING_CONFIG)
@@ -40,77 +40,30 @@ async def additional_settings(callback_query: CallbackQuery, state: FSMContext)
return return
trade_mode_map = { trade_mode_map = {
"Merged_Single": "Односторонний режим", "Long": "Лонг",
"Both_Sides": "Хеджирование", "Short": "Шорт",
"Switch": "Свитч",
} }
margin_type_map = { margin_type_map = {
"ISOLATED_MARGIN": "Изолированная", "ISOLATED_MARGIN": "Изолированная",
"REGULAR_MARGIN": "Кросс", "REGULAR_MARGIN": "Кросс",
} }
order_type_map = {"Market": "Рыночный", "Limit": "Лимитный"}
trade_mode = additional_data.trade_mode or "" trade_mode = additional_data.trade_mode or ""
margin_type = additional_data.margin_type or "" margin_type = additional_data.margin_type or ""
order_type = additional_data.order_type or ""
trade_mode_rus = trade_mode_map.get(trade_mode, trade_mode) trade_mode_rus = trade_mode_map.get(trade_mode, trade_mode)
margin_type_rus = margin_type_map.get(margin_type, margin_type) margin_type_rus = margin_type_map.get(margin_type, margin_type)
order_type_rus = order_type_map.get(order_type, "Условный") switch_side = additional_data.switch_side
def f(x): def f(x):
return safe_float(x) return safe_float(x)
leverage = f(additional_data.leverage) leverage = f(additional_data.leverage)
leverage_to_buy = f(additional_data.leverage_to_buy)
leverage_to_sell = f(additional_data.leverage_to_sell)
martingale = f(additional_data.martingale_factor) martingale = f(additional_data.martingale_factor)
max_bets = additional_data.max_bets_in_series max_bets = additional_data.max_bets_in_series
quantity = f(additional_data.order_quantity) quantity = f(additional_data.order_quantity)
limit_price = f(additional_data.limit_price)
trigger_price = f(additional_data.trigger_price) or 0 trigger_price = f(additional_data.trigger_price) or 0
tickers = await get_tickers(tg_id=tg_id, symbol=symbol)
price_symbol = safe_float(tickers.get("lastPrice")) or 0
bid = f(tickers.get("bid1Price")) or 0
ask = f(tickers.get("ask1Price")) or 0
sym = get_base_currency(symbol)
if trade_mode == "Merged_Single":
leverage_str = f"{leverage:.2f}x"
else:
if margin_type == "ISOLATED_MARGIN":
leverage_str = f"{leverage_to_buy:.2f}x:{leverage_to_sell:.2f}x"
else:
leverage_str = f"{leverage:.2f}x"
conditional_order_type = additional_data.conditional_order_type or ""
conditional_order_type_rus = (
"Лимитный"
if conditional_order_type == "Limit"
else (
"Рыночный"
if conditional_order_type == "Market"
else conditional_order_type
)
)
conditional_order_type_text = (
f"- Тип условного ордера: {conditional_order_type_rus}\n"
if order_type == "Conditional"
else ""
)
limit_price_text = ""
trigger_price_text = ""
if order_type == "Limit":
limit_price_text = f"- Цена лимитного ордера: {limit_price:.4f} USDT\n"
elif order_type == "Conditional":
if conditional_order_type == "Limit":
limit_price_text = f"- Цена лимитного ордера: {limit_price:.4f} USDT\n"
trigger_price_text = f"- Триггер цена: {trigger_price:.4f} USDT\n"
risk_management_data = await rq.get_user_risk_management(tg_id=tg_id) risk_management_data = await rq.get_user_risk_management(tg_id=tg_id)
commission_fee = risk_management_data.commission_fee commission_fee = risk_management_data.commission_fee
client = await get_bybit_client(tg_id=tg_id) client = await get_bybit_client(tg_id=tg_id)
@@ -124,54 +77,32 @@ async def additional_settings(callback_query: CallbackQuery, state: FSMContext)
else: else:
commission_fee_percent = 0.0 commission_fee_percent = 0.0
if order_type == "Conditional": switch_side_mode = ""
if conditional_order_type == "Limit": if trade_mode == "Switch":
entry_price = limit_price switch_side_mode = f"- Направление первой сделки: {switch_side}\n"
ask_price = limit_price
bid_price = limit_price
else:
ask_price = trigger_price
bid_price = trigger_price
entry_price = trigger_price
else:
if order_type == "Limit":
entry_price = limit_price
ask_price = limit_price
bid_price = limit_price
else:
entry_price = price_symbol
ask_price = ask
bid_price = bid
durability_buy = quantity * bid_price quantity_price = quantity * trigger_price
durability_sell = quantity * ask_price
quantity_price = quantity * entry_price
total_commission = quantity_price * commission_fee_percent total_commission = quantity_price * commission_fee_percent
total_budget = await calculate_total_budget( total_budget = await calculate_total_budget(
quantity=durability_buy, quantity=quantity,
martingale_factor=martingale, martingale_factor=martingale,
max_steps=max_bets, max_steps=max_bets,
commission_fee_percent=total_commission, commission_fee_percent=total_commission,
) )
text = ( text = (
f"Основные настройки:\n\n" f"Основные настройки:\n\n"
f"- Режим позиции: {trade_mode_rus}\n" f"- Режим торговли: {trade_mode_rus}\n"
f"{switch_side_mode}"
f"- Тип маржи: {margin_type_rus}\n" f"- Тип маржи: {margin_type_rus}\n"
f"- Размер кредитного плеча: {leverage_str}\n" f"- Размер кредитного плеча: {leverage:.2f}\n"
f"- Тип ордера: {order_type_rus}\n" f"- Базовая ставка: {quantity} USDT\n"
f"- Количество ордера: {quantity} {sym}\n"
f"- Коэффициент мартингейла: {martingale:.2f}\n" f"- Коэффициент мартингейла: {martingale:.2f}\n"
f"{conditional_order_type_text}" f"- Триггер цена: {trigger_price:.4f} USDT\n"
f"{trigger_price_text}"
f"{limit_price_text}"
f"- Максимальное кол-во ставок в серии: {max_bets}\n\n" f"- Максимальное кол-во ставок в серии: {max_bets}\n\n"
f"- Стоимость: {durability_buy:.2f}/{durability_sell:.2f} USDT\n" f"- Бюджет серии: {total_budget:.4f} USDT\n"
f"- Рекомендуемый бюджет: {total_budget:.4f} USDT\n"
) )
keyboard = kbi.get_additional_settings_keyboard( keyboard = kbi.get_additional_settings_keyboard(mode=trade_mode)
current_order_type=order_type, conditional_order=conditional_order_type
)
await callback_query.message.edit_text(text=text, reply_markup=keyboard) await callback_query.message.edit_text(text=text, reply_markup=keyboard)
logger.debug( logger.debug(
"Command additional_settings processed successfully for user: %s", tg_id "Command additional_settings processed successfully for user: %s", tg_id
@@ -202,7 +133,6 @@ async def risk_management(callback_query: CallbackQuery, state: FSMContext) -> N
if risk_management_data: if risk_management_data:
take_profit_percent = risk_management_data.take_profit_percent or "" take_profit_percent = risk_management_data.take_profit_percent or ""
stop_loss_percent = risk_management_data.stop_loss_percent or "" stop_loss_percent = risk_management_data.stop_loss_percent or ""
max_risk_percent = risk_management_data.max_risk_percent or ""
commission_fee = risk_management_data.commission_fee or "" commission_fee = risk_management_data.commission_fee or ""
commission_fee_rus = ( commission_fee_rus = (
"Да" if commission_fee == "Yes_commission_fee" else "Нет" "Да" if commission_fee == "Yes_commission_fee" else "Нет"
@@ -212,7 +142,6 @@ async def risk_management(callback_query: CallbackQuery, state: FSMContext) -> N
text=f"Риск-менеджмент:\n\n" text=f"Риск-менеджмент:\n\n"
f"- Процент изменения цены для фиксации прибыли: {take_profit_percent}%\n" f"- Процент изменения цены для фиксации прибыли: {take_profit_percent}%\n"
f"- Процент изменения цены для фиксации убытка: {stop_loss_percent}%\n\n" f"- Процент изменения цены для фиксации убытка: {stop_loss_percent}%\n\n"
f"- Максимальный риск на сделку (в % от баланса): {max_risk_percent}%\n\n"
f"- Комиссия биржи для расчета прибыли: {commission_fee_rus}\n\n", f"- Комиссия биржи для расчета прибыли: {commission_fee_rus}\n\n",
reply_markup=kbi.risk_management, reply_markup=kbi.risk_management,
) )

View File

@@ -12,9 +12,7 @@ from app.bybit.open_positions import start_trading_cycle
from app.helper_functions import safe_float from app.helper_functions import safe_float
from app.telegram.tasks.tasks import ( from app.telegram.tasks.tasks import (
add_start_task_merged, add_start_task_merged,
add_start_task_switch, cancel_start_task_merged
cancel_start_task_merged,
cancel_start_task_switch,
) )
from logger_helper.logger_helper import LOGGING_CONFIG from logger_helper.logger_helper import LOGGING_CONFIG
@@ -35,97 +33,20 @@ async def start_trading(callback_query: CallbackQuery, state: FSMContext) -> Non
""" """
try: try:
await state.clear() await state.clear()
additional_data = await rq.get_user_additional_settings(
tg_id=callback_query.from_user.id
)
trade_mode = additional_data.trade_mode
symbol = await rq.get_user_symbol(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( deals = await get_active_positions_by_symbol(
tg_id=callback_query.from_user.id, symbol=symbol tg_id=callback_query.from_user.id, symbol=symbol
) )
position = next((d for d in deals if d.get("symbol") == symbol), None) position = next((d for d in deals if d.get("symbol") == symbol), None)
if position: if position:
size = position.get("size", 0) size = position.get("size", 0)
position_idx = position.get("positionIdx")
else: else:
size = 0 size = 0
position_idx = None
if position_idx != 0 and safe_float(size) > 0 and trade_mode == "Merged_Single": if safe_float(size) > 0:
await callback_query.answer( await callback_query.answer(
text="У вас есть активная позиция в режиме хеджирования. " text="У вас есть активная позиция",
"Открытие сделки в одностороннем режиме невозможно.",
)
return
if position_idx == 0 and safe_float(size) > 0 and trade_mode == "Both_Sides":
await callback_query.answer(
text="У вас есть активная позиция в одностороннем режиме. "
"Открытие сделки в режиме хеджирования невозможно.",
)
return
if trade_mode == "Merged_Single":
await callback_query.message.edit_text(
text="Выберите режим торговли:\n\n"
"Лонг - все сделки серии открываются на покупку.\n"
"Шорт - все сделки серии открываются на продажу.\n"
"Свитч - направление каждой сделки серии меняется по переменно.\n",
reply_markup=kbi.merged_start_trading,
)
else: # trade_mode == "Both_Sides":
await callback_query.message.edit_text(
text="Выберите режим торговли:\n\n"
"Лонг - все сделки открываются на покупку.\n"
"Шорт - все сделки открываются на продажу.\n",
reply_markup=kbi.both_start_trading,
)
logger.debug(
"Command start_trading processed successfully for user: %s",
callback_query.from_user.id,
)
except Exception as e:
await callback_query.answer(text="Произошла ошибка при запуске торговли")
logger.error(
"Error processing command start_trading for user %s: %s",
callback_query.from_user.id,
e,
)
@router_start_trading.callback_query(lambda c: c.data == "long" or c.data == "short")
async def start_trading_long(callback_query: CallbackQuery, state: FSMContext) -> None:
"""
Handles the "long" or "short" callback query.
Clears the FSM state and starts the trading cycle.
:param callback_query: Message
:param state: FSMContext
:return: None
"""
try:
if callback_query.data == "long":
side = "Buy"
elif callback_query.data == "short":
side = "Sell"
else:
await callback_query.answer(text="Произошла ошибка при запуске торговли")
return
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)
position_idx = position.get("positionIdx")
else:
size = 0
position_idx = None
if position_idx == 0 and safe_float(size) > 0:
await callback_query.answer(
text="Торговля уже запущена в одностороннем режиме для данного инструмента"
) )
return return
@@ -151,12 +72,9 @@ async def start_trading_long(callback_query: CallbackQuery, state: FSMContext) -
tg_id=callback_query.from_user.id, tg_id=callback_query.from_user.id,
symbol=symbol, symbol=symbol,
auto_trading=True, auto_trading=True,
side=side,
) )
res = await start_trading_cycle( res = await start_trading_cycle(
tg_id=callback_query.from_user.id, tg_id=callback_query.from_user.id,
side=side,
switch_side_mode=False,
) )
error_messages = { error_messages = {
@@ -167,9 +85,10 @@ async def start_trading_long(callback_query: CallbackQuery, state: FSMContext) -
"ab not enough for new order": "Недостаточно средств для создания нового ордера", "ab not enough for new order": "Недостаточно средств для создания нового ордера",
"InvalidRequestError": "Произошла ошибка при запуске торговли.", "InvalidRequestError": "Произошла ошибка при запуске торговли.",
"Order does not meet minimum order value": "Сумма ордера не достаточна для запуска торговли", "Order does not meet minimum order value": "Сумма ордера не достаточна для запуска торговли",
"position idx not match position mode": "Торговля уже запущена в режиме хеджирования на продажу для данного инструмента", "position idx not match position mode": "Ошибка режима позиции для данного инструмента",
"Qty invalid": "Некорректное значение ордера для данного инструмента", "Qty invalid": "Некорректное значение ордера для данного инструмента",
"The number of contracts exceeds maximum limit allowed": "️️Количество контрактов превышает допустимое максимальное количество контрактов", "The number of contracts exceeds maximum limit allowed": "️️Количество контрактов превышает допустимое максимальное количество контрактов",
"The number of contracts exceeds minimum limit allowed": "️️Количество контрактов превышает допустимое минимальное количество контрактов",
} }
if res == "OK": if res == "OK":
@@ -180,7 +99,6 @@ async def start_trading_long(callback_query: CallbackQuery, state: FSMContext) -
tg_id=callback_query.from_user.id, tg_id=callback_query.from_user.id,
symbol=symbol, symbol=symbol,
auto_trading=False, auto_trading=False,
side=side,
) )
text = error_messages.get(res, "Произошла ошибка при запуске торговли") text = error_messages.get(res, "Произошла ошибка при запуске торговли")
await callback_query.message.edit_text( await callback_query.message.edit_text(
@@ -202,189 +120,8 @@ async def start_trading_long(callback_query: CallbackQuery, state: FSMContext) -
logger.error("Cancelled timer for user %s", callback_query.from_user.id) logger.error("Cancelled timer for user %s", callback_query.from_user.id)
@router_start_trading.callback_query(lambda c: c.data == "switch")
async def start_trading_switch(
callback_query: CallbackQuery, state: FSMContext
) -> None:
"""
Handles the "switch" callback query.
Clears the FSM state and sends a message to the user to select the switch side.
:param callback_query: Message
:param state: FSMContext
:return: None
"""
try:
await state.clear()
await callback_query.message.edit_text(
text="Выберите направление первой сделки серии:\n\n"
"Лонг - открывается первая сделка на покупку.\n"
"Шорт - открывается первая сделка на продажу.\n"
"По направлению - сделка открывается в направлении последней сделки предыдущей серии.\n"
"Противоположно - сделка открывается в противоположном направлении последней сделки предыдущей серии.\n",
reply_markup=kbi.switch_side,
)
except Exception as e:
await callback_query.answer(text="Произошла ошибка при запуске торговли")
logger.error(
"Error processing command start trading switch for user %s: %s",
callback_query.from_user.id,
e,
)
@router_start_trading.callback_query( @router_start_trading.callback_query(
lambda c: c.data lambda c: c.data == "cancel_timer_merged"
in {"switch_long", "switch_short", "switch_direction", "switch_opposite"}
)
async def start_switch(callback_query: CallbackQuery, state: FSMContext) -> None:
"""
Starts the trading cycle with the selected side.
:param callback_query:
:param state:
:return:
"""
try:
symbol = await rq.get_user_symbol(tg_id=callback_query.from_user.id)
user_deals_data = await rq.get_user_deal_by_symbol(
tg_id=callback_query.from_user.id, symbol=symbol
)
get_side = "Buy"
if user_deals_data:
get_side = user_deals_data.last_side or "Buy"
if callback_query.data == "switch_long":
side = "Buy"
elif callback_query.data == "switch_short":
side = "Sell"
elif callback_query.data == "switch_direction":
side = get_side
elif callback_query.data == "switch_opposite":
if get_side == "Buy":
side = "Sell"
else:
side = "Buy"
else:
await callback_query.answer(text="Произошла ошибка при запуске торговли")
return
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)
position_idx = position.get("positionIdx")
else:
size = 0
position_idx = None
if position_idx == 1 and safe_float(size) > 0 and side == "Buy":
await callback_query.answer(
text="Торговля уже запущена в режиме хеджирования на покупку для данного инструмента"
)
return
if position_idx == 2 and safe_float(size) > 0 and side == "Sell":
await callback_query.answer(
text="Торговля уже запущена в режиме хеджирования на продажу для данного инструмента"
)
return
conditional_data = await rq.get_user_conditional_settings(
tg_id=callback_query.from_user.id
)
timer_start = conditional_data.timer_start
cancel_start_task_switch(user_id=callback_query.from_user.id)
async def delay_start():
if timer_start > 0:
await callback_query.message.edit_text(
text=f"Торговля будет запущена с задержкой {timer_start} мин.",
reply_markup=kbi.cancel_timer_switch,
)
await rq.set_start_timer(
tg_id=callback_query.from_user.id, timer_start=0
)
await asyncio.sleep(timer_start * 60)
await rq.set_auto_trading(
tg_id=callback_query.from_user.id,
symbol=symbol,
auto_trading=True,
side=side,
)
if side == "Buy":
r_side = "Sell"
else:
r_side = "Buy"
await rq.set_auto_trading(
tg_id=callback_query.from_user.id,
symbol=symbol,
auto_trading=True,
side=r_side,
)
res = await start_trading_cycle(
tg_id=callback_query.from_user.id,
side=side,
switch_side_mode=True,
)
error_messages = {
"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": " ️️Количество контрактов превышает допустимое максимальное количество контрактов",
}
if res == "OK":
await callback_query.message.edit_text(text="Торговля запущена")
await state.clear()
else:
await rq.set_auto_trading(
tg_id=callback_query.from_user.id,
symbol=symbol,
auto_trading=False,
side=side,
)
if side == "Buy":
r_side = "Sell"
else:
r_side = "Buy"
await rq.set_auto_trading(
tg_id=callback_query.from_user.id,
symbol=symbol,
auto_trading=False,
side=r_side,
)
text = error_messages.get(res, "Произошла ошибка при запуске торговли")
await callback_query.message.edit_text(
text=text, reply_markup=kbi.profile_bybit
)
await callback_query.message.edit_text("Запуск торговли...")
task = asyncio.create_task(delay_start())
await add_start_task_switch(user_id=callback_query.from_user.id, task=task)
except asyncio.CancelledError:
logger.error("Cancelled timer for user %s", callback_query.from_user.id)
except Exception as e:
await callback_query.answer(text="Произошла ошибка при запуске торговли")
logger.error(
"Error processing command start switch for user %s: %s",
callback_query.from_user.id,
e,
)
@router_start_trading.callback_query(
lambda c: c.data == "cancel_timer_merged" or c.data == "cancel_timer_switch"
) )
async def cancel_start_trading( async def cancel_start_trading(
callback_query: CallbackQuery, state: FSMContext callback_query: CallbackQuery, state: FSMContext
@@ -400,8 +137,6 @@ async def cancel_start_trading(
await state.clear() await state.clear()
if callback_query.data == "cancel_timer_merged": if callback_query.data == "cancel_timer_merged":
cancel_start_task_merged(user_id=callback_query.from_user.id) cancel_start_task_merged(user_id=callback_query.from_user.id)
elif callback_query.data == "cancel_timer_switch":
cancel_start_task_switch(user_id=callback_query.from_user.id)
await callback_query.message.edit_text( await callback_query.message.edit_text(
text="Запуск торговли отменен", reply_markup=kbi.profile_bybit text="Запуск торговли отменен", reply_markup=kbi.profile_bybit
) )

View File

@@ -45,12 +45,10 @@ async def stop_all_trading(callback_query: CallbackQuery, state: FSMContext):
for active_auto_trading in user_auto_trading_list: for active_auto_trading in user_auto_trading_list:
if active_auto_trading.auto_trading: if active_auto_trading.auto_trading:
symbol = active_auto_trading.symbol symbol = active_auto_trading.symbol
get_side = active_auto_trading.side
req = await rq.set_auto_trading( req = await rq.set_auto_trading(
tg_id=callback_query.from_user.id, tg_id=callback_query.from_user.id,
symbol=symbol, symbol=symbol,
auto_trading=False, auto_trading=False,
side=get_side,
) )
if not req: if not req:
await callback_query.message.edit_text( await callback_query.message.edit_text(

View File

@@ -35,13 +35,7 @@ main_menu = InlineKeyboardMarkup(
text="Сменить торговую пару", callback_data="change_symbol" text="Сменить торговую пару", callback_data="change_symbol"
) )
], ],
[InlineKeyboardButton(text="Мои сделки", callback_data="my_deals")],
[InlineKeyboardButton(text="Начать торговлю", callback_data="start_trading")], [InlineKeyboardButton(text="Начать торговлю", callback_data="start_trading")],
[
InlineKeyboardButton(
text="Остановить торговлю", callback_data="stop_trading"
)
],
] ]
) )
@@ -67,62 +61,39 @@ main_settings = InlineKeyboardMarkup(
# additional_settings # additional_settings
def get_additional_settings_keyboard( def get_additional_settings_keyboard(mode: str
current_order_type: str, conditional_order: str
) -> InlineKeyboardMarkup: ) -> InlineKeyboardMarkup:
""" """
Create keyboard for additional settings Create keyboard for additional settings
:param current_order_type: Market, Limit or Conditional :param mode: Trade mode
:param conditional_order: Market or Limit
:return: InlineKeyboardMarkup :return: InlineKeyboardMarkup
""" """
buttons = [ buttons = [
[ [
InlineKeyboardButton(text="Режим позиции", callback_data="trade_mode"), InlineKeyboardButton(text="Режим торговли", callback_data="trade_mode"),
InlineKeyboardButton(text="Тип маржи", callback_data="margin_type"), InlineKeyboardButton(text="Тип маржи", callback_data="margin_type"),
], ],
[ [
InlineKeyboardButton( InlineKeyboardButton(
text="Размер кредитного плеча", callback_data="leverage" text="Размер кредитного плеча", callback_data="leverage"
), ),
InlineKeyboardButton(text="Тип ордера", callback_data="order_type"), InlineKeyboardButton(
text="Базовая ставка", callback_data="order_quantity"),
], ],
[ [
InlineKeyboardButton(
text="Количество ордера", callback_data="order_quantity"
),
InlineKeyboardButton( InlineKeyboardButton(
text="Коэффициент мартингейла", callback_data="martingale_factor" text="Коэффициент мартингейла", callback_data="martingale_factor"
), ),
InlineKeyboardButton(text="Триггер цена", callback_data="trigger_price"
),
], ],
] ]
if current_order_type == "Conditional": if mode == "Switch":
buttons.append( buttons.append(
[ [InlineKeyboardButton(text="Направление первой сделки", callback_data="switch_side_start")]
InlineKeyboardButton(
text="Тип условного ордера", callback_data="conditional_order_type"
)
]
)
buttons.append(
[InlineKeyboardButton(text="Триггер цена", callback_data="trigger_price")]
)
if conditional_order == "Limit":
buttons.append(
[
InlineKeyboardButton(
text="Цена лимитного ордера", callback_data="limit_price"
)
]
)
elif current_order_type == "Limit":
buttons.append(
[
InlineKeyboardButton(
text="Цена лимитного ордера", callback_data="limit_price"
)
]
) )
buttons.append( buttons.append(
@@ -143,40 +114,31 @@ def get_additional_settings_keyboard(
return InlineKeyboardMarkup(inline_keyboard=buttons) return InlineKeyboardMarkup(inline_keyboard=buttons)
order_type = InlineKeyboardMarkup(
inline_keyboard=[
[
InlineKeyboardButton(text="Рыночный", callback_data="Market"),
InlineKeyboardButton(text="Лимитный", callback_data="Limit"),
],
[InlineKeyboardButton(text="Условный", callback_data="Conditional")],
[
InlineKeyboardButton(text="Назад", callback_data="additional_settings"),
InlineKeyboardButton(text="На главную", callback_data="profile_bybit"),
],
]
)
conditional_order_type = InlineKeyboardMarkup(
inline_keyboard=[
[
InlineKeyboardButton(text="Рыночный", callback_data="set_market"),
InlineKeyboardButton(text="Лимитный", callback_data="set_limit"),
],
[
InlineKeyboardButton(text="Назад", callback_data="additional_settings"),
InlineKeyboardButton(text="На главную", callback_data="profile_bybit"),
],
]
)
trade_mode = InlineKeyboardMarkup( trade_mode = InlineKeyboardMarkup(
inline_keyboard=[ inline_keyboard=[
[ [
InlineKeyboardButton( InlineKeyboardButton(
text="Односторонний режим", callback_data="Merged_Single" text="Лонг", callback_data="Long"
),
InlineKeyboardButton(text="Шорт", callback_data="Short"),
InlineKeyboardButton(text="Свитч", callback_data="Switch"),
],
[
InlineKeyboardButton(text="Назад", callback_data="additional_settings"),
InlineKeyboardButton(text="На главную", callback_data="profile_bybit"),
],
]
)
switch_side = InlineKeyboardMarkup(
inline_keyboard=[
[
InlineKeyboardButton(
text="По направлению", callback_data="switch_direction"
),
InlineKeyboardButton(
text="Противоположно", callback_data="switch_opposite"
), ),
InlineKeyboardButton(text="Хеджирование", callback_data="Both_Sides"),
], ],
[ [
InlineKeyboardButton(text="Назад", callback_data="additional_settings"), InlineKeyboardButton(text="Назад", callback_data="additional_settings"),
@@ -207,21 +169,6 @@ back_to_additional_settings = InlineKeyboardMarkup(
] ]
) )
change_limit_price = InlineKeyboardMarkup(
inline_keyboard=[
[
InlineKeyboardButton(
text="Установить цену", callback_data="set_limit_price"
),
InlineKeyboardButton(text="Последняя цена", callback_data="last_price"),
],
[
InlineKeyboardButton(text="Назад", callback_data="additional_settings"),
InlineKeyboardButton(text="На главную", callback_data="profile_bybit"),
],
]
)
back_to_change_limit_price = InlineKeyboardMarkup( back_to_change_limit_price = InlineKeyboardMarkup(
inline_keyboard=[ inline_keyboard=[
[ [
@@ -239,17 +186,12 @@ risk_management = InlineKeyboardMarkup(
inline_keyboard=[ inline_keyboard=[
[ [
InlineKeyboardButton( InlineKeyboardButton(
text="Изм. цены прибыли", callback_data="take_profit_percent" text="Тейк-профит", callback_data="take_profit_percent"
), ),
InlineKeyboardButton( InlineKeyboardButton(
text="Изм. цены убытка", callback_data="stop_loss_percent" text="Стоп-лосс", callback_data="stop_loss_percent"
), ),
], ],
[
InlineKeyboardButton(
text="Максимальный риск", callback_data="max_risk_percent"
)
],
[InlineKeyboardButton(text="Комиссия биржи", callback_data="commission_fee")], [InlineKeyboardButton(text="Комиссия биржи", callback_data="commission_fee")],
[ [
InlineKeyboardButton(text="Назад", callback_data="main_settings"), InlineKeyboardButton(text="Назад", callback_data="main_settings"),
@@ -391,48 +333,6 @@ def make_close_orders_keyboard(symbol_order: str, order_id: str):
# START TRADING # START TRADING
merged_start_trading = InlineKeyboardMarkup(
inline_keyboard=[
[
InlineKeyboardButton(text="Лонг", callback_data="long"),
InlineKeyboardButton(text="Шорт", callback_data="short"),
],
[InlineKeyboardButton(text="Свитч", callback_data="switch")],
[InlineKeyboardButton(text="Назад", callback_data="profile_bybit")],
]
)
both_start_trading = InlineKeyboardMarkup(
inline_keyboard=[
[
InlineKeyboardButton(text="Лонг", callback_data="long"),
InlineKeyboardButton(text="Шорт", callback_data="short"),
],
[InlineKeyboardButton(text="Назад", callback_data="profile_bybit")],
]
)
switch_side = InlineKeyboardMarkup(
inline_keyboard=[
[
InlineKeyboardButton(text="Лонг", callback_data="switch_long"),
InlineKeyboardButton(text="Шорт", callback_data="switch_short"),
],
[
InlineKeyboardButton(
text="По направлению", callback_data="switch_direction"
),
InlineKeyboardButton(
text="Противоположно", callback_data="switch_opposite"
),
],
[
InlineKeyboardButton(text="Назад", callback_data="start_trading"),
InlineKeyboardButton(text="На главную", callback_data="profile_bybit"),
],
]
)
back_to_start_trading = InlineKeyboardMarkup( back_to_start_trading = InlineKeyboardMarkup(
inline_keyboard=[ inline_keyboard=[
[ [