Question
Bitget CCXT partially close position
I have a strange issue trading on Bitget with CCXT.
I'm developing a python bot that automates some signals from a telegram group from my laptop (win 11, using VS), and the ting is that I have the code working as expected, Entry order executes, Take Profits works as expected as well... but then when I deploy the code (or just run it in VS) on the intel NUC that runs my docker containers, it keeps complaining about Unilateral side, and/or about side mismatch. **I have 'hard' entered long, shot, buy, sell, but the same happens.
If I test the code on a 3rd machine it works as expected (I copied the code directly off the NUC where I get the errors).
Below is the entry order:
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': []}
The entry works as expected on all 3 machines. I have the code then to try and take profit (to ensure that the function actually works), as follows:
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': []}
The above was executed on the laptop where I wrote the code... then trying to execute the exact same thing on the 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': []}
So Entry worked as expected, below I double check to ensure we are in hedge mode:
Order: (31, 5, 'LDOUSDT', 'buy', 1.9338, 5.7, 15.0, 'Open', 'takeprofit')
{'code': '00000', 'msg': 'success', 'requestTime': '1719689424364', 'data': {'posMode': 'hedge_mode'}}
and then finally:
An error occurred: bitget {"code":"40774","msg":"The order type for unilateral position must also be the unilateral position type.","requestTime":1719689424722,"data":null}
Below is the same code that I tested on all 3 machines, Works on both my laptop and the Desktop, but not the 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
The most common suggestion I found is to use One Way Mode, but I really don't want to, especially knowing that it works on 2 computers but not the NUC. Source: Close position Bitget Futures using ccxt and https://tickerly.net/errors/
The other thing I have tried is just hard coding the holdSide, tradeSide, etc. as suggested by bitget's API documentation, but then it just complains about side missmatch, regardless of value, unless I change Long/Short into Buy/Sell, where it just states value is invalid.
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
returns the following:
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}
The above should be a mismatch, as both are short. I have tried entering Long and all other possible values, but the same issue.
As mentioned before I checked Bitgets API documentation and tried the additional parameters as they suggest but still nothing. API docs here: https://docs.ccxt.com/#/exchanges/bitget?id=createorder
I guess another exception was this:
But when I tried using it, it stated that there is no open position.
How I can further troubleshoot this?