Bitget CCXT частично закрывает позицию

У меня странная проблема с торговлей на Bitget с помощью CCXT.

Я разрабатываю бота на Python, который автоматизирует некоторые сигналы из группы телеграмм с моего ноутбука (Win 11, используя VS), и дело в том, что у меня код работает как положено, ордер на вход выполняется, тейк-профит тоже работает как положено. ... но затем, когда я развертываю код (или просто запускаю его в VS) на Intel NUC, на котором работают мои докер-контейнеры, он продолжает жаловаться на одностороннюю сторону и/или на несоответствие сторон. **Я «жестко» вошел в длинную позицию, выстрелил, купил, продал, но происходит то же самое.

Если я проверю код на третьей машине, он будет работать как положено (я скопировал код непосредственно с NUC, где получил ошибки).

Ниже приведен порядок входа:

Trying to execute order: (29, 5, 'LDOUSDT', 'short', 1.9725, 76.0, 15.0, 'Open', 'entry')
Order response: {'info': {'clientOid': '1191025251500830722', 'orderId': '1191025251484053505'}, 'id': '1191025251484053505', 'clientOrderId': '1191025251500830722', 'timestamp': None, 'datetime': None, 'lastTradeTimestamp': None, 'lastUpdateTimestamp': None, 'symbol': 'LDO/USDT:USDT', 'type': None, 'side': None, 'price': None, 'amount': None, 'cost': None, 'average': None, 'filled': None, 'remaining': None, 'timeInForce': None, 'postOnly': None, 'reduceOnly': None, 'stopPrice': None, 'triggerPrice': None, 'takeProfitPrice': None, 'stopLossPrice': None, 'status': None, 'fee': None, 'trades': [], 'fees': []}

Запись работает как положено на всех трёх машинах. У меня есть код, позволяющий попытаться получить прибыль (чтобы убедиться, что функция действительно работает):

Order: (31, 5, 'LDOUSDT', 'buy', 1.9338, 5.7, 15.0, 'Open', 'takeprofit')
{'code': '00000', 'msg': 'success', 'requestTime': '1719688487060', 'data': {'posMode': 'hedge_mode'}}
Take-profit order response: {'info': {'clientOid': '1191025332270542852', 'orderId': '1191025332270542849'}, 'id': '1191025332270542849', 'clientOrderId': '1191025332270542852', 'timestamp': None, 'datetime': None, 'lastTradeTimestamp': None, 'lastUpdateTimestamp': None, 'symbol': 'LDO/USDT:USDT', 'type': None, 'side': None, 'price': None, 'amount': None, 'cost': None, 'average': None, 'filled': None, 'remaining': None, 'timeInForce': None, 'postOnly': None, 'reduceOnly': None, 'stopPrice': None, 'triggerPrice': None, 'takeProfitPrice': None, 'stopLossPrice': None, 'status': None, 'fee': None, 'trades': [], 'fees': []}

Вышеупомянутое было выполнено на ноутбуке, где я написал код... затем попытался выполнить то же самое на Intel NUC:

Trying to execute order: (29, 5, 'LDOUSDT', 'short', 1.9725, 76.0, 15.0, 'Open', 'entry')
Order response: {'info': {'clientOid': '1191029181727256581', 'orderId': '1191029181710479368'}, 'id': '1191029181710479368', 'clientOrderId': '1191029181727256581', 'timestamp': None, 'datetime': None, 'lastTradeTimestamp': None, 'lastUpdateTimestamp': None, 'symbol': 'LDO/USDT:USDT', 'type': None, 'side': None, 'price': None, 'amount': None, 'cost': None, 'average': None, 'filled': None, 'remaining': None, 'timeInForce': None, 'postOnly': None, 'reduceOnly': None, 'stopPrice': None, 'triggerPrice': None, 'takeProfitPrice': None, 'stopLossPrice': None, 'status': None, 'fee': None, 'trades': [], 'fees': []}

Итак, вход сработал, как и ожидалось, ниже я дважды проверяю, находимся ли мы в режиме хеджирования:

Order: (31, 5, 'LDOUSDT', 'buy', 1.9338, 5.7, 15.0, 'Open', 'takeprofit')
{'code': '00000', 'msg': 'success', 'requestTime': '1719689424364', 'data': {'posMode': 'hedge_mode'}}

и наконец:

An error occurred: bitget {"code":"40774","msg":"The order type for unilateral position must also be the unilateral position type.","requestTime":1719689424722,"data":null}

Ниже приведен тот же код, который я тестировал на всех трех машинах. Он работает как на моем ноутбуке, так и на настольном компьютере, но не на NUC:

