fisk/core/exchanges/common.py

103 lines
3.0 KiB
Python
Raw Normal View History

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-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-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()}"
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-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