def get_profit(self, currency) -> U[Tuple, Float]: """Returns current profit for a currency and its weighted average buy cost. :param Text currency: a valid currency to use at profit and cost calc :return: current profit and weighted average buy cost as a tuple """ currency = Currency(str(currency)) btc_symbol = currency + Currency('BTC') # type: Symbol balance = self.get_balance(currency) if balance.used > 0.0: cost = self.get_cost(symbol=btc_symbol) return balance.to_btc - (cost * balance.total) else: return 0.0, 0.0
def altname(self, name) -> U[Currency, Symbol]: """Retrieve alternative currency or symbol name used in a specific exchange. >>> PandaXT('binance').altname('YOYOW') 'YOYOW' >>> PandaXT('binance').altname('YOYOW/BTC') (Symbol:YOYOW/BTC) >>> PandaXT('binance').altname('YOYOW/BTC') (Symbol:YOYOW/BTC) :param name: currency or symbol name to check for alternative name. :type name: Text or Currency or Symbol :return: currency alternative name as Currency or Symbol instance. :rtype: Currency or Symbol """ _symbol = str(name).upper() if '/' in _symbol.strip('/'): # print(_symbol) base, quote = _symbol.split('/') assert quote in self.base_markets, f'{quote} is not a valid base market.' base = self.get_currency(base) s = self.get_currency(base) + Currency(quote) s.price2precision = functools.partial(self.price2precision, s) s.cost2precision = functools.partial(self.cost2precision, s) s.amount2precision = functools.partial(self.amount2precision, s) return s else: return self.get_currency(_symbol)
def get_cost(self, symbol, **kwargs) -> Float: """FIXME do not work well sometimes giving a bad result by unknown reason. Get weighted average (from buy trades data) cost for a symbol. >>> api = PandaXT('binance') >>> symbol_cost = api.get_cost('AGI/BTC') >>> symbol_cost True :param U[Symbol,Text] symbol: a valid exchange symbol. :param kwargs: accepted keys are: balance (Balance) and trades (pd.DataFrame) :return: cost calculation result as float type. """ symbol = self.altname(symbol) if isinstance(symbol or 0, Currency): symbol = symbol + Currency('BTC') base, quote = symbol.parts # reuse provided balance and trades data (for rate limit save) if 'balance' in kwargs: cached = kwargs.get('balance') # type: Balance balance = cached[base] if isinstance(cached, Wallet) else cached else: balance = self.get_balance(base, field='total') # type: Balance total_balance = balance.total if balance and hasattr( balance, 'total') else balance if total_balance > 0.0: trades = kwargs.get('trades', []) if not trades: trades = self.get_user_trades(symbol, side='buy') elif not isinstance(trades, pd.DataFrame): trades = pd.DataFrame(trades) if trades['order'].isna().all(): trades['order'].update(trades['id']) # group-by operations per column columns_op = {'amount': 'mean', 'price': 'mean', 'cost': 'mean', 'timestamp': 'mean'} trades = trades.groupby('order').agg(columns_op).sort_index( ascending=False) # type: pd.DataFrame trades = trades[['price', 'amount']].reset_index(drop=True) for index, price, amount in trades.itertuples(): if round(total_balance - amount, 8) <= 0.0: if round(total_balance - amount, 8) != 0.0: prices = trades[:index + 1]['price'].values amounts = trades[:index + 1]['amount'].values amounts[-1] = total_balance else: prices, amounts = trades[:index + 1].T.values return round(pd.np.average(prices, weights=amounts), 10) else: total_balance -= amount
def get_currency(self, currency) -> Currency: """Currency name sanitizer.""" if currency: if currency in self._api.commonCurrencies: currency = self._api.commonCurrencies.get(currency) if currency not in self.currencies: log.debug(f'Currency {str(currency)} is not supported by exchange.') return Currency(currency)
def currencies(self) -> List[Currency]: """Get supported currencies by exchange as "Currency" list. >>> currencies = PandaXT('binance').currencies >>> len(currencies) > 0 True """ # Initialize markets, symbols, currencies and basemarkets if self._currencies is None: delisted = {d.base for d in self.delisted} self._currencies = sorted([ Currency(c) for c in self._api.currencies if c not in delisted ]) return self._currencies
def is_tradeable(currency, tickers, balance, base_market=None): """Check if a currency balance is over minimum tradeable amount for a base market. :param currency: :type currency: str or Currency :param tickers: :type tickers: Tickers :param balance: :type balance: dict or float :param base_market: :type base_market: str or Currency :return: :rtype: """ base_market = Currency(base_market or 'BTC') symbol: Symbol = self.altname(currency) + base_market market: Market = self.markets.get(symbol, False) if not market: return False # min amount in quote currency limits: Limit = market.limits quote_min_amount = limits.amount ticker = tickers[symbol] # type: Ticker if currency == 'BTC' and 'USD' in base_market: last = 1.0 / ticker.last else: last = ticker.last # converting min_amount to base currency base_min_amount = last * quote_min_amount # subtracting a 0.01% fee base_min_amount = base_min_amount * 0.999 if isinstance(balance or [], dict): balance = balance.get('total', 0.0) else: balance = balance or 0.0 return balance > base_min_amount
def get_balances(self, field=None, tradeables_only=True, fiat=None) -> Wallet: """Get balances. >>> balances = PandaXT('binance').get_balances('total') >>> isinstance(balances, Wallet) True :param Text field: accepted values: if None all balances will be loaded, (values; "total", "used", "free") :param Bool tradeables_only: :return: positive balances. """ def is_zero(v) -> U[Float, Dict]: if isinstance(v, float): return v <= 0.0 elif isinstance(v, dict): return v.get('total', 0.0) <= 0.0 else: return 0.0 def is_tradeable(currency, _tickers, balance, base_market=None) -> Bool: """Check if a currency balance is over minimum tradeable amount for a base market. :param Text currency: :param Tickers _tickers: :param balance: :type balance: Dict or Float :param Text base_market: :return: True if currency is tradeable """ if currency in self.base_markets: return True base_market = Currency(base_market or 'BTC') symbol: Symbol = self.altname(currency) + base_market market: Market = self.markets.get(symbol, False) if not market: return False # min amount in quote currency limits: Limit = market.limits quote_min_amount = limits.amount ticker: Ticker = _tickers[symbol] if currency == 'BTC' and 'USD' in base_market: last = 1.0 / ticker.last else: last = ticker.last # converting min_amount to base currency base_min_amount = last * quote_min_amount # subtracting a 0.01% fee base_min_amount = base_min_amount * 0.999 if isinstance(balance or [], dict): balance = balance.get('total', 0.0) else: balance = balance or 0.0 return balance > base_min_amount try: data = self._api.fetch_balance() if 'info' in data: del data['info'] data: dict = data.pop(field) if field else data data = {str(k): v for k, v in data.items() if not is_zero(v)} if tradeables_only: symbols = {Currency(c) + Currency('BTC') for c in data} tickers = self.get_tickers(symbols) data = {str(k): v for k, v in data.items() if k in self._basemarkets or is_tradeable(k, tickers, v)} wallet = Wallet(**data) if str(fiat or '').lower() in ['EUR', 'USD']: acum = 0 for k, v in Wallet(**data).items(): if fiat.lower() == 'eur': acum += v.to_eur elif fiat.lower() == 'usd': acum += v.to_usd wallet.update({fiat.upper(): acum}) return wallet except ccxt.NetworkError as err: print('PandaXT::get_ohlc', str(err))
def get_balances(self, field=None, tradeables_only=True): """Get balances. >>> balances = PandaXT('binance').get_balances('total') >>> isinstance(balances, Wallet) True :param Text field: accepted values: if None all balances will be loaded, (values; "total", "used", "free") :return Wallet: positive balances. """ def is_tradeable(currency, tickers, balance, base_market=None): """Check if a currency balance is over minimum tradeable amount for a base market. :param currency: :type currency: str or Currency :param tickers: :type tickers: Tickers :param balance: :type balance: dict or float :param base_market: :type base_market: str or Currency :return: :rtype: """ base_market = Currency(base_market or 'BTC') symbol: Symbol = self.altname(currency) + base_market market: Market = self.markets.get(symbol, False) if not market: return False # min amount in quote currency limits: Limit = market.limits quote_min_amount = limits.amount ticker = tickers[symbol] # type: Ticker if currency == 'BTC' and 'USD' in base_market: last = 1.0 / ticker.last else: last = ticker.last # converting min_amount to base currency base_min_amount = last * quote_min_amount # subtracting a 0.01% fee base_min_amount = base_min_amount * 0.999 if isinstance(balance or [], dict): balance = balance.get('total', 0.0) else: balance = balance or 0.0 return balance > base_min_amount data = self._api.fetch_balance() if 'info' in data: del data['info'] data = data.pop(field) if field else data # type: dict def is_zero(v): if isinstance(v, float): return v <= 0.0 else: return v.get('total', 0.0) <= 0.0 data = {str(k): v for k, v in data.items() if not is_zero(v)} if tradeables_only: symbols = {Currency(c) + Currency('BTC') for c in data} tickers = self.get_tickers(symbols) data = { str(k): v for k, v in data.items() if is_tradeable(k, tickers, v) } return Wallet(**data)