async def execute_order(userId, order):
    """Executes a futures order for a user based on the signal received, with specified leverage and margin mode."""
    user = await get_user_data(userId)
    print(f"User: {user}")
    print(f"Trying to execute order: {order}")
    if user:
        apiKey = decrypt(user[2])
        secret = decrypt(user[3])
        password = decrypt(user[4])
        bitget = ccxt.bitget({
            'apiKey': apiKey,
            'secret': secret,
            'password': password
        })
        

        try:
            symbol = order[2]
            size = order[5]
            price = order[4]
            margin_coin = 'USDT'  # Default margin coin
            margin_mode = 'cross'
            side = 'buy' if order[3] == 'long' else 'sell'
            hold_side = 'long' if order[3] == 'long' else 'short'
            pos_side = hold_side  # Ensuring posSide matches the holdSide
            
            bitget.options['defaultType'] = 'swap'
            # Set position mode
            bitget.set_position_mode('False', order[2])
            # Set leverage
            bitget.set_leverage(15, symbol, {
                'marginMode': margin_mode, 
                'marginCoin': margin_coin,
                'holdSide': hold_side
            })

            parms = {
                'holdSide': hold_side,
                'marginMode': margin_mode,
                'marginCoin': margin_coin,
                'posSide': pos_side,
                'tradeSide': 'open',
                'productType': 'USDT-FUTURES'
            }

            #print(f"Setting Leverage: {leverage_response}")

            # Place the futures order
            response = bitget.create_order(symbol, 'market', side, size, price, parms)
            
            print(f"Order response: {response}")
            #await telegram_notifier(f"Order executed for user {userId}: {response}")
        except Exception as e:
            error_message = f"An unexpected error occurred: {e}"
            print(error_message)
            #await telegram_notifier(f"Error executing futures order for user {userId}: {error_message}")
    return None



async def take_profit(userId, tp_order):
    """Sets a take-profit order for the given futures position."""
    user = await get_user_data(userId)
    if user:
        apiKey = decrypt(user[2])
        secret = decrypt(user[3])
        password = decrypt(user[4])
        bitget = ccxt.bitget({
            'apiKey': apiKey,
            'secret': secret,
            'password': password
        })
        
        bitget.options['defaultType'] = 'swap'
        # Set position mode
        hedge = bitget.set_position_mode('False', tp_order[2])
        print(hedge)

        try:
            symbol = tp_order[2]
            size = tp_order[5]
            price = tp_order[4]
            side = tp_order[3]
            hold_side = 'long' if side == 'sell' else 'short'  # Take profit on the opposite side
            
            # Order parameters
            type = 'market'  # or 'limit'
            amount = size  # Amount you want to close, adjust as necessary
            price = None  # Relevant for limit orders
            
            # Additional parameters specific to Bitget
            params = {
                'tdMode': 'cross',  # or 'cross'
                'reduceOnly': True,  # This ensures that the order can only reduce a position  

            }

            # Create the order
            order = bitget.create_order(symbol, type, side, amount, price, params)
            print(f"Take-profit order response: {order}")
            
            if tp_order[8] == 'takeprofit-msl':
                await update_stoploss_order_price(tp_order[1])
                


            return None

        except ccxt.BaseError as e:
            error_message = f"An error occurred: {e}"
            print(error_message)
            #await telegram_notifier(f"Error setting take-profit order for user {userId}: {error_message}")
    return None

Наиболее распространенное предложение, которое я нашел, — это использовать односторонний режим, но мне это действительно не хочется, особенно зная, что он работает на двух компьютерах, но не на NUC. Источник: Закройте позицию Bitget Futures с помощью ccxt и https://tickerly.net/errors/

Еще я пробовал просто жестко запрограммировать HoldSide, tradeSide и т. д., как это предложено в документации API Bitget, но затем он просто жалуется на несоответствие сторон, независимо от значения, если я не изменю Long/Short на Buy/Sell, где это просто утверждает, что значение недействительно.

async def take_profit(userId, tp_order):
    """Sets a take-profit order for the given futures position."""
    user = await get_user_data(userId)
    if user:
        apiKey = decrypt(user[2])
        secret = decrypt(user[3])
        password = decrypt(user[4])
        bitget = ccxt.bitget({
            'apiKey': apiKey,
            'secret': secret,
            'password': password
        })
        
        bitget.options['defaultType'] = 'swap'
        # Set position mode
        hedge = bitget.set_position_mode('False', tp_order[2])
        print(hedge)

        try:
            symbol = tp_order[2]
            size = tp_order[5]
            price = tp_order[4]
            side = 'long'#tp_order[3]
            hold_side = 'long' if side == 'sell' else 'short'  # Take profit on the opposite side
            
            # Order parameters
            type = 'market'  # or 'limit'
            amount = size  # Amount you want to close, adjust as necessary
            price = None  # Relevant for limit orders
            
            # Additional parameters specific to Bitget
            params = {
                'tdMode': 'cross',  # or 'cross'
                'reduceOnly': True,  # This ensures that the order can only reduce a position  

            }

            # Create the order
            order = bitget.create_order(symbol, type, side, amount, price, params)
            print(f"Take-profit order response: {order}")
            
            if tp_order[8] == 'takeprofit-msl':
                await update_stoploss_order_price(tp_order[1])
                


            return None

        except ccxt.BaseError as e:
            error_message = f"An error occurred: {e}"
            print(error_message)
            #await telegram_notifier(f"Error setting take-profit order for user {userId}: {error_message}")
    return None

