def create_order(self, symbol, type, side, amount, price=None, params={}): self.load_markets() market = self.market(symbol) request = { 'instrument': market['id'], 'volume': self.amount_to_precision(symbol, amount), # The value must comply with the list of order types supported by the instrument(see the value of parameter supportedOrderTypes of the Instrument) # If the parameter is not specified, the default value "limit" is used # More about order types in the corresponding section of documentation 'type': type, # 'limit', 'market', 'stopLimit', in fact as of 2018-10-31, only 'limit' orders are supported for all markets 'side': side, # 'buy' or 'sell' # "GTC" - Good-Til-Cancelled # "IOC" - Immediate-Or-Cancel(currently not supported by the exchange API, reserved for future use) # "FOK" - Fill-Or-Kill(currently not supported by the exchange API, reserved for future use) # 'timeInForce': 'GTC', # IOC', 'FOK' # 'strictValidation': False, # False - prices will be rounded to meet the requirement, True - execution of the method will be aborted and an error message will be returned } priceIsRequired = False stopPriceIsRequired = False if type == 'limit': priceIsRequired = True elif type == 'stopLimit': priceIsRequired = True stopPriceIsRequired = True if priceIsRequired: if price is None: raise InvalidOrder( self.id + ' createOrder method requires a price argument for a ' + type + ' order') request['price'] = self.price_to_precision(symbol, price) if stopPriceIsRequired: stopPrice = self.safe_float(params, 'stopPrice') if stopPrice is None: raise InvalidOrder( self.id + ' createOrder method requires a stopPrice extra param for a ' + type + ' order') else: request['stopPrice'] = self.price_to_precision( symbol, stopPrice) response = self.tradingPostPlaceOrder(self.extend(request, params)) # # { # "id": 469594855, # "timestamp": "2018-06-08T16:59:44Z", # "instrument": "BTS-BTC", # "side": "buy", # "type": "limit", # "status": "submitting", # "cancellationReason": null, # "timeInForce": "GTC", # "volume": 4.0, # "price": 0.000025, # "stopPrice": null, # "remainingVolume": 4.0, # "lastUpdate": null, # "parentOrderId": null, # "childOrderId": null # } # return self.parse_order(response, market)
def handle_errors(self, httpCode, reason, url, method, headers, body): if not isinstance(body, basestring): return # fallback to default error handler if len(body) < 2: return # fallback to default error handler if (body[0] == '{') or (body[0] == '['): response = json.loads(body) if 'success' in response: # # 1 - Liqui only returns the integer 'success' key from their private API # # {"success": 1, ...} httpCode == 200 # {"success": 0, ...} httpCode == 200 # # 2 - However, exchanges derived from Liqui, can return non-integers # # It can be a numeric string # {"sucesss": "1", ...} # {"sucesss": "0", ...}, httpCode >= 200(can be 403, 502, etc) # # Or just a string # {"success": "true", ...} # {"success": "false", ...}, httpCode >= 200 # # Or a boolean # {"success": True, ...} # {"success": False, ...}, httpCode >= 200 # # 3 - Oversimplified, Python PEP8 forbids comparison operator(==) of different types # # 4 - We do not want to copy-paste and duplicate the code of self handler to other exchanges derived from Liqui # # To cover points 1, 2, 3 and 4 combined self handler should work like self: # success = self.safe_value(response, 'success', False) if isinstance(success, basestring): if (success == 'true') or (success == '1'): success = True else: success = False if not success: code = self.safe_string(response, 'code') message = self.safe_string(response, 'error') feedback = self.id + ' ' + self.json(response) exceptions = self.exceptions if code in exceptions: raise exceptions[code](feedback) # need a second error map for these messages, apparently... # in fact, we can use the same .exceptions with string-keys to save some loc here if message == 'invalid api key': raise AuthenticationError(feedback) elif message == 'api key dont have trade permission': raise AuthenticationError(feedback) elif message.find( 'invalid parameter' ) >= 0: # errorCode 0, returned on buy(symbol, 0, 0) raise InvalidOrder(feedback) elif message == 'invalid order': raise InvalidOrder(feedback) elif message == 'Requests too often': raise DDoSProtection(feedback) elif message == 'not available': raise DDoSProtection(feedback) elif message == 'data unavailable': raise DDoSProtection(feedback) elif message == 'external service unavailable': raise DDoSProtection(feedback) else: raise ExchangeError(self.id + ' unknown "error" value: ' + self.json(response))
async def create_order(self, symbol, type, side, amount, price=None, params={}): await self.load_markets() if not self.password: raise AuthenticationError( self.id + ' createOrder requires exchange.password to be set to user trading password(not login passwordnot )' ) self.check_required_credentials() market = self.market(symbol) orderType = 1 if (type == 'limit') else 2 order = { 'symbol': market['id'], 'side': side.upper(), 'type': orderType, 'volume': self.amount_to_precision(symbol, amount), 'capital_password': self.password, } if (type == 'market') and (side == 'buy'): if price is None: raise InvalidOrder( self.id + ' createOrder requires price argument for market buy orders to calculate total cost according to exchange rules' ) order['volume'] = self.amount_to_precision( symbol, float(amount) * float(price)) if type == 'limit': order['price'] = self.price_to_precision(symbol, price) else: if price is None: order['price'] = self.price_to_precision(symbol, 0) else: order['price'] = self.price_to_precision(symbol, price) response = await self.privatePostOrder(self.extend(order, params)) # # {"order_id":34343} # timestamp = self.milliseconds() return { 'info': response, 'id': str(response['data']['order_id']), 'timestamp': timestamp, 'datetime': self.iso8601(timestamp), 'lastTradeTimestamp': None, 'status': None, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': amount, 'filled': None, 'remaining': None, 'cost': None, 'trades': None, 'fee': None, }
async def create_order(self, symbol, type, side, amount, price=None, params={}): # for market buy it requires the amount of quote currency to spend if (type == 'market') and (side == 'buy'): if self.options['createMarketBuyOrderRequiresPrice']: if price is None: raise InvalidOrder( self.id + " createOrder() requires the price argument with market buy orders to calculate total order cost(amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = False to supply the cost in the amount argument(the exchange-specific behaviour)" ) else: amount = amount * price await self.load_markets() request = { 'pair': self.market_id(symbol), 'type': side, 'amount': amount, } if type == 'limit': request['price'] = price else: request['order_type'] = type response = await self.privatePostPlaceOrderPair( self.extend(request, params)) # # { # "id": "12978363524", # "time": 1586610022259, # "type": "buy", # "price": "0.033934", # "amount": "0.10722802", # "pending": "0.10722802", # "complete": False # } # placedAmount = self.safe_float(response, 'amount') remaining = self.safe_float(response, 'pending') timestamp = self.safe_value(response, 'time') complete = self.safe_value(response, 'complete') status = 'closed' if complete else 'open' filled = None if (placedAmount is not None) and (remaining is not None): filled = max(placedAmount - remaining, 0) return { 'id': self.safe_string(response, 'id'), 'info': response, 'clientOrderId': None, 'timestamp': timestamp, 'datetime': self.iso8601(timestamp), 'lastTradeTimestamp': None, 'type': type, 'side': self.safe_string(response, 'type'), 'symbol': symbol, 'status': status, 'price': self.safe_float(response, 'price'), 'amount': placedAmount, 'cost': None, 'average': None, 'remaining': remaining, 'filled': filled, 'fee': None, 'trades': None, }
async def create_order(self, symbol, type, side, amount, price=None, params={}): await self.load_markets() market = self.market(symbol) uppercaseType = type.upper() uppercaseSide = side.upper() request = { 'symbol': market['id'], 'type': uppercaseType, # LIMIT, MARKET 'side': uppercaseSide, # or SELL # 'amount': self.amount_to_precision(symbol, amount), # "price": "1234.5678", # required for LIMIT and STOP orders # 'operator': '' # for stop orders, can be found in order introduction # 'stopPrice': self.price_to_precision(symbol, stopPrice), # 'accountId': '...', # subaccount id, optional } stopPrice = self.safe_number(params, 'stopPrice') if stopPrice is None: if (uppercaseType == 'STOP_LIMIT') or (uppercaseType == 'STOP_MARKET'): raise ArgumentsRequired( self.id + ' createOrder() requires a stopPrice parameter for ' + uppercaseType + ' orders') else: if uppercaseType == 'LIMIT': uppercaseType = 'STOP_LIMIT' elif uppercaseType == 'MARKET': uppercaseType = 'STOP_MARKET' operatorString = self.safe_string(params, 'operator') if operatorString is None: raise ArgumentsRequired( self.id + " createOrder() requires an operator parameter 'GTE' or 'LTE' for " + uppercaseType + ' orders') request['operator'] = operatorString request['stopPrice'] = self.price_to_precision(symbol, stopPrice) params = self.omit(params, 'stopPrice') if (uppercaseType == 'LIMIT') or (uppercaseType == 'STOP_LIMIT'): request['price'] = self.price_to_precision(symbol, price) request['amount'] = self.amount_to_precision(symbol, amount) elif (uppercaseType == 'MARKET') or (uppercaseType == 'STOP_MARKET'): if uppercaseSide == 'SELL': request['amount'] = self.amount_to_precision(symbol, amount) elif uppercaseSide == 'BUY': value = self.safe_number(params, 'value') createMarketBuyOrderRequiresPrice = self.safe_value( self.options, 'createMarketBuyOrderRequiresPrice', True) if createMarketBuyOrderRequiresPrice: if price is not None: if value is None: value = amount * price elif value is None: raise InvalidOrder( self.id + " createOrder() requires the price argument with market buy orders to calculate total order cost(amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = False and supply the total cost value in the 'amount' argument or in the 'value' extra parameter(the exchange-specific behaviour)" ) else: value = amount if (value is None) else value precision = market['precision']['price'] request['value'] = self.decimal_to_precision( value, TRUNCATE, precision, self.precisionMode) response = await self.privatePostOrdersCreate( self.extend(request, params)) # # { # "code": "A10000", # "data": { # "amount": "0.001", # "averagePrice": null, # "filledAmount": "0", # "filledFee": "0", # "filledValue": "0", # "id": "870613508008464384", # "operator": "GTE", # "price": "210000", # "side": "BUY", # "status": "SUBMITTED", # "stopPrice": "211000", # "symbol": "BTC_BRL", # "timestamp": 1627612035528, # "type": "STOP_LIMIT", # "value": "210" # }, # "message": "Success" # } # data = self.safe_value(response, 'data', {}) return self.parse_order(data, market)
def handle_errors(self, httpCode, reason, url, method, headers, body, response=None): if not isinstance(body, basestring): return # fallback to default error handler if len(body) < 2: return # fallback to default error handler if (body[0] == '{') or (body[0] == '['): response = json.loads(body) if 'code' in response: # # {"code": "100005", "msg": "request sign illegal", "data": null} # code = self.safe_string(response, 'code') if code is not None: message = self.safe_string(response, 'msg') feedback = self.id + ' ' + self.json(response) if code != '0': exceptions = self.exceptions if code in exceptions: if code == '1': # # {"code":"1","msg":"系统错误","data":null} # {“code”:“1",“msg”:“Balance insufficient,余额不足“,”data”:null} # if message.find('Balance insufficient') >= 0: raise InsufficientFunds(feedback) elif code == '2': if message == 'offsetNot Null': raise ExchangeError(feedback) elif message == 'api_keyNot EXIST': raise AuthenticationError(feedback) elif message == 'price precision exceed the limit': raise InvalidOrder(feedback) elif message == 'Parameter error': raise BadRequest(feedback) raise exceptions[code](feedback) else: raise ExchangeError(self.id + ' unknown "error" value: ' + self.json(response)) else: # # Google Translate: # 订单状态不能取消,订单取消失败 = Order status cannot be canceled # 根据订单号没有查询到订单,订单取消失败 = The order was not queried according to the order number # # {"code":"0","msg":"suc","data":{"success":[],"failed":[{"err-msg":"订单状态不能取消,订单取消失败","order-id":32857051,"err-code":"8"}]}} # {"code":"0","msg":"suc","data":{"success":[],"failed":[{"err-msg":"Parameter error","order-id":32857050,"err-code":"2"},{"err-msg":"订单状态不能取消,订单取消失败","order-id":32857050,"err-code":"8"}]}} # {"code":"0","msg":"suc","data":{"success":[],"failed":[{"err-msg":"Parameter error","order-id":98549677,"err-code":"2"},{"err-msg":"根据订单号没有查询到订单,订单取消失败","order-id":98549677,"err-code":"8"}]}} # if feedback.find('订单状态不能取消,订单取消失败') >= 0: if feedback.find('Parameter error') >= 0: raise OrderNotFound(feedback) else: raise InvalidOrder(feedback) elif feedback.find('根据订单号没有查询到订单,订单取消失败') >= 0: raise OrderNotFound(feedback)
async def create_order(self, symbol, type, side, amount, price=None, params={}): await self.load_markets() # # obsolete since v2 # https://github.com/ccxt/ccxt/issues/4815 # # if not self.password: # raise AuthenticationError(self.id + ' createOrder requires exchange.password to be set to user trading password(not login passwordnot )') # } # self.check_required_credentials() market = self.market(symbol) orderType = 1 if (type == 'limit') else 2 order = { 'symbol': market['id'], 'side': side.upper(), 'type': orderType, 'volume': self.amount_to_precision(symbol, amount), # 'capital_password': self.password, # obsolete since v2, https://github.com/ccxt/ccxt/issues/4815 } if (type == 'market') and (side == 'buy'): if price is None: raise InvalidOrder( self.id + ' createOrder requires price argument for market buy orders to calculate total cost according to exchange rules' ) order['volume'] = self.amount_to_precision( symbol, float(amount) * float(price)) if type == 'limit': order['price'] = self.price_to_precision(symbol, price) else: if price is None: order['price'] = self.price_to_precision(symbol, 0) else: order['price'] = self.price_to_precision(symbol, price) response = await self.v2PostOrder(self.extend(order, params)) # # { # "code": "0", # "msg": "suc", # "data": { # "order_id": 481 # } # } # timestamp = self.milliseconds() return { 'info': response, 'id': self.safe_string(response['data'], 'order_id'), 'timestamp': timestamp, 'datetime': self.iso8601(timestamp), 'lastTradeTimestamp': None, 'status': None, 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': amount, 'filled': None, 'remaining': None, 'cost': None, 'trades': None, 'fee': None, }
async def create_order(self, symbol, type, side, amount, price=None, params={}): if type == 'market': # for market buy it requires the amount of quote currency to spend if side == 'buy': if self.options['createMarketBuyOrderRequiresPrice']: if price is None: raise InvalidOrder( self.id + " createOrder() requires the price argument with market buy orders to calculate total order cost(amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = False to supply the cost in the amount argument(the exchange-specific behaviour)" ) else: amount = amount * price await self.load_markets() market = self.market(symbol) orderType = '1' if (type == 'limit') else '2' orderSide = side.upper() amountToPrecision = self.amount_to_precision(symbol, amount) request = { 'side': orderSide, 'type': orderType, 'symbol': market['id'], 'volume': amountToPrecision, # An excerpt from their docs: # side required Trading Direction # type required pending order types,1:Limit-price Delegation 2:Market- price Delegation # volume required # Purchase Quantity(polysemy,multiplex field) # type=1: Quantity of buying and selling # type=2: Buying represents gross price, and selling represents total number # Trading restriction user/me-user information # price optional Delegation Price:type=2:self parameter is no use. # fee_is_user_exchange_coin optional # 0,when making transactions with all platform currencies, # self parameter represents whether to use them to pay # fees or not and 0 is no, 1 is yes. } priceToPrecision = None if type == 'limit': priceToPrecision = self.price_to_precision(symbol, price) request['price'] = priceToPrecision response = await self.privatePostCreateOrder( self.extend(request, params)) # # {code: '0', # msg: 'suc', # data: {'order_id' : 34343} } # result = self.parse_order(response['data'], market) return self.extend( result, { 'info': response, 'symbol': symbol, 'type': type, 'side': side, 'status': 'open', 'price': float(priceToPrecision), 'amount': float(amountToPrecision), })
async def create_order(self, symbol, type, side, amount, price=None, params={}): await self.load_markets() market = self.market(symbol) uppercaseType = type.upper() uppercaseSide = side.upper() request = { 'symbol': market['id'], 'type': uppercaseType, # LIMIT, MARKET 'side': uppercaseSide, # or SELL # 'accountId': '...', # subaccount id, optional # 'amount': self.amount_to_precision(symbol, amount), # "price": "1234.5678", # required for LIMIT and STOP orders } if uppercaseType == 'LIMIT': request['price'] = self.price_to_precision(symbol, price) request['amount'] = self.amount_to_precision(symbol, amount) elif uppercaseType == 'MARKET': if uppercaseSide == 'SELL': request['amount'] = self.amount_to_precision(symbol, amount) elif uppercaseSide == 'BUY': value = self.safe_float(params, 'value') createMarketBuyOrderRequiresPrice = self.safe_value( self.options, 'createMarketBuyOrderRequiresPrice', True) if createMarketBuyOrderRequiresPrice: if price is not None: if value is None: value = amount * price elif value is None: raise InvalidOrder( self.id + " createOrder() requires the price argument with market buy orders to calculate total order cost(amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = False and supply the total cost value in the 'amount' argument or in the 'value' extra parameter(the exchange-specific behaviour)" ) else: value = amount if (value is None) else value precision = market['precision']['price'] request['value'] = self.decimal_to_precision( value, TRUNCATE, precision, self.precisionMode) response = await self.privatePostOrdersCreate( self.extend(request, params)) # # { # "code": "A10000", # "data": { # "amount": "0.001", # "averagePrice": null, # "filledAmount": "0", # "filledFee": "0", # "filledValue": "0", # "id": "633679992971251712", # "price": "35000", # "side": "BUY", # "status": "PROCESSING", # "symbol": "BTC_BRL", # "timestamp": 1571122683535, # "type": "LIMIT", # "value": "35" # }, # "message": "Success" # } # data = self.safe_value(response, 'data', {}) return self.parse_order(data, market)
def handle_errors(self, code, reason, url, method, headers, body): if (code == 418) or (code == 429): raise DDoSProtection(self.id + ' ' + str(code) + ' ' + reason + ' ' + body) # error response in a form: {"code": -1013, "msg": "Invalid quantity."} # following block cointains legacy checks against message patterns in "msg" property # will switch "code" checks eventually, when we know all of them if code >= 400: if body.find('Price * QTY is zero or less') >= 0: raise InvalidOrder( self.id + ' order cost = amount * price is zero or less ' + body) if body.find('LOT_SIZE') >= 0: raise InvalidOrder( self.id + ' order amount should be evenly divisible by lot size ' + body) if body.find('PRICE_FILTER') >= 0: raise InvalidOrder( self.id + ' order price exceeds allowed price precision or invalid, use self.price_to_precision(symbol, amount) ' + body) if len(body) > 0: if body[0] == '{': response = json.loads(body) # check success value for wapi endpoints # response in format {'msg': 'The coin does not exist.', 'success': True/false} success = self.safe_value(response, 'success', True) if not success: message = self.safe_string(response, 'msg') parsedMessage = None if message is not None: try: parsedMessage = json.loads(message) except Exception as e: # do nothing parsedMessage = None if parsedMessage is not None: response = parsedMessage # checks against error codes error = self.safe_string(response, 'code') if error is not None: exceptions = self.exceptions if error in exceptions: # a workaround for {"code":-2015,"msg":"Invalid API-key, IP, or permissions for action."} # despite that their message is very confusing, it is raised by Binance # on a temporary ban(the API key is valid, but disabled for a while) if (error == '-2015') and self.options[ 'hasAlreadyAuthenticatedSuccessfully']: raise DDoSProtection(self.id + ' temporary banned: ' + body) message = self.safe_string(response, 'msg') if message == 'Order would trigger immediately.': raise InvalidOrder(self.id + ' ' + body) elif message == 'Account has insufficient balance for requested action.': raise InsufficientFunds(self.id + ' ' + body) elif message == 'Rest API trading is not enabled.': raise ExchangeNotAvailable(self.id + ' ' + body) raise exceptions[error](self.id + ' ' + body) else: raise ExchangeError(self.id + ' ' + body) if not success: raise ExchangeError(self.id + ' ' + body)
async def create_order(self, symbol, type, side, amount, price=None, params={}): await self.load_markets() market = self.market(symbol) # the next 5 lines are added to support for testing orders method = 'privatePostOrder' test = self.safe_value(params, 'test', False) if test: method += 'Test' params = self.omit(params, 'test') uppercaseType = type.upper() order = { 'symbol': market['id'], 'quantity': self.amount_to_precision(symbol, amount), 'type': uppercaseType, 'side': side.upper(), 'newOrderRespType': self.options[ 'newOrderRespType'], # 'ACK' for order id, 'RESULT' for full order or 'FULL' for order with fills } timeInForceIsRequired = False priceIsRequired = False stopPriceIsRequired = False if uppercaseType == 'LIMIT': priceIsRequired = True timeInForceIsRequired = True elif (uppercaseType == 'STOP_LOSS') or (uppercaseType == 'TAKE_PROFIT'): stopPriceIsRequired = True elif (uppercaseType == 'STOP_LOSS_LIMIT') or (uppercaseType == 'TAKE_PROFIT_LIMIT'): stopPriceIsRequired = True priceIsRequired = True timeInForceIsRequired = True elif uppercaseType == 'LIMIT_MAKER': priceIsRequired = True if priceIsRequired: if price is None: raise InvalidOrder( self.id + ' createOrder method requires a price argument for a ' + type + ' order') order['price'] = self.price_to_precision(symbol, price) if timeInForceIsRequired: order['timeInForce'] = self.options[ 'defaultTimeInForce'] # 'GTC' = Good To Cancel(default), 'IOC' = Immediate Or Cancel if stopPriceIsRequired: stopPrice = self.safe_float(params, 'stopPrice') if stopPrice is None: raise InvalidOrder( self.id + ' createOrder method requires a stopPrice extra param for a ' + type + ' order') else: order['stopPrice'] = self.price_to_precision(symbol, stopPrice) response = await getattr(self, method)(self.extend(order, params)) return self.parse_order(response, market)
async def create_order(self, symbol, type, side, amount, price=None, params={}): await self.load_markets() market = self.market(symbol) request = { 'market': market['id'], 'side': side, # "buy" or "sell" # 'price': 0.306525, # send null for market orders 'type': type, # "limit", "market", "stop", "trailingStop", or "takeProfit" 'size': float(self.amount_to_precision(symbol, amount)), # 'reduceOnly': False, # optional, default is False # 'ioc': False, # optional, default is False, limit or market orders only # 'postOnly': False, # optional, default is False, limit or market orders only # 'clientId': 'abcdef0123456789', # string, optional, client order id, limit or market orders only } priceToPrecision = None if price is not None: priceToPrecision = float(self.price_to_precision(symbol, price)) method = 'privatePostConditionalOrders' if type == 'limit': method = 'privatePostOrders' request['price'] = priceToPrecision elif type == 'market': method = 'privatePostOrders' request['price'] = None elif (type == 'stop') or (type == 'takeProfit'): request['triggerPrice'] = priceToPrecision # request['orderPrice'] = number # optional, order type is limit if self is specified, otherwise market elif type == 'trailingStop': request['trailValue'] = priceToPrecision # negative for "sell", positive for "buy" else: raise InvalidOrder(self.id + ' createOrder() does not support order type ' + type + ', only limit, market, stop, trailingStop, or takeProfit orders are supported') response = await getattr(self, method)(self.extend(request, params)) # # orders # # { # "success": True, # "result": [ # { # "createdAt": "2019-03-05T09:56:55.728933+00:00", # "filledSize": 0, # "future": "XRP-PERP", # "id": 9596912, # "market": "XRP-PERP", # "price": 0.306525, # "remainingSize": 31431, # "side": "sell", # "size": 31431, # "status": "open", # "type": "limit", # "reduceOnly": False, # "ioc": False, # "postOnly": False, # "clientId": null, # } # ] # } # # conditional orders # # { # "success": True, # "result": [ # { # "createdAt": "2019-03-05T09:56:55.728933+00:00", # "future": "XRP-PERP", # "id": 9596912, # "market": "XRP-PERP", # "triggerPrice": 0.306525, # "orderId": null, # "side": "sell", # "size": 31431, # "status": "open", # "type": "stop", # "orderPrice": null, # "error": null, # "triggeredAt": null, # "reduceOnly": False # } # ] # } # # result = self.safe_value(response, 'result', []) return self.parse_order(result, market)
async def create_order(self, symbol, type, side, amount, price=None, params={}): await self.load_markets() market = self.market(symbol) type = type.upper() if type == 'STOP_LIMIT': type = 'STOP-LIMIT' request = { # == Required == # orderType : string # can be MARKET,LIMIT,STOP,STOP-LIMIT # symbol : string # orderQty : string # Buying or selling quantity # side : string # BUY or SELL # == Required according to ordeType == # price : string # limit price in limit and stop-limit orders # stopPrice : string # Trigger price for stop-limit order and stop order # == Optional == # clOrdID : string # timeInForce :string # GTC/IOC/FOK,default is GTC 'orderType': type, 'symbol': market['id'], 'orderQty': self.amount_to_precision(symbol, amount), 'side': side.upper(), } if (type == 'LIMIT') or (type == 'STOP-LIMIT'): if price is None: raise InvalidOrder( self.id + ' createOrder method requires a price for a ' + type + ' order') request['price'] = self.price_to_precision(symbol, price) if (type == 'STOP') or (type == 'STOP-LIMIT'): stopPrice = self.safe_float(params, 'stopPrice') if stopPrice is None: raise InvalidOrder( self.id + ' createOrder method requires a stopPrice extra param for a ' + type + ' order') request['stopPrice'] = self.price_to_precision(symbol, stopPrice) response = await self.privatePostV2SpotOrders( self.extend(request, params)) # Response # { # "code":1, # "data":{ # "avgPrice":"0", # "base":"BTC", # "clOrdID":"aax", # "commission":"0", # "createTime":null, # "cumQty":"0", # "id":null, # "isTriggered":null, # "lastPrice":"0", # "lastQty":"0", # "leavesQty":"0", # "orderID":"wJ4L366KB", # "orderQty":"0.02", # "orderStatus":0, # "orderType":2, # "price":"8000", # "quote":"USDT", # "rejectCode":null, # "rejectReason":null, # "side":1, # "stopPrice":null, # "symbol":"BTCUSDT", # "transactTime":null, # "updateTime":null, # "timeInForce":1, # "userID":"216214" # }, # "message":"success", # "ts":1573530401264 # } data = self.safe_value(response, 'data') order = await self.fetch_order(data['orderID']) if order['status'] == 'rejected': info = self.safe_value(order, 'info') raise InvalidOrder(' order was rejected by the exchange ' + self.safe_value(info, 'rejectReason')) return order
async def create_order(self, symbol, type, side, amount, price=None, params={}): await self.load_markets() market = self.market(symbol) exchangeType = type.upper() exchangeSide = side.upper() baseId = market['id'].split('_')[1] quoteId = market['id'].split('_')[0] request = { 'market': quoteId, 'trade': baseId, 'side': exchangeSide, 'type': exchangeType, # MARKET, LIMIT, STOPLIMIT 'volume': self.amount_to_precision(symbol, amount), } if exchangeType == 'LIMIT': if price is None: raise InvalidOrder(self.id + ' createOrder method requires a price argument for a ' + type + ' order') request['rate'] = self.price_to_precision(symbol, price) timeInForce = self.safe_string(params, 'timeInForce') if timeInForce is not None: if timeInForce != 'GTC' and timeInForce != 'IOC' and timeInForce != 'FOK': request['timeInForce'] = timeInForce if exchangeType == 'STOPLIMIT': stopPrice = self.safe_float(params, 'stopPrice') if price is None: raise InvalidOrder(self.id + ' createOrder method requires a price argument for a ' + type + ' order') if stopPrice is None: raise InvalidOrder(self.id + ' createOrder method requires a stop argument for a ' + type + ' order') request['rate'] = self.price_to_precision(symbol, price) request['stop'] = self.price_to_precision(symbol, stopPrice) if exchangeType == 'STOPMARKET': stopPrice = self.safe_float(params, 'stopPrice') if stopPrice is None: raise InvalidOrder(self.id + ' createOrder method requires a stop argument for a ' + type + ' order') request['stop'] = self.price_to_precision(symbol, stopPrice) request['timeInForce'] = 'GTC' if exchangeType == 'MARKET': if price is not None: raise InvalidOrder(self.id + ' createOrder method does not support price argument for a ' + type + ' order') response = await self.privatePostPlaceOrder(request) # # { # status: 'Success', # data: { # "orderId": 214083724 # } # } # timestamp = self.seconds() result = self.safe_value(response, 'data') id = self.safe_integer(result, 'orderId') order = { 'id': id, 'info': response, 'timestamp': timestamp, 'datetime': self.iso8601(timestamp), 'symbol': symbol, 'type': type, 'side': side, 'price': price, 'amount': amount, 'cost': None, 'average': None, 'filled': None, 'remaining': None, 'status': None, 'fee': None, 'trades': None, } return order
async def create_order(self, symbol, type, side, amount, price=None, params={}): await self.load_markets() market = self.market(symbol) options = self.safe_value(self.options, 'timeInForce') defaultTimeInForce = self.safe_value(options, type) timeInForce = self.safe_string_2(params, 'timeInForce', 'time_in_force', defaultTimeInForce) request = { 'market_id': market['id'], 'type': type, 'side': side, 'time_in_force': timeInForce, } clientOrderId = self.safe_string_2(params, 'clientOrderId', 'client_order_id') if clientOrderId is not None: request['client_order_id'] = clientOrderId costToPrecision = None if type == 'limit': request['limit_price'] = self.price_to_precision(symbol, price) request['quantity'] = self.amount_to_precision(symbol, amount) elif type == 'market': # for market buy it requires the amount of quote currency to spend if side == 'buy': cost = self.safe_number(params, 'cost') createMarketBuyOrderRequiresPrice = self.safe_value(self.options, 'createMarketBuyOrderRequiresPrice', True) if createMarketBuyOrderRequiresPrice: if price is not None: if cost is None: cost = amount * price elif cost is None: raise InvalidOrder(self.id + " createOrder() requires the price argument for market buy orders to calculate total order cost(amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = False and supply the total cost value in the 'amount' argument or in the 'cost' extra parameter(the exchange-specific behaviour)") else: cost = amount if (cost is None) else cost costToPrecision = self.cost_to_precision(symbol, cost) request['cost'] = costToPrecision else: request['quantity'] = self.amount_to_precision(symbol, amount) query = self.omit(params, ['timeInForce', 'time_in_force', 'clientOrderId', 'client_order_id']) response = await self.privatePostNewOrder(self.extend(request, query)) # # { # data: { # id: string, # user_id: string, # market_id: string, # type: 'orderType', # side: 'side', # quantity: string, # limit_price: string, # time_in_force: 'timeInForce', # filled_cost: string, # filled_quantity: string, # open_quantity: string, # cancelled_quantity: string, # status: 'orderStatus', # time: 'date', # client_order_id: string, # } # } # data = self.safe_value(response, 'data') order = self.parse_order(data, market) # a workaround for incorrect huge amounts # returned by the exchange on market buys if (type == 'market') and (side == 'buy'): order['amount'] = None order['cost'] = self.parse_number(costToPrecision) order['remaining'] = None return order
def handle_errors(self, code, reason, url, method, headers, body, response=None): if body[0] == '{': response = json.loads(body) # {success: False, message: "message"} success = self.safe_value(response, 'success') if success is None: raise ExchangeError(self.id + ': malformed response: ' + self.json(response)) if isinstance(success, basestring): # bleutrade uses string instead of boolean success = True if (success == 'true') else False if not success: message = self.safe_string(response, 'message') feedback = self.id + ' ' + self.json(response) exceptions = self.exceptions if message == 'APIKEY_INVALID': if self.options['hasAlreadyAuthenticatedSuccessfully']: raise DDoSProtection(feedback) else: raise AuthenticationError(feedback) if message == 'DUST_TRADE_DISALLOWED_MIN_VALUE_50K_SAT': raise InvalidOrder( self.id + ' order cost should be over 50k satoshi ' + self.json(response)) if message == 'INVALID_ORDER': # Bittrex will return an ambiguous INVALID_ORDER message # upon canceling already-canceled and closed orders # therefore self special case for cancelOrder # url = 'https://bittrex.com/api/v1.1/market/cancel?apikey=API_KEY&uuid=ORDER_UUID' cancel = 'cancel' indexOfCancel = url.find(cancel) if indexOfCancel >= 0: parts = url.split('&') orderId = None for i in range(0, len(parts)): part = parts[i] keyValue = part.split('=') if keyValue[0] == 'uuid': orderId = keyValue[1] break if orderId is not None: raise OrderNotFound(self.id + ' cancelOrder ' + orderId + ' ' + self.json(response)) else: raise OrderNotFound(self.id + ' cancelOrder ' + self.json(response)) if message in exceptions: raise exceptions[message](feedback) if message is not None: if message.find('throttled. Try again') >= 0: raise DDoSProtection(feedback) if message.find('problem') >= 0: raise ExchangeNotAvailable( feedback ) # 'There was a problem processing your request. If self problem persists, please contact...') raise ExchangeError(feedback)
def create_order(self, symbol, type, side, amount, price=None, params={}): self.load_markets() market = self.market(symbol) uppercaseType = type.upper() if uppercaseType == 'MARKET': type = 1 elif uppercaseType == 'LIMIT': type = 2 elif uppercaseType == 'STOPMARKET': type = 3 elif uppercaseType == 'STOPLIMIT': type = 4 uppercaseSide = side.upper() side = uppercaseSide == 1 if 'BUY' else 2 request = { 'accountId': self.uid, 'marketId': market['id'], 'type': type, 'side': side, # 'postOnly': False, # 'timeInForce': 'GTC', } clientOrderId = self.safe_value_2(params, 'refId', 'clientOrderId') query = params if clientOrderId is not None: request['refId'] = clientOrderId query = self.omit(params, ['refId', 'clientOrderId']) if (uppercaseType == 'MARKET') and (uppercaseSide == 'BUY'): # for market buy it requires the amount of quote currency to spend cost = self.safe_number(params, 'cost') if self.options['createMarketBuyOrderRequiresPrice']: if cost is None: if price is not None: cost = amount * price else: raise InvalidOrder( self.id + " createOrder() requires the price argument with market buy orders to calculate total order cost(amount to spend), where cost = amount * price. Supply a price argument to createOrder() call if you want the cost to be calculated for you from price and amount, or, alternatively, add .options['createMarketBuyOrderRequiresPrice'] = False to supply the cost in the amount argument(the exchange-specific behaviour)" ) else: cost = amount if (cost is None) else cost precision = market['precision']['price'] request['cost'] = self.decimal_to_precision( cost, TRUNCATE, precision, self.precisionMode) else: request['qty'] = self.amount_to_precision(symbol, amount) if uppercaseType == 'LIMIT': request['limitPrice'] = self.price_to_precision(symbol, price) response = self.privatePostOrders(self.extend(request, query)) # # market buy # # { # "id":"ff81127c-8fd5-4846-b683-110639dcd322", # "accountId":"6d445378-d8a3-4932-91cd-545d0a4ad2a2", # "marketId":33, # "type":1, # "side":1, # "cost":"25", # "postOnly":false, # "timeInForce":"GTC", # "state":1, # "placedAt":1589510846735 # } # # market sell, limit buy, limit sell # # { # "id":"042a38b0-e369-4ad2-ae73-a18ff6b1dcf1", # "accountId":"6d445378-d8a3-4932-91cd-545d0a4ad2a2", # "marketId":33, # "type":2, # "side":1, # "qty":"1000", # "limitPrice":"100", # "postOnly":false, # "timeInForce":"GTC", # "state":1, # "placedAt":1589403938682, # } # return self.parse_order(response, market)
def create_order(self, market, type, price, side, amount): self._update_orders() type_market = False type_limit = False if type == 'market': if price is not None: raise InvalidOrder( 'ExchangeAccount: market order has no price') type_market = True elif type == 'limit': price = _convert_float_or_raise(price, 'ExchangeAccount: price') type_limit = True if price <= 0: raise BadRequest('ExchangeAccount: price needs to be positive') else: raise InvalidOrder( 'ExchangeAccount: only market and limit order supported') if market is None: raise InvalidOrder('ExchangeAccount: market is None') symbol = market.get('symbol') ohlcv = self._ohlcvs.get(symbol) if ohlcv is None: raise InvalidOrder('ExchangeAccount: no prices available for {}' .format(symbol)) if side not in ['buy', 'sell']: raise InvalidOrder('ExchangeAccount: side {} not supported' .format(side)) buy = side == 'buy' amount = _convert_float_or_raise(amount, 'ExchangeAccount: amount') if amount <= 0: raise BadRequest('ExchangeAccount: amount needs to be positive') base = market.get('base') quote = market.get('quote') if base is None: raise BadRequest('ExchangeAccount: market has no base') if quote is None: raise BadRequest('ExchangeAccount: market has no quote') self._last_order_id += 1 order_id = str(self._last_order_id) date = self._timeframe.date() timestamp = int(date.value / 10e5) order = { 'info': {}, 'id': order_id, 'timestamp': timestamp, 'datetime': Exchange.iso8601(timestamp), 'lastTradeTimestamp': None, 'symbol': symbol, 'type': type, 'side': side, 'price': None, 'amount': amount, 'cost': None, 'average': None, 'filled': 0, 'remaining': amount, 'status': 'open', 'fee': {'currency': base if buy else quote, 'cost': None, 'rate': None}, 'trades': None, } if type_market: # Determinie the price of the market order # We could use the next low/high to fill the order, but then we # need to wait for the next date to fill the order, otherwise we # would introduce a possibility to see the future price # (Look-Ahead Bias) # If we wait for the next date, we would return a market order that # is pending, but this should never happen in reality # Maybe the factor should depend on the volume factor = Decimal('0.0015') if buy: price = (1 + factor) * _convert_float(ohlcv['high'][date]) else: price = (1 - factor) * _convert_float(ohlcv['low'][date]) fee_percentage = market.get('taker', 0) fee_percentage = _convert_float_or_raise(fee_percentage, 'ExchangeAccount: fee') self._update_balance(price, amount, base, quote, buy, fee_percentage) self._fill_order(order, buy, price, timestamp, fee_percentage) self._closed_orders[order_id] = order if type_limit: # TODO Probably use taker fee, if the order can be filled now fee_percentage = market.get('maker', 0) fee_percentage = _convert_float_or_raise(fee_percentage, 'ExchangeAccount: fee') if buy: self._balances[quote].change_used(price * amount) else: self._balances[base].change_used(amount) self._open_orders[order_id] = order self._private_order_info[order_id] = { 'id': order_id, 'base': base, 'quote': quote, 'price': price, 'buy': buy, 'fee_percentage': fee_percentage, 'fillable_date': self._limit_order_fillable_date( symbol, buy, price), } self._update_next_private_order_to_update() return {'id': order_id, 'info': {}}