Compare commits
	
		
			2 Commits
		
	
	
		
			9fcd92cc72
			...
			7b1a803db4
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 7b1a803db4 | |||
| 
						 | 
					ddfa3a7360 | 
@@ -54,6 +54,10 @@ sudo -u www-data /usr/bin/pip install -r requirements.txt
 | 
				
			|||||||
cp .env.sample .env
 | 
					cp .env.sample .env
 | 
				
			||||||
nvim .env
 | 
					nvim .env
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					5. Выполните миграции:
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					alembic upgrade head
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
5. Запустите бота:
 | 
					5. Запустите бота:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										32
									
								
								alembic/versions/fbf4e3658310_added_side_mode_column.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								alembic/versions/fbf4e3658310_added_side_mode_column.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
				
			|||||||
 | 
					"""Added side_mode column
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Revision ID: fbf4e3658310
 | 
				
			||||||
 | 
					Revises: 
 | 
				
			||||||
 | 
					Create Date: 2025-10-22 13:08:02.317419
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					from typing import Sequence, Union
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from alembic import op
 | 
				
			||||||
 | 
					import sqlalchemy as sa
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# revision identifiers, used by Alembic.
 | 
				
			||||||
 | 
					revision: str = 'fbf4e3658310'
 | 
				
			||||||
 | 
					down_revision: Union[str, Sequence[str], None] = None
 | 
				
			||||||
 | 
					branch_labels: Union[str, Sequence[str], None] = None
 | 
				
			||||||
 | 
					depends_on: Union[str, Sequence[str], None] = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def upgrade() -> None:
 | 
				
			||||||
 | 
					    """Upgrade schema."""
 | 
				
			||||||
 | 
					    # ### commands auto generated by Alembic - please adjust! ###
 | 
				
			||||||
 | 
					    op.add_column('user_deals', sa.Column('side_mode', sa.String(), nullable=True))
 | 
				
			||||||
 | 
					    # ### end Alembic commands ###
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def downgrade() -> None:
 | 
				
			||||||
 | 
					    """Downgrade schema."""
 | 
				
			||||||
 | 
					    # ### commands auto generated by Alembic - please adjust! ###
 | 
				
			||||||
 | 
					    op.drop_column('user_deals', 'side_mode')
 | 
				
			||||||
 | 
					    # ### end Alembic commands ###
 | 
				
			||||||
@@ -11,14 +11,14 @@ from app.bybit.logger_bybit.logger_bybit import LOGGING_CONFIG
 | 
				
			|||||||
from app.bybit.set_functions.set_leverage import set_leverage
 | 
					from app.bybit.set_functions.set_leverage import set_leverage
 | 
				
			||||||
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.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 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
 | 
					        tg_id: int
 | 
				
			||||||
) -> str | None:
 | 
					) -> str | None:
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Start trading cycle
 | 
					    Start trading cycle
 | 
				
			||||||
@@ -93,6 +93,7 @@ async def start_trading_cycle(
 | 
				
			|||||||
                last_side=side,
 | 
					                last_side=side,
 | 
				
			||||||
                current_step=1,
 | 
					                current_step=1,
 | 
				
			||||||
                trade_mode=trade_mode,
 | 
					                trade_mode=trade_mode,
 | 
				
			||||||
 | 
					                side_mode=switch_side,
 | 
				
			||||||
                margin_type=margin_type,
 | 
					                margin_type=margin_type,
 | 
				
			||||||
                leverage=leverage,
 | 
					                leverage=leverage,
 | 
				
			||||||
                order_quantity=order_quantity,
 | 
					                order_quantity=order_quantity,
 | 
				
			||||||
@@ -107,19 +108,19 @@ async def start_trading_cycle(
 | 
				
			|||||||
        return (
 | 
					        return (
 | 
				
			||||||
            res
 | 
					            res
 | 
				
			||||||
            if res
 | 
					            if res
 | 
				
			||||||
            in {
 | 
					               in {
 | 
				
			||||||
                "Limit price is out min price",
 | 
					                   "Limit price is out min price",
 | 
				
			||||||
                "Limit price is out max price",
 | 
					                   "Limit price is out max price",
 | 
				
			||||||
                "Risk is too high for this trade",
 | 
					                   "Risk is too high for this trade",
 | 
				
			||||||
                "estimated will trigger liq",
 | 
					                   "estimated will trigger liq",
 | 
				
			||||||
                "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"
 | 
					                   "The number of contracts exceeds minimum limit allowed"
 | 
				
			||||||
            }
 | 
					               }
 | 
				
			||||||
            else None
 | 
					            else None
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -128,8 +129,90 @@ async def start_trading_cycle(
 | 
				
			|||||||
        return None
 | 
					        return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def trading_cycle_profit(
 | 
				
			||||||
 | 
					        tg_id: int, symbol: str, 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)
 | 
				
			||||||
 | 
					        total_fee = user_auto_trading_data.total_fee
 | 
				
			||||||
 | 
					        trade_mode = user_deals_data.trade_mode
 | 
				
			||||||
 | 
					        margin_type = user_deals_data.margin_type
 | 
				
			||||||
 | 
					        leverage = user_deals_data.leverage
 | 
				
			||||||
 | 
					        trigger_price = 0
 | 
				
			||||||
 | 
					        take_profit_percent = user_deals_data.take_profit_percent
 | 
				
			||||||
 | 
					        stop_loss_percent = user_deals_data.stop_loss_percent
 | 
				
			||||||
 | 
					        max_bets_in_series = user_deals_data.max_bets_in_series
 | 
				
			||||||
 | 
					        martingale_factor = user_deals_data.martingale_factor
 | 
				
			||||||
 | 
					        side_mode = user_deals_data.side_mode
 | 
				
			||||||
 | 
					        base_quantity = user_deals_data.base_quantity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await set_margin_mode(tg_id=tg_id, margin_mode=margin_type)
 | 
				
			||||||
 | 
					        await set_leverage(
 | 
				
			||||||
 | 
					            tg_id=tg_id,
 | 
				
			||||||
 | 
					            symbol=symbol,
 | 
				
			||||||
 | 
					            leverage=leverage,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if trade_mode == "Switch":
 | 
				
			||||||
 | 
					            if side_mode == "Противоположно":
 | 
				
			||||||
 | 
					                s_side = "Sell" if side == "Buy" else "Buy"
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                s_side = side
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            s_side = side
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        res = await open_positions(
 | 
				
			||||||
 | 
					            tg_id=tg_id,
 | 
				
			||||||
 | 
					            symbol=symbol,
 | 
				
			||||||
 | 
					            side=s_side,
 | 
				
			||||||
 | 
					            order_quantity=base_quantity,
 | 
				
			||||||
 | 
					            trigger_price=trigger_price,
 | 
				
			||||||
 | 
					            margin_type=margin_type,
 | 
				
			||||||
 | 
					            leverage=leverage,
 | 
				
			||||||
 | 
					            take_profit_percent=take_profit_percent,
 | 
				
			||||||
 | 
					            stop_loss_percent=stop_loss_percent,
 | 
				
			||||||
 | 
					            commission_fee_percent=total_fee
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if res == "OK":
 | 
				
			||||||
 | 
					            await rq.set_user_deal(
 | 
				
			||||||
 | 
					                tg_id=tg_id,
 | 
				
			||||||
 | 
					                symbol=symbol,
 | 
				
			||||||
 | 
					                last_side=side,
 | 
				
			||||||
 | 
					                current_step=1,
 | 
				
			||||||
 | 
					                trade_mode=trade_mode,
 | 
				
			||||||
 | 
					                side_mode=side_mode,
 | 
				
			||||||
 | 
					                margin_type=margin_type,
 | 
				
			||||||
 | 
					                leverage=leverage,
 | 
				
			||||||
 | 
					                order_quantity=base_quantity,
 | 
				
			||||||
 | 
					                trigger_price=trigger_price,
 | 
				
			||||||
 | 
					                martingale_factor=martingale_factor,
 | 
				
			||||||
 | 
					                max_bets_in_series=max_bets_in_series,
 | 
				
			||||||
 | 
					                take_profit_percent=take_profit_percent,
 | 
				
			||||||
 | 
					                stop_loss_percent=stop_loss_percent,
 | 
				
			||||||
 | 
					                base_quantity=base_quantity
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            return "OK"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return (
 | 
				
			||||||
 | 
					            res
 | 
				
			||||||
 | 
					            if res
 | 
				
			||||||
 | 
					               in {
 | 
				
			||||||
 | 
					                   "Risk is too high for this trade",
 | 
				
			||||||
 | 
					                   "ab not enough for new order",
 | 
				
			||||||
 | 
					                   "InvalidRequestError",
 | 
				
			||||||
 | 
					                   "The number of contracts exceeds maximum limit allowed",
 | 
				
			||||||
 | 
					               }
 | 
				
			||||||
 | 
					            else None
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    except Exception as e:
 | 
				
			||||||
 | 
					        logger.error("Error in trading_cycle_profit: %s", e)
 | 
				
			||||||
 | 
					        return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async def trading_cycle(
 | 
					async def trading_cycle(
 | 
				
			||||||
    tg_id: int, symbol: str, reverse_side: str
 | 
					        tg_id: int, symbol: str, 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)
 | 
				
			||||||
@@ -146,23 +229,7 @@ async def trading_cycle(
 | 
				
			|||||||
        current_step = user_deals_data.current_step
 | 
					        current_step = user_deals_data.current_step
 | 
				
			||||||
        order_quantity = user_deals_data.order_quantity
 | 
					        order_quantity = user_deals_data.order_quantity
 | 
				
			||||||
        base_quantity = user_deals_data.base_quantity
 | 
					        base_quantity = user_deals_data.base_quantity
 | 
				
			||||||
 | 
					        side_mode = user_deals_data.side_mode
 | 
				
			||||||
        await set_margin_mode(tg_id=tg_id, margin_mode=margin_type)
 | 
					 | 
				
			||||||
        await set_leverage(
 | 
					 | 
				
			||||||
            tg_id=tg_id,
 | 
					 | 
				
			||||||
            symbol=symbol,
 | 
					 | 
				
			||||||
            leverage=leverage,
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if reverse_side == "Buy":
 | 
					 | 
				
			||||||
            real_side = "Sell"
 | 
					 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            real_side = "Buy"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        side = real_side
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if trade_mode == "Switch":
 | 
					 | 
				
			||||||
            side = "Sell" if real_side == "Buy" else "Buy"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        next_quantity = safe_float(order_quantity) * (
 | 
					        next_quantity = safe_float(order_quantity) * (
 | 
				
			||||||
            safe_float(martingale_factor)
 | 
					            safe_float(martingale_factor)
 | 
				
			||||||
@@ -172,6 +239,13 @@ async def trading_cycle(
 | 
				
			|||||||
        if max_bets_in_series < current_step:
 | 
					        if max_bets_in_series < current_step:
 | 
				
			||||||
            return "Max bets in series"
 | 
					            return "Max bets in series"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        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,
 | 
				
			||||||
@@ -192,6 +266,7 @@ async def trading_cycle(
 | 
				
			|||||||
                last_side=side,
 | 
					                last_side=side,
 | 
				
			||||||
                current_step=current_step,
 | 
					                current_step=current_step,
 | 
				
			||||||
                trade_mode=trade_mode,
 | 
					                trade_mode=trade_mode,
 | 
				
			||||||
 | 
					                side_mode=side_mode,
 | 
				
			||||||
                margin_type=margin_type,
 | 
					                margin_type=margin_type,
 | 
				
			||||||
                leverage=leverage,
 | 
					                leverage=leverage,
 | 
				
			||||||
                order_quantity=next_quantity,
 | 
					                order_quantity=next_quantity,
 | 
				
			||||||
@@ -207,12 +282,12 @@ async def trading_cycle(
 | 
				
			|||||||
        return (
 | 
					        return (
 | 
				
			||||||
            res
 | 
					            res
 | 
				
			||||||
            if res
 | 
					            if res
 | 
				
			||||||
            in {
 | 
					               in {
 | 
				
			||||||
                "Risk is too high for this trade",
 | 
					                   "Risk is too high for this trade",
 | 
				
			||||||
                "ab not enough for new order",
 | 
					                   "ab not enough for new order",
 | 
				
			||||||
                "InvalidRequestError",
 | 
					                   "InvalidRequestError",
 | 
				
			||||||
                "The number of contracts exceeds maximum limit allowed",
 | 
					                   "The number of contracts exceeds maximum limit allowed",
 | 
				
			||||||
            }
 | 
					               }
 | 
				
			||||||
            else None
 | 
					            else None
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -222,16 +297,16 @@ async def trading_cycle(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async def open_positions(
 | 
					async def open_positions(
 | 
				
			||||||
    tg_id: int,
 | 
					        tg_id: int,
 | 
				
			||||||
    side: str,
 | 
					        side: str,
 | 
				
			||||||
    symbol: str,
 | 
					        symbol: str,
 | 
				
			||||||
    order_quantity: float,
 | 
					        order_quantity: float,
 | 
				
			||||||
    trigger_price: float,
 | 
					        trigger_price: float,
 | 
				
			||||||
    margin_type: str,
 | 
					        margin_type: str,
 | 
				
			||||||
    leverage: str,
 | 
					        leverage: str,
 | 
				
			||||||
    take_profit_percent: float,
 | 
					        take_profit_percent: float,
 | 
				
			||||||
    stop_loss_percent: float,
 | 
					        stop_loss_percent: float,
 | 
				
			||||||
    commission_fee_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)
 | 
				
			||||||
@@ -252,39 +327,28 @@ async def open_positions(
 | 
				
			|||||||
            po_trigger_price = None
 | 
					            po_trigger_price = None
 | 
				
			||||||
            trigger_direction = None
 | 
					            trigger_direction = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        get_leverage = safe_float(leverage)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        price_for_cals = trigger_price if po_trigger_price is not None else price_symbol
 | 
					        price_for_cals = trigger_price if po_trigger_price is not None else price_symbol
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if qty_formatted <= 0:
 | 
					        if qty_formatted <= 0:
 | 
				
			||||||
            return "Order does not meet minimum order value"
 | 
					            return "Order does not meet minimum order value"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if margin_type == "ISOLATED_MARGIN":
 | 
					        if margin_type == "ISOLATED_MARGIN":
 | 
				
			||||||
            liq_long, liq_short = await get_liquidation_price(
 | 
					            if side == "Buy":
 | 
				
			||||||
                tg_id=tg_id,
 | 
					                take_profit_price = price_for_cals * (
 | 
				
			||||||
                entry_price=price_for_cals,
 | 
					                        1 + take_profit_percent / 100) + commission_fee_percent / qty_formatted
 | 
				
			||||||
                symbol=symbol,
 | 
					                stop_loss_price = None
 | 
				
			||||||
                leverage=get_leverage,
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (liq_long > 0 or liq_short > 0) and price_for_cals > 0:
 | 
					 | 
				
			||||||
                if side == "Buy":
 | 
					 | 
				
			||||||
                    base_tp = price_for_cals + (price_for_cals - liq_long)
 | 
					 | 
				
			||||||
                    take_profit_price = base_tp + commission_fee_percent / qty_formatted
 | 
					 | 
				
			||||||
                else:
 | 
					 | 
				
			||||||
                    base_tp = price_for_cals - (liq_short - price_for_cals)
 | 
					 | 
				
			||||||
                    take_profit_price = base_tp - commission_fee_percent / qty_formatted
 | 
					 | 
				
			||||||
                take_profit_price = max(take_profit_price, 0)
 | 
					 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                take_profit_price = None
 | 
					                take_profit_price = price_for_cals * (
 | 
				
			||||||
 | 
					                        1 - take_profit_percent / 100) - commission_fee_percent / qty_formatted
 | 
				
			||||||
            stop_loss_price = None
 | 
					                stop_loss_price = None
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            if side == "Buy":
 | 
					            if side == "Buy":
 | 
				
			||||||
                take_profit_price = price_for_cals * (1 + take_profit_percent / 100) + commission_fee_percent / qty_formatted
 | 
					                take_profit_price = price_for_cals * (
 | 
				
			||||||
 | 
					                            1 + take_profit_percent / 100) + commission_fee_percent / qty_formatted
 | 
				
			||||||
                stop_loss_price = price_for_cals * (1 - stop_loss_percent / 100)
 | 
					                stop_loss_price = price_for_cals * (1 - stop_loss_percent / 100)
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                take_profit_price = price_for_cals * (1 - take_profit_percent / 100) - commission_fee_percent / qty_formatted
 | 
					                take_profit_price = price_for_cals * (
 | 
				
			||||||
 | 
					                            1 - take_profit_percent / 100) - commission_fee_percent / qty_formatted
 | 
				
			||||||
                stop_loss_price = price_for_cals * (1 + stop_loss_percent / 100)
 | 
					                stop_loss_price = price_for_cals * (1 + stop_loss_percent / 100)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            take_profit_price = max(take_profit_price, 0)
 | 
					            take_profit_price = max(take_profit_price, 0)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,7 +3,7 @@ import logging.config
 | 
				
			|||||||
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.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, trading_cycle_profit
 | 
				
			||||||
from app.helper_functions import format_value, safe_float
 | 
					from app.helper_functions import format_value, safe_float
 | 
				
			||||||
 | 
					
 | 
				
			||||||
logging.config.dictConfig(LOGGING_CONFIG)
 | 
					logging.config.dictConfig(LOGGING_CONFIG)
 | 
				
			||||||
@@ -57,7 +57,7 @@ class TelegramMessageHandler:
 | 
				
			|||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if user_deals_data is not None and auto_trading:
 | 
					            if user_deals_data is not None and auto_trading:
 | 
				
			||||||
                text += f"Текущая ставка: {user_deals_data.order_quantity}\n"
 | 
					                text += f"Текущая ставка: {user_deals_data.order_quantity} USDT\n"
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                text += f"Количество: {qty}\n"
 | 
					                text += f"Количество: {qty}\n"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -146,11 +146,12 @@ class TelegramMessageHandler:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            text += (
 | 
					            text += (
 | 
				
			||||||
                f"Цена исполнения: {exec_price}\n"
 | 
					                f"Цена исполнения: {exec_price}\n"
 | 
				
			||||||
                f"Движение: {side_rus}\n"
 | 
					 | 
				
			||||||
                f"Комиссия: {exec_fee:.8f}\n"
 | 
					                f"Комиссия: {exec_fee:.8f}\n"
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if safe_float(closed_size) > 0:
 | 
					            if safe_float(closed_size) == 0:
 | 
				
			||||||
 | 
					                text += f"Движение: {side_rus}\n"
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
                text += f"\nРеализованная прибыль: {total_pnl:.7f}\n"
 | 
					                text += f"\nРеализованная прибыль: {total_pnl:.7f}\n"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await self.telegram_bot.send_message(
 | 
					            await self.telegram_bot.send_message(
 | 
				
			||||||
@@ -165,31 +166,70 @@ class TelegramMessageHandler:
 | 
				
			|||||||
                and user_symbols is not None
 | 
					                and user_symbols is not None
 | 
				
			||||||
            ):
 | 
					            ):
 | 
				
			||||||
                if safe_float(total_pnl) > 0:
 | 
					                if safe_float(total_pnl) > 0:
 | 
				
			||||||
                    profit_text = "📈 Прибыль достигнута\n"
 | 
					                    profit_text = "📈 Прибыль достигнута. Начинаем новую серию с базовой ставки\n"
 | 
				
			||||||
                    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
 | 
				
			||||||
                    )
 | 
					                    )
 | 
				
			||||||
                    await rq.set_auto_trading(
 | 
					 | 
				
			||||||
                        tg_id=tg_id, symbol=symbol, auto_trading=False
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if side == "Buy":
 | 
				
			||||||
 | 
					                        r_side = "Sell"
 | 
				
			||||||
 | 
					                    else:
 | 
				
			||||||
 | 
					                        r_side = "Buy"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    await rq.set_last_side_by_symbol(
 | 
				
			||||||
 | 
					                        tg_id=tg_id, symbol=symbol, last_side=r_side)
 | 
				
			||||||
                    await rq.set_total_fee_user_auto_trading(
 | 
					                    await rq.set_total_fee_user_auto_trading(
 | 
				
			||||||
                        tg_id=tg_id, symbol=symbol, total_fee=0
 | 
					                        tg_id=tg_id, symbol=symbol, total_fee=0
 | 
				
			||||||
                    )
 | 
					                    )
 | 
				
			||||||
                    await rq.set_fee_user_auto_trading(
 | 
					                    await rq.set_fee_user_auto_trading(
 | 
				
			||||||
                        tg_id=tg_id, symbol=symbol, fee=0
 | 
					                        tg_id=tg_id, symbol=symbol, fee=0
 | 
				
			||||||
                    )
 | 
					                    )
 | 
				
			||||||
                    base_quantity = user_deals_data.base_quantity
 | 
					
 | 
				
			||||||
                    await rq.set_order_quantity(
 | 
					                    res = await trading_cycle_profit(
 | 
				
			||||||
                        tg_id=tg_id, order_quantity=base_quantity
 | 
					                        tg_id=tg_id, symbol=symbol, side=r_side
 | 
				
			||||||
                    )
 | 
					                    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if res == "OK":
 | 
				
			||||||
 | 
					                        pass
 | 
				
			||||||
 | 
					                    else:
 | 
				
			||||||
 | 
					                        errors = {
 | 
				
			||||||
 | 
					                            "Max bets in series": "❗️ Максимальное количество сделок в серии достигнуто",
 | 
				
			||||||
 | 
					                            "Risk is too high for this trade": "❗️ Риск сделки слишком высок для продолжения",
 | 
				
			||||||
 | 
					                            "ab not enough for new order": "❗️ Недостаточно средств для продолжения торговли",
 | 
				
			||||||
 | 
					                            "InvalidRequestError": "❗️ Недостаточно средств для размещения нового ордера с заданным количеством и плечом.",
 | 
				
			||||||
 | 
					                            "The number of contracts exceeds maximum limit allowed": "❗️ Превышен максимальный лимит ставки",
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        error_text = errors.get(
 | 
				
			||||||
 | 
					                            res, "❗️ Не удалось открыть новую сделку"
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                        await rq.set_auto_trading(
 | 
				
			||||||
 | 
					                            tg_id=tg_id, symbol=symbol, auto_trading=False
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        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 self.telegram_bot.send_message(
 | 
				
			||||||
 | 
					                            chat_id=tg_id,
 | 
				
			||||||
 | 
					                            text=error_text,
 | 
				
			||||||
 | 
					                            reply_markup=kbi.profile_bybit,
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
                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
 | 
				
			||||||
                    )
 | 
					                    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if side == "Buy":
 | 
				
			||||||
 | 
					                        r_side = "Sell"
 | 
				
			||||||
 | 
					                    else:
 | 
				
			||||||
 | 
					                        r_side = "Buy"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    res = await trading_cycle(
 | 
					                    res = await trading_cycle(
 | 
				
			||||||
                        tg_id=tg_id, symbol=symbol, reverse_side=side
 | 
					                        tg_id=tg_id, symbol=symbol, side=r_side
 | 
				
			||||||
                    )
 | 
					                    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if res == "OK":
 | 
					                    if res == "OK":
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -124,6 +124,8 @@ async def set_symbol(message: Message, state: FSMContext) -> None:
 | 
				
			|||||||
        risk_percent = 100 / safe_float(max_leverage)
 | 
					        risk_percent = 100 / safe_float(max_leverage)
 | 
				
			||||||
        await rq.set_stop_loss_percent(
 | 
					        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_take_profit_percent(
 | 
				
			||||||
 | 
					            tg_id=message.from_user.id, take_profit_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 rq.set_order_quantity(tg_id=message.from_user.id, order_quantity=1.0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,7 +43,7 @@ async def settings_for_trade_mode(
 | 
				
			|||||||
            text="Выберите режим торговли:\n\n"
 | 
					            text="Выберите режим торговли:\n\n"
 | 
				
			||||||
                 "Лонг - все сделки серии открываются на покупку.\n"
 | 
					                 "Лонг - все сделки серии открываются на покупку.\n"
 | 
				
			||||||
                 "Шорт - все сделки серии открываются на продажу.\n"
 | 
					                 "Шорт - все сделки серии открываются на продажу.\n"
 | 
				
			||||||
                 "Свитч - направление каждой сделки серии меняется по переменно.\n",
 | 
					                 "Свитч - направление первой сделки серии меняется по переменно.\n",
 | 
				
			||||||
            reply_markup=kbi.trade_mode,
 | 
					            reply_markup=kbi.trade_mode,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        logger.debug(
 | 
					        logger.debug(
 | 
				
			||||||
@@ -580,6 +580,8 @@ async def set_leverage_handler(message: Message, state: FSMContext) -> None:
 | 
				
			|||||||
            risk_percent = 100 / safe_float(leverage_float)
 | 
					            risk_percent = 100 / safe_float(leverage_float)
 | 
				
			||||||
            await rq.set_stop_loss_percent(
 | 
					            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_take_profit_percent(
 | 
				
			||||||
 | 
					                tg_id=message.from_user.id, take_profit_percent=risk_percent)
 | 
				
			||||||
            logger.info(
 | 
					            logger.info(
 | 
				
			||||||
                "User %s set leverage: %s", message.from_user.id, leverage_float
 | 
					                "User %s set leverage: %s", message.from_user.id, leverage_float
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -143,6 +143,7 @@ class UserDeals(Base):
 | 
				
			|||||||
    current_step = Column(Integer, nullable=True)
 | 
					    current_step = Column(Integer, nullable=True)
 | 
				
			||||||
    symbol = Column(String, nullable=True)
 | 
					    symbol = Column(String, nullable=True)
 | 
				
			||||||
    trade_mode = Column(String, nullable=True)
 | 
					    trade_mode = Column(String, nullable=True)
 | 
				
			||||||
 | 
					    side_mode = Column(String, nullable=True)
 | 
				
			||||||
    base_quantity = Column(Float, nullable=True)
 | 
					    base_quantity = Column(Float, nullable=True)
 | 
				
			||||||
    margin_type = Column(String, nullable=True)
 | 
					    margin_type = Column(String, nullable=True)
 | 
				
			||||||
    leverage = Column(String, nullable=True)
 | 
					    leverage = Column(String, nullable=True)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -898,6 +898,7 @@ async def set_user_deal(
 | 
				
			|||||||
        last_side: str,
 | 
					        last_side: str,
 | 
				
			||||||
        current_step: int,
 | 
					        current_step: int,
 | 
				
			||||||
        trade_mode: str,
 | 
					        trade_mode: str,
 | 
				
			||||||
 | 
					        side_mode: str,
 | 
				
			||||||
        margin_type: str,
 | 
					        margin_type: str,
 | 
				
			||||||
        leverage: str,
 | 
					        leverage: str,
 | 
				
			||||||
        order_quantity: float,
 | 
					        order_quantity: float,
 | 
				
			||||||
@@ -915,6 +916,7 @@ async def set_user_deal(
 | 
				
			|||||||
    :param last_side: Last side
 | 
					    :param last_side: Last side
 | 
				
			||||||
    :param current_step: Current step
 | 
					    :param current_step: Current step
 | 
				
			||||||
    :param trade_mode: Trade mode
 | 
					    :param trade_mode: Trade mode
 | 
				
			||||||
 | 
					    :param side_mode: Side mode
 | 
				
			||||||
    :param margin_type: Margin type
 | 
					    :param margin_type: Margin type
 | 
				
			||||||
    :param leverage: Leverage
 | 
					    :param leverage: Leverage
 | 
				
			||||||
    :param order_quantity: Order quantity
 | 
					    :param order_quantity: Order quantity
 | 
				
			||||||
@@ -944,6 +946,7 @@ async def set_user_deal(
 | 
				
			|||||||
                deal.last_side = last_side
 | 
					                deal.last_side = last_side
 | 
				
			||||||
                deal.current_step = current_step
 | 
					                deal.current_step = current_step
 | 
				
			||||||
                deal.trade_mode = trade_mode
 | 
					                deal.trade_mode = trade_mode
 | 
				
			||||||
 | 
					                deal.side_mode = side_mode
 | 
				
			||||||
                deal.margin_type = margin_type
 | 
					                deal.margin_type = margin_type
 | 
				
			||||||
                deal.leverage = leverage
 | 
					                deal.leverage = leverage
 | 
				
			||||||
                deal.order_quantity = order_quantity
 | 
					                deal.order_quantity = order_quantity
 | 
				
			||||||
@@ -961,6 +964,7 @@ async def set_user_deal(
 | 
				
			|||||||
                    last_side=last_side,
 | 
					                    last_side=last_side,
 | 
				
			||||||
                    current_step=current_step,
 | 
					                    current_step=current_step,
 | 
				
			||||||
                    trade_mode=trade_mode,
 | 
					                    trade_mode=trade_mode,
 | 
				
			||||||
 | 
					                    side_mode=side_mode,
 | 
				
			||||||
                    margin_type=margin_type,
 | 
					                    margin_type=margin_type,
 | 
				
			||||||
                    leverage=leverage,
 | 
					                    leverage=leverage,
 | 
				
			||||||
                    order_quantity=order_quantity,
 | 
					                    order_quantity=order_quantity,
 | 
				
			||||||
@@ -1050,6 +1054,34 @@ async def set_fee_user_deal_by_symbol(tg_id: int, symbol: str, fee: float):
 | 
				
			|||||||
        return False
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async def set_last_side_by_symbol(tg_id: int, symbol: str, last_side: str):
 | 
				
			||||||
 | 
					    """Set last side for a user deal by symbol in the database."""
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        async with async_session() as session:
 | 
				
			||||||
 | 
					            result = await session.execute(select(User).filter_by(tg_id=tg_id))
 | 
				
			||||||
 | 
					            user = result.scalars().first()
 | 
				
			||||||
 | 
					            if user is None:
 | 
				
			||||||
 | 
					                logger.error(f"User with tg_id={tg_id} not found")
 | 
				
			||||||
 | 
					                return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            result = await session.execute(
 | 
				
			||||||
 | 
					                select(UserDeals).filter_by(user_id=user.id, symbol=symbol)
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            record = result.scalars().first()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if record:
 | 
				
			||||||
 | 
					                record.last_side = last_side
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                logger.error(f"User deal with user_id={user.id} and symbol={symbol} not found")
 | 
				
			||||||
 | 
					                return False
 | 
				
			||||||
 | 
					            await session.commit()
 | 
				
			||||||
 | 
					            logger.info("Set last side for user %s and symbol %s", tg_id, symbol)
 | 
				
			||||||
 | 
					            return True
 | 
				
			||||||
 | 
					    except Exception as e:
 | 
				
			||||||
 | 
					        logger.error("Error setting user deal last side for user %s and symbol %s: %s", tg_id, symbol, e)
 | 
				
			||||||
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# USER AUTO TRADING
 | 
					# USER AUTO TRADING
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async def get_all_user_auto_trading(tg_id: int):
 | 
					async def get_all_user_auto_trading(tg_id: int):
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user