A partial fill order seems to be a common problem that has been discussed on Reddit.
The following is from the API documentation related to an order_limit_buy
, which you are executing.
order_limit_buy(timeInForce='GTC', **params)[source]
Send in a new limit buy order
Any order with an icebergQty MUST have timeInForce set to GTC.
Parameters:
- symbol (str) – required
- quantity (decimal) – required
- price (str) – required
- timeInForce (str) – default Good till cancelled
- newClientOrderId (str) – A unique id for the order. Automatically generated if not sent.
- stopPrice (decimal) – Used with stop orders
- icebergQty (decimal) – Used with iceberg orders
- newOrderRespType (str) – Set the response JSON. ACK, RESULT, or FULL; default: RESULT.
- recvWindow (int) – the number of milliseconds the request is valid for
Returns:. API response
See order endpoint for full response options
Raises:
- BinanceRequestException
- BinanceAPIException
- BinanceOrderException
- BinanceOrderMinAmountException
- BinanceOrderMinPriceException
- BinanceOrderMinTotalException
- BinanceOrderUnknownSymbolException
- BinanceOrderInactiveSymbolException
Below is the source code for the order_limit_buy function
def order_limit_buy(self, timeInForce=BaseClient.TIME_IN_FORCE_GTC, **params):
"""Send in a new limit buy order
Any order with an icebergQty MUST have timeInForce set to GTC.
:param symbol: required
:type symbol: str
:param quantity: required
:type quantity: decimal
:param price: required
:type price: str
:param timeInForce: default Good till cancelled
:type timeInForce: str
:param newClientOrderId: A unique id for the order. Automatically generated if not sent.
:type newClientOrderId: str
:param stopPrice: Used with stop orders
:type stopPrice: decimal
:param icebergQty: Used with iceberg orders
:type icebergQty: decimal
:param newOrderRespType: Set the response JSON. ACK, RESULT, or FULL; default: RESULT.
:type newOrderRespType: str
:param recvWindow: the number of milliseconds the request is valid for
:type recvWindow: int
:returns: API response
See order endpoint for full response options
:raises: BinanceRequestException, BinanceAPIException, BinanceOrderException, BinanceOrderMinAmountException, BinanceOrderMinPriceException, BinanceOrderMinTotalException, BinanceOrderUnknownSymbolException, BinanceOrderInactiveSymbolException
"""
params.update({
'side': self.SIDE_BUY,
})
return self.order_limit(timeInForce=timeInForce, **params)
Neither the API parameters or the Python order_limit_buy
function make it clear how to prevent the partial fill order issue.
Here is your buy order:
order=self._get_auth_client(account).order_limit_buy(symbol=formatted_name,
quantity=amount,
price=fiat_price)
Your order has the 3 required parameters as stated in the API documentation:
- symbol (str) – required
- quantity (decimal) – required
- price (str) – required
I found the article What Is a Stop-Limit Order? on the Binance Academy website. The article had this statement:
If you're worried about your orders only partially filling, consider
using fill or kill.
Based on this statement I started looking through the API documentation and the source code for how to set either a FILL
or KILL
order.
I noted that the Python order_limit_buy
function has this parameter:
:param timeInForce: default Good till cancelled
:type timeInForce: str
The default value is Good till cancelled
or GTC
.
Looking at the API source code I found that the timeInForce
parameter has 3 possible values:
TIME_IN_FORCE_GTC = 'GTC' # Good till cancelled
TIME_IN_FORCE_IOC = 'IOC' # Immediate or cancel
TIME_IN_FORCE_FOK = 'FOK' # Fill or kill
Note the value TIME_IN_FORCE_FOK
or FOK
.
The following is from the Binance API documentation on GitHub:
Time in force (timeInForce):
This sets how long an order will be active before expiration.
Status |
Description |
GTC |
Good Till Canceled
An order will be on the book unless the order is canceled. |
IOC |
Immediate Or Cancel
An order will try to fill the order as much as it can before the order expires. |
FOK |
Fill or Kill
An order will expire if the full order cannot be filled upon execution |
Your buy request should look like this when using the timeInForce
parameter with the value FOK
:
order=self._get_auth_client(account).order_limit_buy(symbol=formatted_name,
quantity=amount,
price=fiat_price,
timeInForce='FOK')
I created a Binance TestNet Account and developed the code below as a test. I set my target price at 2687.00 to buy ETHUSDT. I used a loop to place my limited buy and to check to see if it was filled.
from binance.client import Client
api_key = 'my key'
api_secret = 'my secret'
client = Client(api_key, api_secret, testnet=True)
order_status = True
while True:
limit_order = client.order_limit_buy(symbol="ETHUSDT", quantity=0.01, price='2687.00', timeInForce='FOK')
ticker = client.get_ticker(symbol="ETHUSDT")
print(f"Current Price: {ticker.get('askPrice')}")
print(limit_order)
_status = limit_order.get('status')
if _status == 'FILLED':
order_status = False
print(order_status)
break
elif _status == 'EXPIRED':
order_status = True
The output from the code above is below:
NOTE: this is a snippet of the output, because the loop will run until the buy order triggers.
Current Price: 2687.33000000
{'symbol': 'ETHUSDT', 'orderId': 962373, 'orderListId': -1, 'clientOrderId': 'nW7bI2tkTwQEvrb8sSqgM6', 'transactTime': 1651927994444, 'price': '2687.00000000', 'origQty': '0.01000000', 'executedQty': '0.00000000', 'cummulativeQuoteQty': '0.00000000', 'status': 'EXPIRED', 'timeInForce': 'FOK', 'type': 'LIMIT', 'side': 'BUY', 'fills': []}
Current Price: 2687.33000000
{'symbol': 'ETHUSDT', 'orderId': 962378, 'orderListId': -1, 'clientOrderId': '7MPoHZDykxsK3Oqo7uOB78', 'transactTime': 1651927995310, 'price': '2687.00000000', 'origQty': '0.01000000', 'executedQty': '0.00000000', 'cummulativeQuoteQty': '0.00000000', 'status': 'EXPIRED', 'timeInForce': 'FOK', 'type': 'LIMIT', 'side': 'BUY', 'fills': []}
Current Price: 2687.33000000
{'symbol': 'ETHUSDT', 'orderId': 962387, 'orderListId': -1, 'clientOrderId': '3mRS9GK6pfGpqaqU9SK1vK', 'transactTime': 1651927996177, 'price': '2687.00000000', 'origQty': '0.01000000', 'executedQty': '0.00000000', 'cummulativeQuoteQty': '0.00000000', 'status': 'EXPIRED', 'timeInForce': 'FOK', 'type': 'LIMIT', 'side': 'BUY', 'fills': []}
Current Price: 2687.33000000
{'symbol': 'ETHUSDT', 'orderId': 962395, 'orderListId': -1, 'clientOrderId': '8yTAtrsjNH2PELtg93SdH3', 'transactTime': 1651927997041, 'price': '2687.00000000', 'origQty': '0.01000000', 'executedQty': '0.00000000', 'cummulativeQuoteQty': '0.00000000', 'status': 'EXPIRED', 'timeInForce': 'FOK', 'type': 'LIMIT', 'side': 'BUY', 'fills': []}
Current Price: 2687.06000000
{'symbol': 'ETHUSDT', 'orderId': 962403, 'orderListId': -1, 'clientOrderId': '5q8GPEg5bYgzoW7PUnR2VN', 'transactTime': 1651927997903, 'price': '2687.00000000', 'origQty': '0.01000000', 'executedQty': '0.00000000', 'cummulativeQuoteQty': '0.00000000', 'status': 'EXPIRED', 'timeInForce': 'FOK', 'type': 'LIMIT', 'side': 'BUY', 'fills': []}
Current Price: 2686.87000000
{'symbol': 'ETHUSDT', 'orderId': 962420, 'orderListId': -1, 'clientOrderId': 'ENxCN1JAW4OcxLiAkSmdIH', 'transactTime': 1651927999639, 'price': '2687.00000000', 'origQty': '0.01000000', 'executedQty': '0.01000000', 'cummulativeQuoteQty': '26.86870000', 'status': 'FILLED', 'timeInForce': 'FOK', 'type': 'LIMIT', 'side': 'BUY', 'fills': [{'price': '2686.87000000', 'qty': '0.01000000', 'commission': '0.00000000', 'commissionAsset': 'ETH', 'tradeId': 225595}]}
False # loop closed