from decimal import Decimal as D from core.exchanges import GenericAPIError from core.lib.elastic import store_msg from core.util import logs log = logs.get_logger(__name__) def get_balance_hook(user_id, user_name, account_id, account_name, balance): """ Called every time the balance is fetched on an account. Store this into Elasticsearch. """ store_msg( "balances", { "user_id": user_id, "user_name": user_name, "account_id": account_id, "account_name": account_name, "balance": balance, }, ) def get_pair(account, base, quote, invert=False): """ Get the pair for the given account and currencies. :param account: Account object :param base: Base currency :param quote: Quote currency :param invert: Invert the pair :return: currency symbol, e.g. BTC_USD, BTC/USD, etc. """ # Currently we only have two exchanges with different pair separators if account.exchange == "alpaca": separator = "/" elif account.exchange == "oanda": separator = "_" # Flip the pair if needed if invert: symbol = f"{quote.upper()}{separator}{base.upper()}" else: symbol = f"{base.upper()}{separator}{quote.upper()}" # Check it exists if symbol not in account.supported_symbols: return False return symbol def to_currency(direction, account, amount, from_currency, to_currency): """ Convert an amount from one currency to another. :param direction: Direction of the trade :param account: Account object :param amount: Amount to convert :param from_currency: Currency to convert from :param to_currency: Currency to convert to :return: Converted amount """ # If we're converting to the same currency, just return the amount if from_currency == to_currency: return amount inverted = False # This is needed because OANDA has different values for bid and ask if direction == "buy": price_index = "bids" elif direction == "sell": price_index = "asks" symbol = get_pair(account, from_currency, to_currency) if not symbol: symbol = get_pair(account, from_currency, to_currency, invert=True) inverted = True # Bit of a hack but it works if not symbol: log.error(f"Could not find symbol for {from_currency} -> {to_currency}") raise Exception("Could not find symbol") try: prices = account.client.get_currencies([symbol]) except GenericAPIError as e: log.error(f"Error getting currencies and inverted currencies: {e}") return None price = D(prices["prices"][0][price_index][0]["price"]) # If we had to flip base and quote, we need to use the reciprocal of the price if inverted: price = D(1.0) / price # Convert the amount to the destination currency converted = D(amount) * price return converted