Multiple order failed in creating JSON list for batchOrders parameter

Dear all,

I am trying to place a multiple order composed of a market order and a trailing stop loss order of the opposite side.

I fail in putting the parameter batchOrders in JSON format. I get the error message:
TypeError: Object of type Decimal is not JSON serializable

I suspect it is because one of my parameter (quantity) is in Decimal format. Any idea how can I solve this? Many thanks in advance.

please find below the code used:

	def PlaceFutureBatchOrder(self, log_filename, symbol:str, side:str, Order_type:str, quantity:float=0, price:float=0):

		params1 = {
			'symbol': symbol,
			'side': side, 			# BUY or SELL
			'type': Order_type,				# MARKET, LIMIT, STOP_LOSS etc
			'quantity': quantity,
		}

		params2 = {
			'symbol': symbol,
			'type': "TRAILING_STOP_MARKET",
			'quantity': quantity,
			'callbackRate': 0.9
		}

		if params1['side'] == "SELL":
			params2['side'] = "BUY"
		else:
			params2['side'] = "SELL"

		if Order_type != 'MARKET' and Order_type != 'STOP_LOSS' and Order_type != 'TAKE_PROFIT':
			params1['timeInForce'] = 'GTC'
			params1['price'] = self.floatToString(price)

		if Order_type == 'STOP_LOSS' or Order_type == 'TAKE_PROFIT':
			params1['stopPrice'] = self.floatToString(price)

		param_head = [params1, params2]
		json.dumps(param_head)

		params = {
			'batchOrders': param_head,
			'recvWindow': 5000,
			'timestamp': int(round(time.time()*1000))
		}

		self.signRequest(params)


		url = self.base + self.endpoints['BatchOrder']
		
		try:
			response = requests.post(url, params=params, headers=self.headers)
			data = response.text
						
		except Exception as e:
			print(" Exception occurred when trying to place order on "+url)
			print(e)
			data = {'code': '-1', 'msg':e}

do you mind print out how is your url looks like, please hide the signature.

I get an error message when the code gets to json.dumps(param_head), before the url definition.

so if I comment that json.dump command, the url I get is:
https://testnet.binancefuture.com/fapi/v1/batchOrders

parameters and headers are added with the function requests.post but I don’t know how to make requests display them if this is what you wanted to see.

many thanks for your reply

Somewhere in your code you are using Decimal object (I could reproduce your issue). Probably your quantity parameter (despite your function is waiting for a float).

Try to reuse your self.floatToString() method on the quantity parameters ( in params1 and params2 ):

		params1 = {
		'symbol': symbol,
		'side': side, 			# BUY or SELL
		'type': Order_type,				# MARKET, LIMIT, STOP_LOSS etc
		'quantity': self.floatToString(quantity),
	}

	params2 = {
		'symbol': symbol,
		'type': "TRAILING_STOP_MARKET",
		'quantity': self.floatToString(quantity),
		'callbackRate': 0.9
	}

Or time to create a self.decimalToString() function.