возвращает следующее:

Trying to execute order: (29, 5, 'LDOUSDT', 'short', 1.9725, 76.0, 15.0, 'Open', 'entry')
Order response: {'info': {'clientOid': '1191031910600159232', 'orderId': '1191031910583382017'}, 'id': '1191031910583382017', 'clientOrderId': '1191031910600159232', 'timestamp': None, 'datetime': None, 'lastTradeTimestamp': None, 'lastUpdateTimestamp': None, 'symbol': 'LDO/USDT:USDT', 'type': None, 'side': None, 'price': None, 'amount': None, 'cost': None, 'average': None, 'filled': None, 'remaining': None, 'timeInForce': None, 'postOnly': None, 'reduceOnly': None, 'stopPrice': None, 'triggerPrice': None, 'takeProfitPrice': None, 'stopLossPrice': None, 'status': None, 'fee': None, 'trades': [], 'fees': []}
Order: (31, 5, 'LDOUSDT', 'buy', 1.9338, 5.7, 15.0, 'Open', 'takeprofit')
{'code': '00000', 'msg': 'success', 'requestTime': '1719690075905', 'data': {'posMode': 'hedge_mode'}}
An error occurred: bitget {"code":"400172","msg":"side mismatch","requestTime":1719690076235,"data":null}

Вышеупомянутое должно быть несоответствием, поскольку оба короткие. Я пробовал ввести Long и все другие возможные значения, но проблема та же.

Как упоминалось ранее, я проверил документацию Bitgets API и попробовал дополнительные параметры, как они предлагают, но все равно ничего. Документация по API здесь: https://docs.ccxt.com/#/exchanges/bitget?id=createorder

Думаю, еще одним исключением было следующее:

Но когда я попробовал его использовать, он заявил, что открытой позиции нет.

Как я могу устранить эту неполадку?

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
0
67
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Похоже, это ошибка в библиотеке CCXT при торговле односторонними позициями, здесь требуется ссылка на параметр tradeSide: https://github.com/ccxt/ccxt/issues/19140#issuecomment-1882527221

так как это мой код тейк-профита, у меня tradeSide статический 'close'

Мой код работает следующим образом:

async def take_profit(userId, tp_order):
    """Sets a take-profit order for the given futures position."""
    user = await get_user_data(userId)
    if user:
        apiKey = decrypt(user[2])
        secret = decrypt(user[3])
        password = decrypt(user[4])
        bitget = ccxt.bitget({
            'apiKey': apiKey,
            'secret': secret,
            'password': password
        })
        
        bitget.options['defaultType'] = 'swap'
        # Set position mode
        bitget.set_position_mode('False', tp_order[2])

        try:
            symbol = tp_order[2]
            size = tp_order[5]
            price = tp_order[4]
            side = tp_order[3]
            
            # Order parameters
            type = 'market'  # or 'limit'
            amount = size  # Amount you want to close, adjust as necessary
            price = None  # Relevant for limit orders
            
            # Additional parameters specific to Bitget
            params = {
                'tdMode': 'cross',  # or 'cross'
                'reduceOnly': True,  # This ensures that the order can only reduce a position
                'tradeSide': 'close',
                'productType': 'USDT-FUTURES'
            }

            # Create the order
            order = bitget.create_order(symbol, type, side, amount, price, params)
            print(f"Take-profit order response: {order}")
            
            if tp_order[8] == 'takeprofit-msl':
                await update_stoploss_order_price(tp_order[1])
               
            return None

        except ccxt.BaseError as e:
            error_message = f"An error occurred: {e}"
            print(error_message)
            #await telegram_notifier(f"Error setting take-profit order for user {userId}: {error_message}")
    return None

Обратите внимание, что сторона должна иметь значение «купить» для длинных позиций и «продать» для коротких. Я не уверен на 100%, почему простое прохождение длинной или короткой позиции не работает и приводит к «ошибке несоответствия сторон».

Другие вопросы по теме