def get_ticker(self, symbol_pair): is_restricted_to_values(symbol_pair, currencies.SYMBOL_PAIRS) symbol_pair = self.SYMBOLS_MAPPING[symbol_pair] return self._get('/v1/future_ticker.do?symbol={}&contract_type=quarter' ''.format(symbol_pair), model_class=OkexTicker)
def get_ticker(self, symbol_pair, **kwargs): is_restricted_to_values(symbol_pair, currencies.SYMBOL_PAIRS) symbol_pair = self.SYMBOLS_MAPPING[symbol_pair] return self._get('/v1/pubticker/{}'.format(symbol_pair), model_class=BitfinexTicker, **kwargs)
def get_order_book(self, symbol_pair, **kwargs): is_restricted_to_values(symbol_pair, currencies.SYMBOL_PAIRS) symbol_pair = self.SYMBOLS_MAPPING[symbol_pair] return self._get('/v1/book/{}'.format(symbol_pair), model_class=BitfinexOrderBook, **kwargs)
def get_order_book(self, symbol_pair): is_restricted_to_values(symbol_pair, currencies.SYMBOL_PAIRS) symbol_pair = self.SYMBOLS_MAPPING[symbol_pair] params = {'pair': symbol_pair} return self._get('/0/public/Depth', params=params, model_class=KrakenOrderBook)
def get_ticker(self, symbol_pair): is_restricted_to_values(symbol_pair, currencies.SYMBOL_PAIRS) symbol_pair = self.SYMBOLS_MAPPING[symbol_pair] params = {'pair': symbol_pair} return self._get('/0/public/Ticker', params=params, transformation=self._transform_ticker, model_class=KrakenTicker)
def get_open_orders(self, symbol_pair): is_restricted_to_values(symbol_pair, currencies.SYMBOL_PAIRS) path = '/v1/orders' payload = {'request': path, 'nonce': str(time.time())} signed_payload = self._sign_payload(payload) data = self._post(path, headers=signed_payload, model_class=BitfinexOrder) return [order for order in data if order['symbol_pair'] == symbol_pair]
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 get_order_book(self, symbol_pair): is_restricted_to_values(symbol_pair, currencies.SYMBOL_PAIRS) symbol_pair = self.SYMBOLS_MAPPING[symbol_pair] OkexOrderBook.TICKER = getattr(self, '{}_ticker'.format(symbol_pair)) OkexOrderBook.SYMBOL = symbol_pair OkexOrderBook.CONTRACT_UNIT_AMOUNTS = self.CONTRACT_UNIT_AMOUNTS return self._get( '/v1/future_depth.do?size=100&symbol={}&contract_type=quarter' ''.format(symbol_pair), model_class=OkexOrderBook)
def get_open_positions(self, symbol_pair): is_restricted_to_values(symbol_pair, currencies.SYMBOL_PAIRS) path = '/v1/positions' payload = { 'request': path, 'nonce': str(time.time()), } signed_payload = self._sign_payload(payload) positions = self._post(path, headers=signed_payload, model_class=BitfinexPosition) return [pos for pos in positions if pos['symbol_pair'] == symbol_pair]
def cancel_all_orders(self, symbol_pair): is_restricted_to_values(symbol_pair, currencies.SYMBOL_PAIRS) path = '/0/private/CancelOrder' payload = { 'nonce': int(1000 * time.time()), 'txid': self.get_userref(symbol_pair) } headers = { 'API-Key': self.api_key, 'API-Sign': self._sign_payload(path, payload) } return self._post(path, headers=headers, body=payload)
def cancel_all_orders(self, symbol_pair, **kwargs): is_restricted_to_values(symbol_pair, currencies.SYMBOL_PAIRS) path = '/v1/order/cancel/multi' orders = self.get_open_orders(symbol_pair=symbol_pair) if not orders: return payload = { 'request': path, 'nonce': str(time.time()), 'order_ids': [order.id for order in orders] } signed_payload = self._sign_payload(payload) return self._post(path, headers=signed_payload, **kwargs)
def close_all_positions(self, symbol_pair): is_restricted_to_values(symbol_pair, currencies.SYMBOL_PAIRS) positions = self.get_open_positions(symbol_pair=symbol_pair) for pos in positions: # as we want to close the position, # we need to performe the opposite action to the given one. action = exchanges.SELL if pos.action == exchanges.BUY else exchanges.BUY self.open_order(action, pos.amount, pos.symbol_pair, pos.price, exchanges.MARKET, amount_in_contracts=True, closing=True)
def get_open_positions(self, symbol_pair): is_restricted_to_values(symbol_pair, currencies.SYMBOL_PAIRS) symbol_pair = self.SYMBOLS_MAPPING[symbol_pair] path = '/v1/future_position.do' params = { 'symbol': symbol_pair, 'contract_type': 'quarter', } params['api_key'] = self.api_key params['sign'] = self._sign_params(params) return self._post(path, params=params, transformation=self._transform_open_positions, model_class=OkexPosition)
def get_account_balance(self, symbol=None): is_restricted_to_values(symbol, currencies.SYMBOLS + [None]) path = '/v1/balances' payload = {'request': path, 'nonce': str(time.time())} signed_payload = self._sign_payload(payload) data = self._post(path, headers=signed_payload, transformation=self._transform_account_balance, model_class=BitfinexAccountBalance) if symbol is None: return data for symbol_balance in data: if symbol_balance.symbol == symbol: return symbol_balance return self._empty_account_balance(symbol)
def close_position(self, position_id, symbol_pair, **kwargs): is_restricted_to_values(symbol_pair, currencies.SYMBOL_PAIRS) positions = self.get_open_positions(symbol_pair) try: pos = [pos for pos in positions if pos.id == position_id][0] except IndexError: raise self.ERROR_CLASS('Could not find position with ' 'ID: "{}"'.format(position_id)) # as we want to close the position, # we need to performe the opposite action to the given one. action = exchanges.SELL if pos.action == exchanges.BUY else exchanges.BUY return self.open_order(action, pos.amount, pos.symbol_pair, pos.price, exchanges.MARKET, **kwargs)
def get_open_orders(self, symbol_pair): is_restricted_to_values(symbol_pair, currencies.SYMBOL_PAIRS) path = '/0/private/OpenOrders' payload = { 'nonce': int(1000 * time.time()), } headers = { 'API-Key': self.api_key, 'API-Sign': self._sign_payload(path, payload) } data = self._post(path, headers=headers, body=payload, transformation=self._transform_open_orders, model_class=KrakenOrder) return [order for order in data if order['symbol_pair'] == symbol_pair]
def cancel_all_orders(self, symbol_pair): is_restricted_to_values(symbol_pair, currencies.SYMBOL_PAIRS) orders = self.get_open_orders(symbol_pair=symbol_pair) if not orders: return symbol_pair = self.SYMBOLS_MAPPING[symbol_pair] order_ids = ','.join([order.id for order in orders]) path = '/v1/future_cancel.do' params = { 'symbol': symbol_pair, 'contract_type': 'quarter', 'order_id': order_ids, } params['api_key'] = self.api_key params['sign'] = self._sign_params(params) return self._post(path, params=params)
def get_account_balance(self, symbol=None): is_restricted_to_values(symbol, currencies.SYMBOLS + [None]) path = '/v1/future_userinfo.do' params = {} params['api_key'] = self.api_key params['sign'] = self._sign_params(params) data = self._post(path, params=params, transformation=self._transform_account_balance, model_class=OkexAccountBalance) if symbol is None: return data for symbol_balance in data: if symbol_balance.symbol == symbol: return symbol_balance raise self.ERROR_CLASS( 'Symbol "{}" was not found in the account balance'.format(symbol))
def get_open_positions(self, symbol_pair): is_restricted_to_values(symbol_pair, currencies.SYMBOL_PAIRS) path = '/0/private/OpenPositions' payload = { 'nonce': int(1000 * time.time()), 'docalcs': True, } headers = { 'API-Key': self.api_key, 'API-Sign': self._sign_payload(path, payload) } positions = self._post(path, headers=headers, body=payload, transformation=self._transform_open_positions, model_class=KrakenPosition) return [pos for pos in positions if pos['symbol_pair'] == symbol_pair]
def get_open_orders(self, symbol_pair): is_restricted_to_values(symbol_pair, currencies.SYMBOL_PAIRS) path = '/v1/future_order_info.do' symbol_pair = self.SYMBOLS_MAPPING[symbol_pair] params = { 'symbol': symbol_pair, 'contract_type': 'quarter', 'status': self.ORDER_STATUS['unfilled'], 'order_id': -1, # all orders with given "status" 'current_page': 1, 'page_length': 50, } params['api_key'] = self.api_key params['sign'] = self._sign_params(params) data = self._post(path, params=params, transformation=self._transform_open_orders, model_class=OkexOrder) return [order for order in data if order['symbol_pair'] == symbol_pair]
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 get_account_balance(self, symbol=None): is_restricted_to_values(symbol, currencies.SYMBOLS + [None]) path = '/0/private/Balance' payload = { 'nonce': int(1000 * time.time()), } headers = { 'API-Key': self.api_key, 'API-Sign': self._sign_payload(path, payload) } data = self._post(path, headers=headers, body=payload, transformation=self._transform_account_balance, model_class=KrakenAccountBalance) if symbol is None: return data for symbol_balance in data: if symbol_balance.symbol == symbol: return symbol_balance return self._empty_account_balance(symbol)
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 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)
def close_all_positions(self, symbol_pair, **kwargs): is_restricted_to_values(symbol_pair, currencies.SYMBOL_PAIRS) positions = self.get_open_positions(symbol_pair=symbol_pair) for pos in positions: self.close_position(pos.id, pos.symbol_pair, **kwargs)