Many thanks for your answer.
In the binance API guide (https://binance-docs.github.io) they say quantity should be of type decimal, but from the error message I received it seems that json.dumps is not accepting decimals (type not serializable).
I am not sure if translating the parameter to a string will be accepted by binance, for example, if I convert to float I get:
{‘code’: -1022, ‘msg’: ‘Signature for this request is not valid.’}
and I guess this is because Binance is expecting a Decimal value in the parameter quantity.
What do you think?

No, by decimal, it’s mean the mathematical form, that is not an integer, and it’s not the Python Decimal object. You can send float or string it doesn’t matter. Which mean your json.dumps issue is solved.

About your Signature for this request is not valid is a different issue. Your signature is not valid.
You need to puts some logs around your self.signRequest(params)
Somehow your code is not clear: it should be something like that:

signed_params = self.signRequest(params)

With the current code you share with us with have no idea if the params got signed or not.
Also since you manage to post your request you can share the log of your request ( not the signature and key please ).

Sorry Alexis, I am not very good in Python, I am trying to give you additional information.
This is my signrequest function:

def signRequest(self, params:dict):
query_string = '&'.join(["{}={}".format(d, params[d]) for d in params])
signature = hmac.new(testnet_binance_keys['secret_key'].encode('utf-8'), query_string.encode('utf-8'), hashlib.sha256)
params['signature'] = signature.hexdigest()	

I used the same signRequest function that worked with my “new order” function to place a single order. I guess this is the problem, In the single order function you are not supposed to have a json list in the parameters.

If add to my code:

param_signed = self.signRequest(params)
print(param_signed)

I get:

None

Is this what you asked with "some logs around my self.signRequest(params)?
If not, please be so kind to guide me in printing what you wish to read from the code.
Many thanks again for your patience.

you should return the params from

def signRequest(self, params:dict):
  query_string = '&'.join(["{}={}".format(d, params[d]) for d in params])
  signature = hmac.new(testnet_binance_keys['secret_key'].encode('utf-8'), query_string.encode('utf-8'), hashlib.sha256)
  params['signature'] = signature.hexdigest()
  return params // you should return the params	

also add print(params) before it.
I am nearly sure your param_head is still an array when what you want is a json object:

        param_head_json = json.dumps(param_head)
		params = {
			'batchOrders': param_head_json,
			'recvWindow': 5000,
			'timestamp': int(round(time.time()*1000))
		}

As suggested, I have put the return params line at the end of my def signRequest(self, params:dict):

moreover I have put print(params) after the definition of params (the piece of code that Alexis has posted in his reply)).

This is the result:
{‘batchOrders’: [{‘symbol’: ‘TRXUSDT’, ‘side’: ‘SELL’, ‘type’: ‘MARKET’, ‘quantity’: 80.0}, {‘symbol’: ‘TRXUSDT’, ‘type’: ‘TRAILING_STOP_MARKET’, ‘quantity’: 80.0, ‘callbackRate’: 0.9, ‘side’: ‘BUY’}], ‘recvWindow’: 5000, ‘timestamp’: 1586987007098

this is the error message I receive:
{‘code’: -1022, ‘msg’: ‘Signature for this request is not valid.’}

Does this mean, as Alexis suspected that the problem is that my param_head is still an array and not a json object? Any idea how can I fix this?

Many thanks for your patience and support

Try to replace your json.dumps(param_head)

with

param_head_json = json.dumps(param_head, separators=(",", ":"))

Also, share again your full code, because I am lost about what you change and what you didn’t.

For batchOrders, hope this topic helps

I have been beating my head against the wall with the same issue. That is, that the signature fails when using batchOrders. I have gotten to the conclusion that the string should be setup differently when calculating the signature as compared to in the url. But I cannot find any information on how e.g. @dino would write the string that is used for calculating the signature provided in the example.

Currently I am providing this query_string when calculating the signature:
batchOrders=[{“symbol”:“BTCUSDT”,“side”:“SELL”,“type”:“MARKET”,“quantity”:“0.001”,“reduceOnly”:“False”}]&timestamp=1591910444596

and it is calculated with the following code-snippet from python-binance:
m = hmac.new(self.API_SECRET.encode(‘utf-8’), query_string.encode(‘utf-8’), hashlib.sha256)

Does anyone happen to know if there is something obviously wrong in my query_string?

Ah, thank you. I assumed that the .encode(‘utf-8’) would perform that operation for me. But apparently it required me to manually replace the {} and " before generating the signature.

Sorry, I found the original encoding strategies is not available. It’s asking for standard url encoding for the json string, this example script is working for now.

Hi @dino .
I got error with signature when I try to use batchOrders endpoint.
It normally works on placing normal singular order.

I saw example for python and I checked what query string is generated and after encoding is the same like mine.

URLs before decoding:
(mine is on top and from python example on bottom)

URLs after decoding:

I can’t see difference in structure between these two query strings.

The signature has to be generated on the encoded query string.

@tantialex This is how query string looks like before generating signature.

batchOrders=[{"quantity":4,"timeInForce":"GTC","side":"SELL","symbol":"KNCUSDT","price":1.7,"type":"LIMIT"},{"quantity":4,"timeInForce":"GTC","side":"SELL","symbol":"KNCUSDT","price":1.75,"type":"LIMIT"},{"quantity":4,"timeInForce":"GTC","side":"SELL","symbol":"KNCUSDT","price":1.78,"type":"LIMIT"}]&timestamp=1652695226537

Have a look at this thread.

Did you find the solution.