2023-01-05 19:27:59 +00:00
|
|
|
from decimal import Decimal as D
|
|
|
|
|
2023-01-11 19:46:47 +00:00
|
|
|
from core.exchanges import GenericAPIError
|
2022-12-13 07:20:49 +00:00
|
|
|
from core.lib.elastic import store_msg
|
2023-01-11 19:46:47 +00:00
|
|
|
from core.util import logs
|
|
|
|
|
|
|
|
log = logs.get_logger(__name__)
|
2022-12-13 07:20:49 +00:00
|
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
},
|
|
|
|
)
|
2023-01-05 17:25:06 +00:00
|
|
|
|
|
|
|
|
2023-01-11 19:46:47 +00:00
|
|
|
def get_pair(account, base, quote, invert=False):
|
2023-01-05 23:37:50 +00:00
|
|
|
"""
|
2023-01-11 19:46:47 +00:00
|
|
|
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.
|
2023-01-05 23:37:50 +00:00
|
|
|
"""
|
2023-01-11 19:46:47 +00:00
|
|
|
if account.exchange == "alpaca":
|
|
|
|
separator = "/"
|
|
|
|
elif account.exchange == "oanda":
|
|
|
|
separator = "_"
|
2023-02-17 07:20:15 +00:00
|
|
|
else:
|
|
|
|
separator = "_"
|
2023-01-11 19:46:47 +00:00
|
|
|
|
|
|
|
# Flip the pair if needed
|
|
|
|
if invert:
|
|
|
|
symbol = f"{quote.upper()}{separator}{base.upper()}"
|
2023-01-05 23:37:50 +00:00
|
|
|
else:
|
2023-01-11 19:46:47 +00:00
|
|
|
symbol = f"{base.upper()}{separator}{quote.upper()}"
|
|
|
|
# Check it exists
|
|
|
|
if symbol not in account.supported_symbols:
|
|
|
|
return False
|
|
|
|
return symbol
|
2023-01-05 23:37:50 +00:00
|
|
|
|
2023-01-05 19:27:59 +00:00
|
|
|
|
2023-02-17 07:20:15 +00:00
|
|
|
def get_symbol_price(account, price_index, 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"])
|
|
|
|
return price
|
|
|
|
|
|
|
|
|
2023-01-11 19:46:47 +00:00
|
|
|
def to_currency(direction, account, amount, from_currency, to_currency):
|
2023-01-05 17:25:06 +00:00
|
|
|
"""
|
2023-01-11 19:46:47 +00:00
|
|
|
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
|
2023-01-05 17:25:06 +00:00
|
|
|
"""
|
2023-01-11 19:46:47 +00:00
|
|
|
# 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")
|
2023-02-17 07:20:15 +00:00
|
|
|
price = get_symbol_price(account, price_index, symbol)
|
2023-01-11 19:46:47 +00:00
|
|
|
|
|
|
|
# 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
|