def cancel_order(self, order_id): is_instance(order_id, (str, )) passes_test(order_id, lambda x: int(x)) # NOTE: OKEX doesn't provide a way of getting all orders from any # symbol pair. We need to loop through all of them until we find it. order = None for symbol_pair in currencies.SYMBOL_PAIRS: orders = self.get_open_orders(symbol_pair=symbol_pair) orders = [ order for order in orders if int(order.id) == int(order_id) ] if orders: # found order in current symbol pair, no need to keep iterating order = orders[0] break if not order: raise ValueError( 'Could not find order with ID "{}"'.format(order_id)) path = '/v1/future_cancel.do' params = { 'symbol': order.symbol_pair, 'contract_type': 'quarter', 'order_id': int(order_id), } params['api_key'] = self.api_key params['sign'] = self._sign_params(params) return self._post(path, params=params)
def volume_weighted_average_price(action, order_book, amount): """ Calculates the weighted average price of an operation (sell/buy) for a given `amount` and `order_book`. @params: * action: exchanges.ACTIONS choice * order_book: models.base.OrderBook instance or subclass * amount: Decimal or valid numeric argument representing volume to operate. @returns: a Decimal object representing the weighted average price. """ # validate arguments is_restricted_to_values(action, exchanges.ACTIONS) is_instance(amount, (Decimal, float, int, str)) passes_test(amount, lambda x: Decimal(x)) order_list = order_book.asks if action == exchanges.BUY else order_book.bids total_market_depth = sum([t[1] for t in order_list]) if amount > total_market_depth: raise exceptions.InsufficientMarketDepth( 'Not enough depth in OrderBook to {} {} volume'.format( action, amount)) if action == exchanges.BUY: # when checking the "asks" list, we want to # iterate orders from cheapest to highest order_list = list(reversed(order_list)) accum = Decimal(0.0) # most of the times last used order in the orderbook # is partially used. we need to know which was the portion # used from that order to calculate the weighted average rest = amount for index, price_tuple in enumerate(order_list): volume = price_tuple[1] accum += volume if accum >= amount: break rest -= volume # get the sub list representing only the necessary # orders to fulfill the amount amount sub_list = order_list[:index + 1] # change the last used order in the list, to just # the needed rest amount sub_list[-1] = (sub_list[-1][0], Decimal(rest)) sub_list_amounts = [t[1] for t in sub_list] return sum(x * y for x, y in sub_list) / sum(sub_list_amounts)
def open_order(self, action, amount, symbol_pair, price, order_type): """ Creates a new Order. :action: exchanges.ACTIONS choice :amount: Decimal, float, integer or string representing number value. :symbol_pair: currencies.SYMBOL_PAIRS choice :price: Decimal, float, integer or string representing number value. :order_type: exchanges.ORDER_TYPES choice """ # validate arguments is_restricted_to_values(action, exchanges.ACTIONS) is_instance(amount, (Decimal, float, int, str)) passes_test(amount, lambda x: Decimal(x)) is_restricted_to_values(symbol_pair, currencies.SYMBOL_PAIRS) is_instance(price, (Decimal, float, int, str)) passes_test(price, lambda x: isinstance(Decimal(x), Decimal)) is_restricted_to_values(order_type, exchanges.ORDER_TYPES) path = '/0/private/AddOrder' userref = self.get_userref(symbol_pair) symbol_pair = self.SYMBOLS_MAPPING[symbol_pair] payload = { 'pair': symbol_pair, 'type': action, 'ordertype': order_type, 'price': Decimal(str(price)), 'volume': Decimal(str(amount)), 'leverage': 2, 'nonce': int(1000 * time.time()), 'userref': userref, } headers = { 'API-Key': self.api_key, 'API-Sign': self._sign_payload(path, payload) } return self._post(path, headers=headers, body=payload, transformation=self._transform_new_order, model_class=KrakenOrder)
def open_order(self, action, amount, symbol_pair, price, order_type, **kwargs): """ Creates a new Order. :action: exchanges.ACTIONS choice :amount: Decimal, float, integer or string representing number value. :symbol_pair: currencies.SYMBOL_PAIRS choice :price: Decimal, float, integer or string representing number value. :order_type: exchanges.ORDER_TYPES choice """ # validate arguments is_restricted_to_values(action, exchanges.ACTIONS) is_instance(amount, (Decimal, float, int, str)) passes_test(amount, lambda x: Decimal(x)) is_restricted_to_values(symbol_pair, currencies.SYMBOL_PAIRS) is_instance(price, (Decimal, float, int, str)) passes_test(price, lambda x: isinstance(Decimal(x), Decimal)) is_restricted_to_values(order_type, exchanges.ORDER_TYPES) path = '/v1/order/new' symbol_pair = self.SYMBOLS_MAPPING[symbol_pair] payload = { 'request': path, 'nonce': str(time.time()), 'side': action, 'amount': str(amount), 'symbol': symbol_pair, 'price': str(price), 'type': order_type, } signed_payload = self._sign_payload(payload) return self._post(path, headers=signed_payload, model_class=BitfinexOrder, **kwargs)
def worst_order_price(action, order_book, amount): """ Calculates the worst used order price in given `order_book` to fulfill an operation (sell/buy) of the given `amount`. @params: * action: exchanges.ACTIONS choice * order_book: models.base.OrderBook instance or subclass * amount: Decimal or valid numeric argument representing volume to operate. @returns: a Decimal object representing the worst order price. """ # validate arguments is_restricted_to_values(action, exchanges.ACTIONS) is_instance(amount, (Decimal, float, int, str)) passes_test(amount, lambda x: Decimal(x)) order_list = order_book.asks if action == exchanges.BUY else order_book.bids total_market_depth = sum([t[1] for t in order_list]) if amount > total_market_depth: raise exceptions.InsufficientMarketDepth( 'Not enough depth in OrderBook to {} {} volume'.format( action, amount)) if action == exchanges.BUY: # when checking the "asks" list, we want to # iterate orders from cheapest to highest order_list = list(reversed(order_list)) accum = Decimal('0') for price_tuple in order_list: price, volume = price_tuple accum += volume if accum >= amount: return price
def open_order(self, action, amount, symbol_pair, price, order_type, amount_in_contracts=False, closing=False): """ Creates a new Order. :action: exchanges.ACTIONS choice :amount: Decimal, float, integer or string representing number value. If `amount_in_contracts == True`, `amount` needs to be an integer number greater or equal to 1. :symbol_pair: currencies.SYMBOL_PAIRS choice :price: Decimal, float, integer or string representing number value. :order_type: exchanges.ORDER_TYPES choice :amount_in_contracts: (True|False) Whether the `amount` argument is expressed in cryptos or contracts :closing: (True|False) Whether the order we are opening is to close an existing position or not """ # validate arguments is_restricted_to_values(action, exchanges.ACTIONS) is_instance(amount, (Decimal, float, int, str)) passes_test(amount, lambda x: Decimal(x)) if amount_in_contracts: passes_test(amount, lambda x: Decimal(x) >= 1) is_restricted_to_values(symbol_pair, currencies.SYMBOL_PAIRS) is_instance(price, (Decimal, float, int, str)) passes_test(price, lambda x: Decimal(x)) is_restricted_to_values(order_type, exchanges.ORDER_TYPES) is_instance(amount_in_contracts, bool) is_instance(closing, bool) path = '/v1/future_trade.do' symbol_pair = self.SYMBOLS_MAPPING[symbol_pair] if closing: action = (self.ACTION['close_long'] if action == exchanges.SELL else self.ACTION['close_short']) else: action = (self.ACTION['open_short'] if action == exchanges.SELL else self.ACTION['open_long']) if not amount_in_contracts: amount = crypto_to_contracts( amount, getattr(self, '{}_ticker'.format(symbol_pair)).last, self.CONTRACT_UNIT_AMOUNTS[symbol_pair]) match_price = 1 if order_type == 'market' else 0 params = { 'symbol': symbol_pair, 'contract_type': 'quarter', 'price': float(Decimal(price)), 'match_price': match_price, # if market, 'price' field is ignored 'amount': int(amount), # in contracts 'type': action, 'lever_rate': 10, # default } params['api_key'] = self.api_key params['sign'] = self._sign_params(params) return self._post(path, params=params, model_class=OkexOrder)