Some fixes for health calculations WIP.
This commit is contained in:
parent
95e2496ad3
commit
4a24f3df55
|
@ -71,10 +71,9 @@ for account in mango_accounts:
|
||||||
report: mango.AccountTokenValues = mango.AccountTokenValues.from_account_basket_base_token(
|
report: mango.AccountTokenValues = mango.AccountTokenValues.from_account_basket_base_token(
|
||||||
asset, open_orders, group)
|
asset, open_orders, group)
|
||||||
# print(report)
|
# print(report)
|
||||||
|
market_cache: mango.MarketCache = group.market_cache_from_cache(cache, report.base_token)
|
||||||
price_from_cache: mango.TokenValue = group.token_price_from_cache(cache, report.base_token)
|
price_from_cache: mango.TokenValue = group.token_price_from_cache(cache, report.base_token)
|
||||||
perp_market_cache: typing.Optional[mango.PerpMarketCache] = group.perp_market_cache_from_cache(
|
priced_report: mango.AccountTokenValues = report.priced(market_cache)
|
||||||
cache, report.base_token)
|
|
||||||
priced_report: mango.AccountTokenValues = report.priced(price_from_cache, perp_market_cache)
|
|
||||||
account_value += priced_report.net_value
|
account_value += priced_report.net_value
|
||||||
quote_token_free_in_open_orders += priced_report.quote_token_free
|
quote_token_free_in_open_orders += priced_report.quote_token_free
|
||||||
quote_token_total_in_open_orders += priced_report.quote_token_total
|
quote_token_total_in_open_orders += priced_report.quote_token_total
|
||||||
|
@ -82,7 +81,9 @@ for account in mango_accounts:
|
||||||
|
|
||||||
quote_report: mango.AccountTokenValues = mango.AccountTokenValues(account.shared_quote_token.token_info.token,
|
quote_report: mango.AccountTokenValues = mango.AccountTokenValues(account.shared_quote_token.token_info.token,
|
||||||
account.shared_quote_token.token_info.token,
|
account.shared_quote_token.token_info.token,
|
||||||
|
account.shared_quote_token.raw_deposit,
|
||||||
account.shared_quote_token.deposit,
|
account.shared_quote_token.deposit,
|
||||||
|
account.shared_quote_token.raw_borrow,
|
||||||
account.shared_quote_token.borrow,
|
account.shared_quote_token.borrow,
|
||||||
mango.TokenValue(
|
mango.TokenValue(
|
||||||
group.shared_quote_token.token, Decimal(0)),
|
group.shared_quote_token.token, Decimal(0)),
|
||||||
|
@ -92,7 +93,12 @@ for account in mango_accounts:
|
||||||
quote_token_total_in_open_orders,
|
quote_token_total_in_open_orders,
|
||||||
mango.TokenValue(
|
mango.TokenValue(
|
||||||
group.shared_quote_token.token, Decimal(0)),
|
group.shared_quote_token.token, Decimal(0)),
|
||||||
Decimal(0), Decimal(0), Decimal(0),
|
Decimal(0), Decimal(0),
|
||||||
|
mango.TokenValue(
|
||||||
|
group.shared_quote_token.token, Decimal(0)),
|
||||||
|
mango.TokenValue(
|
||||||
|
group.shared_quote_token.token, Decimal(0)),
|
||||||
|
Decimal(0), Decimal(0),
|
||||||
mango.NullLotSizeConverter())
|
mango.NullLotSizeConverter())
|
||||||
account_value += quote_report.net_value + quote_token_total_in_open_orders
|
account_value += quote_report.net_value + quote_token_total_in_open_orders
|
||||||
print(quote_report)
|
print(quote_report)
|
||||||
|
|
|
@ -8,7 +8,7 @@ from .accounttokenvalues import AccountTokenValues, PricedAccountTokenValues
|
||||||
from .addressableaccount import AddressableAccount
|
from .addressableaccount import AddressableAccount
|
||||||
from .arguments import parse_args
|
from .arguments import parse_args
|
||||||
from .balancesheet import BalanceSheet
|
from .balancesheet import BalanceSheet
|
||||||
from .cache import PriceCache, RootBankCache, PerpMarketCache, Cache
|
from .cache import PriceCache, RootBankCache, PerpMarketCache, MarketCache, Cache
|
||||||
from .client import ClientException, RateLimitException, TooMuchBandwidthRateLimitException, TooManyRequestsRateLimitException, BlockhashNotFoundException, NodeIsBehindException, FailedToFetchBlockhashException, TransactionException, CompatibleClient, BetterClient
|
from .client import ClientException, RateLimitException, TooMuchBandwidthRateLimitException, TooManyRequestsRateLimitException, BlockhashNotFoundException, NodeIsBehindException, FailedToFetchBlockhashException, TransactionException, CompatibleClient, BetterClient
|
||||||
from .combinableinstructions import CombinableInstructions
|
from .combinableinstructions import CombinableInstructions
|
||||||
from .constants import SYSTEM_PROGRAM_ADDRESS, SOL_MINT_ADDRESS, SOL_DECIMALS, SOL_DECIMAL_DIVISOR, WARNING_DISCLAIMER_TEXT, MangoConstants
|
from .constants import SYSTEM_PROGRAM_ADDRESS, SOL_MINT_ADDRESS, SOL_DECIMALS, SOL_DECIMAL_DIVISOR, WARNING_DISCLAIMER_TEXT, MangoConstants
|
||||||
|
|
|
@ -42,20 +42,26 @@ from .version import Version
|
||||||
# `AccountBasketToken` gathers basket items together instead of separate arrays.
|
# `AccountBasketToken` gathers basket items together instead of separate arrays.
|
||||||
#
|
#
|
||||||
class AccountBasketToken:
|
class AccountBasketToken:
|
||||||
def __init__(self, token_info: TokenInfo, deposit: TokenValue, borrow: TokenValue):
|
def __init__(self, token_info: TokenInfo, raw_deposit: Decimal, deposit: TokenValue, raw_borrow: Decimal, borrow: TokenValue):
|
||||||
self.token_info: TokenInfo = token_info
|
self.token_info: TokenInfo = token_info
|
||||||
|
self.raw_deposit: Decimal = raw_deposit
|
||||||
self.deposit: TokenValue = deposit
|
self.deposit: TokenValue = deposit
|
||||||
|
self.raw_borrow: Decimal = raw_borrow
|
||||||
self.borrow: TokenValue = borrow
|
self.borrow: TokenValue = borrow
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def net_value(self) -> TokenValue:
|
def net_value(self) -> TokenValue:
|
||||||
return self.deposit - self.borrow
|
return self.deposit - self.borrow
|
||||||
|
|
||||||
|
@property
|
||||||
|
def raw_net_value(self) -> Decimal:
|
||||||
|
return self.raw_deposit - self.raw_borrow
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"""« 𝙰𝚌𝚌𝚘𝚞𝚗𝚝𝙱𝚊𝚜𝚔𝚎𝚝𝚃𝚘𝚔𝚎𝚗 {self.token_info.token.symbol}
|
return f"""« 𝙰𝚌𝚌𝚘𝚞𝚗𝚝𝙱𝚊𝚜𝚔𝚎𝚝𝚃𝚘𝚔𝚎𝚗 {self.token_info.token.symbol}
|
||||||
Net Value: {self.net_value}
|
Net Value: {self.net_value}
|
||||||
Deposited: {self.deposit}
|
Deposited: {self.deposit} (raw value: {self.raw_deposit})
|
||||||
Borrowed: {self.borrow}
|
Borrowed: {self.borrow} (raw value {self.raw_borrow})
|
||||||
»"""
|
»"""
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
|
@ -68,8 +74,8 @@ class AccountBasketToken:
|
||||||
# account.
|
# account.
|
||||||
#
|
#
|
||||||
class AccountBasketBaseToken(AccountBasketToken):
|
class AccountBasketBaseToken(AccountBasketToken):
|
||||||
def __init__(self, token_info: TokenInfo, quote_token_info: TokenInfo, deposit: TokenValue, borrow: TokenValue, spot_open_orders: typing.Optional[PublicKey], perp_account: typing.Optional[PerpAccount]):
|
def __init__(self, token_info: TokenInfo, quote_token_info: TokenInfo, raw_deposit: Decimal, deposit: TokenValue, raw_borrow: Decimal, borrow: TokenValue, spot_open_orders: typing.Optional[PublicKey], perp_account: typing.Optional[PerpAccount]):
|
||||||
super().__init__(token_info, deposit, borrow)
|
super().__init__(token_info, raw_deposit, deposit, raw_borrow, borrow)
|
||||||
self.quote_token_info: TokenInfo = quote_token_info
|
self.quote_token_info: TokenInfo = quote_token_info
|
||||||
self.spot_open_orders: typing.Optional[PublicKey] = spot_open_orders
|
self.spot_open_orders: typing.Optional[PublicKey] = spot_open_orders
|
||||||
self.perp_account: typing.Optional[PerpAccount] = perp_account
|
self.perp_account: typing.Optional[PerpAccount] = perp_account
|
||||||
|
@ -80,8 +86,8 @@ class AccountBasketBaseToken(AccountBasketToken):
|
||||||
perp_account = f"{self.perp_account}".replace("\n", "\n ")
|
perp_account = f"{self.perp_account}".replace("\n", "\n ")
|
||||||
return f"""« 𝙰𝚌𝚌𝚘𝚞𝚗𝚝𝙱𝚊𝚜𝚔𝚎𝚝𝙱𝚊𝚜𝚎𝚃𝚘𝚔𝚎𝚗 {self.token_info.token.symbol}
|
return f"""« 𝙰𝚌𝚌𝚘𝚞𝚗𝚝𝙱𝚊𝚜𝚔𝚎𝚝𝙱𝚊𝚜𝚎𝚃𝚘𝚔𝚎𝚗 {self.token_info.token.symbol}
|
||||||
Net Value: {self.net_value}
|
Net Value: {self.net_value}
|
||||||
Deposited: {self.deposit}
|
Deposited: {self.deposit} (raw value: {self.raw_deposit})
|
||||||
Borrowed: {self.borrow}
|
Borrowed: {self.borrow} (raw value {self.raw_borrow})
|
||||||
Spot OpenOrders: {self.spot_open_orders or "None"}
|
Spot OpenOrders: {self.spot_open_orders or "None"}
|
||||||
Perp Account:
|
Perp Account:
|
||||||
{perp_account}
|
{perp_account}
|
||||||
|
@ -146,9 +152,11 @@ class Account(AddressableAccount):
|
||||||
|
|
||||||
for index, token_info in enumerate(group.tokens[:-1]):
|
for index, token_info in enumerate(group.tokens[:-1]):
|
||||||
if token_info:
|
if token_info:
|
||||||
intrinsic_deposit = token_info.root_bank.deposit_index * layout.deposits[index]
|
raw_deposit: Decimal = layout.deposits[index]
|
||||||
|
intrinsic_deposit = token_info.root_bank.deposit_index * raw_deposit
|
||||||
deposit = TokenValue(token_info.token, token_info.token.shift_to_decimals(intrinsic_deposit))
|
deposit = TokenValue(token_info.token, token_info.token.shift_to_decimals(intrinsic_deposit))
|
||||||
intrinsic_borrow = token_info.root_bank.borrow_index * layout.borrows[index]
|
raw_borrow: Decimal = layout.borrows[index]
|
||||||
|
intrinsic_borrow = token_info.root_bank.borrow_index * raw_borrow
|
||||||
borrow = TokenValue(token_info.token, token_info.token.shift_to_decimals(intrinsic_borrow))
|
borrow = TokenValue(token_info.token, token_info.token.shift_to_decimals(intrinsic_borrow))
|
||||||
perp_open_orders = PerpOpenOrders(placed_orders_all_markets[index])
|
perp_open_orders = PerpOpenOrders(placed_orders_all_markets[index])
|
||||||
group_basket_market = group.markets[index]
|
group_basket_market = group.markets[index]
|
||||||
|
@ -163,19 +171,22 @@ class Account(AddressableAccount):
|
||||||
mngo_token_info.token)
|
mngo_token_info.token)
|
||||||
spot_open_orders = layout.spot_open_orders[index]
|
spot_open_orders = layout.spot_open_orders[index]
|
||||||
basket_item: AccountBasketBaseToken = AccountBasketBaseToken(
|
basket_item: AccountBasketBaseToken = AccountBasketBaseToken(
|
||||||
token_info, quote_token_info, deposit, borrow, spot_open_orders, perp_account)
|
token_info, quote_token_info, raw_deposit, deposit, raw_borrow, borrow, spot_open_orders, perp_account)
|
||||||
basket += [basket_item]
|
basket += [basket_item]
|
||||||
active_in_basket += [True]
|
active_in_basket += [True]
|
||||||
else:
|
else:
|
||||||
active_in_basket += [False]
|
active_in_basket += [False]
|
||||||
|
|
||||||
intrinsic_quote_deposit = quote_token_info.root_bank.deposit_index * layout.deposits[-1]
|
raw_quote_deposit: Decimal = layout.deposits[-1]
|
||||||
|
intrinsic_quote_deposit = quote_token_info.root_bank.deposit_index * raw_quote_deposit
|
||||||
quote_deposit = TokenValue(quote_token_info.token,
|
quote_deposit = TokenValue(quote_token_info.token,
|
||||||
quote_token_info.token.shift_to_decimals(intrinsic_quote_deposit))
|
quote_token_info.token.shift_to_decimals(intrinsic_quote_deposit))
|
||||||
intrinsic_quote_borrow = quote_token_info.root_bank.borrow_index * layout.borrows[-1]
|
raw_quote_borrow: Decimal = layout.borrows[-1]
|
||||||
|
intrinsic_quote_borrow = quote_token_info.root_bank.borrow_index * raw_quote_borrow
|
||||||
quote_borrow = TokenValue(quote_token_info.token,
|
quote_borrow = TokenValue(quote_token_info.token,
|
||||||
quote_token_info.token.shift_to_decimals(intrinsic_quote_borrow))
|
quote_token_info.token.shift_to_decimals(intrinsic_quote_borrow))
|
||||||
quote: AccountBasketToken = AccountBasketToken(quote_token_info, quote_deposit, quote_borrow)
|
quote: AccountBasketToken = AccountBasketToken(
|
||||||
|
quote_token_info, raw_quote_deposit, quote_deposit, raw_quote_borrow, quote_borrow)
|
||||||
|
|
||||||
msrm_amount: Decimal = layout.msrm_amount
|
msrm_amount: Decimal = layout.msrm_amount
|
||||||
being_liquidated: bool = bool(layout.being_liquidated)
|
being_liquidated: bool = bool(layout.being_liquidated)
|
||||||
|
|
|
@ -18,7 +18,7 @@ import typing
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
|
||||||
from .account import AccountBasketBaseToken
|
from .account import AccountBasketBaseToken
|
||||||
from .cache import PerpMarketCache
|
from .cache import PerpMarketCache, MarketCache
|
||||||
from .calculators.unsettledfundingcalculator import calculate_unsettled_funding, UnsettledFundingParams
|
from .calculators.unsettledfundingcalculator import calculate_unsettled_funding, UnsettledFundingParams
|
||||||
from .group import Group
|
from .group import Group
|
||||||
from .lotsizeconverter import LotSizeConverter
|
from .lotsizeconverter import LotSizeConverter
|
||||||
|
@ -55,10 +55,12 @@ def _token_values_from_open_orders(base_token: Token, quote_token: Token, spot_o
|
||||||
# `AccountTokenValues` gathers basket items together instead of separate arrays.
|
# `AccountTokenValues` gathers basket items together instead of separate arrays.
|
||||||
#
|
#
|
||||||
class AccountTokenValues:
|
class AccountTokenValues:
|
||||||
def __init__(self, base_token: Token, quote_token: Token, deposit: TokenValue, borrow: TokenValue, base_token_free: TokenValue, base_token_total: TokenValue, quote_token_free: TokenValue, quote_token_total: TokenValue, perp_base_position: TokenValue, raw_perp_quote_position: Decimal, long_settled_funding: Decimal, short_settled_funding: Decimal, lot_size_converter: LotSizeConverter):
|
def __init__(self, base_token: Token, quote_token: Token, raw_deposit: Decimal, deposit: TokenValue, raw_borrow: Decimal, borrow: TokenValue, base_token_free: TokenValue, base_token_total: TokenValue, quote_token_free: TokenValue, quote_token_total: TokenValue, perp_base_position: TokenValue, raw_perp_quote_position: Decimal, raw_taker_quote: Decimal, bids_quantity: TokenValue, asks_quantity: TokenValue, long_settled_funding: Decimal, short_settled_funding: Decimal, lot_size_converter: LotSizeConverter):
|
||||||
self.base_token: Token = base_token
|
self.base_token: Token = base_token
|
||||||
self.quote_token: Token = quote_token
|
self.quote_token: Token = quote_token
|
||||||
|
self.raw_deposit: Decimal = raw_deposit
|
||||||
self.deposit: TokenValue = deposit
|
self.deposit: TokenValue = deposit
|
||||||
|
self.raw_borrow: Decimal = raw_borrow
|
||||||
self.borrow: TokenValue = borrow
|
self.borrow: TokenValue = borrow
|
||||||
|
|
||||||
self.base_token_free: TokenValue = base_token_free
|
self.base_token_free: TokenValue = base_token_free
|
||||||
|
@ -67,6 +69,9 @@ class AccountTokenValues:
|
||||||
self.quote_token_total: TokenValue = quote_token_total
|
self.quote_token_total: TokenValue = quote_token_total
|
||||||
self.perp_base_position: TokenValue = perp_base_position
|
self.perp_base_position: TokenValue = perp_base_position
|
||||||
self.raw_perp_quote_position: Decimal = raw_perp_quote_position
|
self.raw_perp_quote_position: Decimal = raw_perp_quote_position
|
||||||
|
self.raw_taker_quote: Decimal = raw_taker_quote
|
||||||
|
self.bids_quantity: TokenValue = bids_quantity
|
||||||
|
self.asks_quantity: TokenValue = asks_quantity
|
||||||
self.long_settled_funding: Decimal = long_settled_funding
|
self.long_settled_funding: Decimal = long_settled_funding
|
||||||
self.short_settled_funding: Decimal = short_settled_funding
|
self.short_settled_funding: Decimal = short_settled_funding
|
||||||
self.lot_size_converter: LotSizeConverter = lot_size_converter
|
self.lot_size_converter: LotSizeConverter = lot_size_converter
|
||||||
|
@ -83,8 +88,16 @@ class AccountTokenValues:
|
||||||
def quote_token_locked(self) -> TokenValue:
|
def quote_token_locked(self) -> TokenValue:
|
||||||
return self.quote_token_total - self.quote_token_free
|
return self.quote_token_total - self.quote_token_free
|
||||||
|
|
||||||
def priced(self, price: TokenValue, perp_market_cache: typing.Optional[PerpMarketCache]) -> "PricedAccountTokenValues":
|
@property
|
||||||
return PricedAccountTokenValues(self, price, perp_market_cache)
|
def if_all_bids_executed(self) -> TokenValue:
|
||||||
|
return self.perp_base_position + self.bids_quantity
|
||||||
|
|
||||||
|
@property
|
||||||
|
def if_all_asks_executed(self) -> TokenValue:
|
||||||
|
return self.perp_base_position - self.asks_quantity
|
||||||
|
|
||||||
|
def priced(self, market_cache: MarketCache) -> "PricedAccountTokenValues":
|
||||||
|
return PricedAccountTokenValues(self, market_cache)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_account_basket_base_token(account_basket_token: AccountBasketBaseToken, open_orders_by_address: typing.Dict[str, OpenOrders], group: Group) -> "AccountTokenValues":
|
def from_account_basket_base_token(account_basket_token: AccountBasketBaseToken, open_orders_by_address: typing.Dict[str, OpenOrders], group: Group) -> "AccountTokenValues":
|
||||||
|
@ -106,16 +119,28 @@ class AccountTokenValues:
|
||||||
long_settled_funding: Decimal = perp_account.long_settled_funding / lot_size_converter.quote_lot_size
|
long_settled_funding: Decimal = perp_account.long_settled_funding / lot_size_converter.quote_lot_size
|
||||||
short_settled_funding: Decimal = perp_account.short_settled_funding / lot_size_converter.quote_lot_size
|
short_settled_funding: Decimal = perp_account.short_settled_funding / lot_size_converter.quote_lot_size
|
||||||
|
|
||||||
return AccountTokenValues(base_token, quote_token, account_basket_token.deposit, account_basket_token.borrow, base_token_free, base_token_total, quote_token_free, quote_token_total, perp_base_position, perp_quote_position, long_settled_funding, short_settled_funding, lot_size_converter)
|
taker_quote: Decimal = perp_account.taker_quote * lot_size_converter.quote_lot_size
|
||||||
|
bids_quantity: TokenValue = TokenValue(base_token, base_token.shift_to_decimals(
|
||||||
|
perp_account.bids_quantity * lot_size_converter.base_lot_size))
|
||||||
|
asks_quantity: TokenValue = TokenValue(base_token, base_token.shift_to_decimals(
|
||||||
|
perp_account.asks_quantity * lot_size_converter.base_lot_size))
|
||||||
|
|
||||||
|
return AccountTokenValues(base_token, quote_token, account_basket_token.raw_deposit, account_basket_token.deposit, account_basket_token.raw_borrow, account_basket_token.borrow, base_token_free, base_token_total, quote_token_free, quote_token_total, perp_base_position, perp_quote_position, taker_quote, bids_quantity, asks_quantity, long_settled_funding, short_settled_funding, lot_size_converter)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"""« 𝙰𝚌𝚌𝚘𝚞𝚗𝚝𝚃𝚘𝚔𝚎𝚗𝚁𝚎𝚙𝚘𝚛𝚝 {self.base_token.symbol}
|
return f"""« 𝙰𝚌𝚌𝚘𝚞𝚗𝚝𝚃𝚘𝚔𝚎𝚗𝚁𝚎𝚙𝚘𝚛𝚝 {self.base_token.symbol}
|
||||||
Deposited: {self.deposit}
|
Deposited : {self.deposit}
|
||||||
Borrowed : {self.borrow}
|
Borrowed : {self.borrow}
|
||||||
Unsettled:
|
Unsettled:
|
||||||
Base : {self.base_token_total} ({self.base_token_free} free)
|
Base : {self.base_token_total} ({self.base_token_free} free)
|
||||||
Quote : {self.quote_token_total} ({self.quote_token_free} free)
|
Quote : {self.quote_token_total} ({self.quote_token_free} free)
|
||||||
Net Value: {self.net_value}
|
Perp:
|
||||||
|
Base : {self.perp_base_position}
|
||||||
|
Quote : {self.raw_perp_quote_position}
|
||||||
|
If Executed:
|
||||||
|
All Bids : {self.if_all_bids_executed}
|
||||||
|
All Asks : {self.if_all_asks_executed}
|
||||||
|
Net Value : {self.net_value}
|
||||||
»"""
|
»"""
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
|
@ -123,29 +148,45 @@ class AccountTokenValues:
|
||||||
|
|
||||||
|
|
||||||
class PricedAccountTokenValues(AccountTokenValues):
|
class PricedAccountTokenValues(AccountTokenValues):
|
||||||
def __init__(self, original_account_token_values: AccountTokenValues, price: TokenValue, perp_market_cache: typing.Optional[PerpMarketCache]):
|
def __init__(self, original_account_token_values: AccountTokenValues, market_cache: MarketCache):
|
||||||
deposit: TokenValue = original_account_token_values.deposit * price
|
price: TokenValue = market_cache.adjusted_price(
|
||||||
borrow: TokenValue = original_account_token_values.borrow * price
|
original_account_token_values.base_token, original_account_token_values.quote_token)
|
||||||
|
|
||||||
|
if market_cache.root_bank is None:
|
||||||
|
raise Exception(f"No root bank for token {original_account_token_values.base_token} in {market_cache}")
|
||||||
|
|
||||||
|
deposit_value: Decimal = original_account_token_values.raw_deposit * market_cache.root_bank.deposit_index * price.value
|
||||||
|
shifted_deposit_value: Decimal = original_account_token_values.quote_token.shift_to_decimals(deposit_value)
|
||||||
|
deposit: TokenValue = TokenValue(original_account_token_values.quote_token, shifted_deposit_value)
|
||||||
|
|
||||||
|
borrow_value: Decimal = original_account_token_values.raw_borrow * market_cache.root_bank.borrow_index * price.value
|
||||||
|
shifted_borrow_value: Decimal = original_account_token_values.quote_token.shift_to_decimals(borrow_value)
|
||||||
|
borrow: TokenValue = TokenValue(original_account_token_values.quote_token, shifted_borrow_value)
|
||||||
|
|
||||||
base_token_free: TokenValue = original_account_token_values.base_token_free * price
|
base_token_free: TokenValue = original_account_token_values.base_token_free * price
|
||||||
base_token_total: TokenValue = original_account_token_values.base_token_total * price
|
base_token_total: TokenValue = original_account_token_values.base_token_total * price
|
||||||
|
|
||||||
perp_base_position: TokenValue = original_account_token_values.perp_base_position * price
|
perp_base_position: TokenValue = original_account_token_values.perp_base_position * price
|
||||||
|
|
||||||
super().__init__(original_account_token_values.base_token, original_account_token_values.quote_token,
|
super().__init__(original_account_token_values.base_token, original_account_token_values.quote_token,
|
||||||
deposit, borrow, base_token_free, base_token_total,
|
original_account_token_values.raw_deposit, deposit,
|
||||||
|
original_account_token_values.raw_borrow, borrow, base_token_free, base_token_total,
|
||||||
original_account_token_values.quote_token_free,
|
original_account_token_values.quote_token_free,
|
||||||
original_account_token_values.quote_token_total,
|
original_account_token_values.quote_token_total,
|
||||||
perp_base_position, original_account_token_values.raw_perp_quote_position,
|
perp_base_position, original_account_token_values.raw_perp_quote_position,
|
||||||
|
original_account_token_values.raw_taker_quote,
|
||||||
|
original_account_token_values.bids_quantity, original_account_token_values.asks_quantity,
|
||||||
original_account_token_values.long_settled_funding, original_account_token_values.short_settled_funding,
|
original_account_token_values.long_settled_funding, original_account_token_values.short_settled_funding,
|
||||||
original_account_token_values.lot_size_converter)
|
original_account_token_values.lot_size_converter)
|
||||||
self.original_account_token_values: AccountTokenValues = original_account_token_values
|
self.original_account_token_values: AccountTokenValues = original_account_token_values
|
||||||
self.price: TokenValue = price
|
self.price: TokenValue = price
|
||||||
self.perp_market_cache: typing.Optional[PerpMarketCache] = perp_market_cache
|
self.perp_market_cache: typing.Optional[PerpMarketCache] = market_cache.perp_market
|
||||||
perp_quote_position: TokenValue = TokenValue(original_account_token_values.quote_token, Decimal(0))
|
perp_quote_position: TokenValue = TokenValue(
|
||||||
if perp_market_cache is not None:
|
original_account_token_values.quote_token, original_account_token_values.raw_perp_quote_position)
|
||||||
|
if market_cache.perp_market is not None:
|
||||||
original: AccountTokenValues = original_account_token_values
|
original: AccountTokenValues = original_account_token_values
|
||||||
long_funding: Decimal = perp_market_cache.long_funding / original.lot_size_converter.quote_lot_size
|
long_funding: Decimal = market_cache.perp_market.long_funding / original.lot_size_converter.quote_lot_size
|
||||||
short_funding: Decimal = perp_market_cache.short_funding / original.lot_size_converter.quote_lot_size
|
short_funding: Decimal = market_cache.perp_market.short_funding / original.lot_size_converter.quote_lot_size
|
||||||
unsettled_funding: TokenValue = calculate_unsettled_funding(UnsettledFundingParams(
|
unsettled_funding: TokenValue = calculate_unsettled_funding(UnsettledFundingParams(
|
||||||
quote_token=original.quote_token,
|
quote_token=original.quote_token,
|
||||||
base_position=original.perp_base_position,
|
base_position=original.perp_base_position,
|
||||||
|
@ -154,21 +195,45 @@ class PricedAccountTokenValues(AccountTokenValues):
|
||||||
short_funding=short_funding,
|
short_funding=short_funding,
|
||||||
short_settled_funding=original.short_settled_funding
|
short_settled_funding=original.short_settled_funding
|
||||||
))
|
))
|
||||||
perp_quote_position += unsettled_funding
|
perp_quote_position -= unsettled_funding
|
||||||
|
|
||||||
self.perp_quote_position: TokenValue = perp_quote_position
|
self.perp_quote_position: TokenValue = perp_quote_position
|
||||||
|
|
||||||
|
@property
|
||||||
|
def if_all_bids_executed(self) -> TokenValue:
|
||||||
|
return self.perp_base_position + (self.bids_quantity * self.price)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def if_all_asks_executed(self) -> TokenValue:
|
||||||
|
return self.perp_base_position - (self.asks_quantity * self.price)
|
||||||
|
|
||||||
|
def if_worst_execution(self) -> typing.Tuple[TokenValue, TokenValue]:
|
||||||
|
taker_quote: TokenValue = TokenValue(self.perp_quote_position.token, self.raw_taker_quote)
|
||||||
|
# print("Quote calc", self.perp_quote_position, taker_quote, self.bids_quantity, self.price)
|
||||||
|
|
||||||
|
if abs(self.if_all_bids_executed.value) > abs(self.if_all_asks_executed.value):
|
||||||
|
base_position = self.if_all_bids_executed
|
||||||
|
quote_position = self.perp_quote_position + taker_quote - (self.bids_quantity * self.price)
|
||||||
|
else:
|
||||||
|
base_position = self.if_all_asks_executed
|
||||||
|
quote_position = self.perp_quote_position + taker_quote + (self.asks_quantity * self.price)
|
||||||
|
|
||||||
|
return base_position, quote_position
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"""« 𝙿𝚛𝚒𝚌𝚎𝚍𝙰𝚌𝚌𝚘𝚞𝚗𝚝𝚃𝚘𝚔𝚎𝚗𝚅𝚊𝚕𝚞𝚎𝚜 {self.base_token.symbol} priced in {self.quote_token.symbol}
|
return f"""« 𝙿𝚛𝚒𝚌𝚎𝚍𝙰𝚌𝚌𝚘𝚞𝚗𝚝𝚃𝚘𝚔𝚎𝚗𝚅𝚊𝚕𝚞𝚎𝚜 {self.base_token.symbol} priced in {self.quote_token.symbol}
|
||||||
Deposited: {self.original_account_token_values.deposit:<45} worth {self.deposit}
|
Deposited : {self.original_account_token_values.deposit:<45} worth {self.deposit}
|
||||||
Borrowed: {self.original_account_token_values.borrow:<45} worth {self.borrow}
|
Borrowed : {self.original_account_token_values.borrow:<45} worth {self.borrow}
|
||||||
Unsettled:
|
Unsettled:
|
||||||
Base : {self.original_account_token_values.base_token_total:<45} worth {self.base_token_total}
|
Base : {self.original_account_token_values.base_token_total:<45} worth {self.base_token_total}
|
||||||
Quote : {self.original_account_token_values.quote_token_total:<45} worth {self.quote_token_total}
|
Quote : {self.original_account_token_values.quote_token_total:<45} worth {self.quote_token_total}
|
||||||
Perp:
|
Perp:
|
||||||
Base : {self.original_account_token_values.perp_base_position:<45} worth {self.perp_base_position}
|
Base : {self.original_account_token_values.perp_base_position:<45} worth {self.perp_base_position}
|
||||||
Quote : {self.perp_quote_position:<45} worth {self.perp_quote_position}
|
Quote : {self.perp_quote_position:<45} worth {self.perp_quote_position}
|
||||||
Net Value: {self.original_account_token_values.net_value:<45} worth {self.net_value}
|
If Executed:
|
||||||
|
All Bids : {self.original_account_token_values.if_all_bids_executed:<45} worth {self.if_all_bids_executed}
|
||||||
|
All Asks : {self.original_account_token_values.if_all_asks_executed:<45} worth {self.if_all_asks_executed}
|
||||||
|
Net Value : {self.original_account_token_values.net_value:<45} worth {self.net_value}
|
||||||
»"""
|
»"""
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
|
|
|
@ -24,15 +24,15 @@ from .addressableaccount import AddressableAccount
|
||||||
from .context import Context
|
from .context import Context
|
||||||
from .layouts import layouts
|
from .layouts import layouts
|
||||||
from .metadata import Metadata
|
from .metadata import Metadata
|
||||||
|
from .token import Token
|
||||||
|
from .tokenvalue import TokenValue
|
||||||
from .version import Version
|
from .version import Version
|
||||||
|
|
||||||
|
|
||||||
# # 🥭 NodeBank class
|
# # 🥭 PriceCache class
|
||||||
#
|
#
|
||||||
# `NodeBank` stores details of deposits/borrows and vault.
|
# `PriceCache` stores a cached price.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
class PriceCache:
|
class PriceCache:
|
||||||
def __init__(self, price: Decimal, last_update: datetime):
|
def __init__(self, price: Decimal, last_update: datetime):
|
||||||
self.price: Decimal = price
|
self.price: Decimal = price
|
||||||
|
@ -45,12 +45,16 @@ class PriceCache:
|
||||||
return PriceCache(layout.price, layout.last_update)
|
return PriceCache(layout.price, layout.last_update)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"« 𝙿𝚛𝚒𝚌𝚎𝙲𝚊𝚌𝚑𝚎 [{self.last_update}] {self.price:,.8f} »"
|
return f"« 𝙿𝚛𝚒𝚌𝚎𝙲𝚊𝚌𝚑𝚎 [{self.last_update}] {self.price:,.20f} »"
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f"{self}"
|
return f"{self}"
|
||||||
|
|
||||||
|
|
||||||
|
# # 🥭 RootBankCache class
|
||||||
|
#
|
||||||
|
# `RootBankCache` stores cached details of deposits and borrows.
|
||||||
|
#
|
||||||
class RootBankCache:
|
class RootBankCache:
|
||||||
def __init__(self, deposit_index: Decimal, borrow_index: Decimal, last_update: datetime):
|
def __init__(self, deposit_index: Decimal, borrow_index: Decimal, last_update: datetime):
|
||||||
self.deposit_index: Decimal = deposit_index
|
self.deposit_index: Decimal = deposit_index
|
||||||
|
@ -64,12 +68,16 @@ class RootBankCache:
|
||||||
return RootBankCache(layout.deposit_index, layout.borrow_index, layout.last_update)
|
return RootBankCache(layout.deposit_index, layout.borrow_index, layout.last_update)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"« 𝚁𝚘𝚘𝚝𝙱𝚊𝚗𝚔𝙲𝚊𝚌𝚑𝚎 [{self.last_update}] {self.deposit_index:,.8f} / {self.borrow_index:,.8f} »"
|
return f"« 𝚁𝚘𝚘𝚝𝙱𝚊𝚗𝚔𝙲𝚊𝚌𝚑𝚎 [{self.last_update}] {self.deposit_index:,.20f} / {self.borrow_index:,.20f} »"
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f"{self}"
|
return f"{self}"
|
||||||
|
|
||||||
|
|
||||||
|
# # 🥭 PerpMarketCache class
|
||||||
|
#
|
||||||
|
# `PerpMarketCache` stores cached details of long and short funding.
|
||||||
|
#
|
||||||
class PerpMarketCache:
|
class PerpMarketCache:
|
||||||
def __init__(self, long_funding: Decimal, short_funding: Decimal, last_update: datetime):
|
def __init__(self, long_funding: Decimal, short_funding: Decimal, last_update: datetime):
|
||||||
self.long_funding: Decimal = long_funding
|
self.long_funding: Decimal = long_funding
|
||||||
|
@ -83,7 +91,44 @@ class PerpMarketCache:
|
||||||
return PerpMarketCache(layout.long_funding, layout.short_funding, layout.last_update)
|
return PerpMarketCache(layout.long_funding, layout.short_funding, layout.last_update)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"« 𝙿𝚎𝚛𝚙𝙼𝚊𝚛𝚔𝚎𝚝𝙲𝚊𝚌𝚑𝚎 [{self.last_update}] {self.long_funding:,.8f} / {self.short_funding:,.8f} »"
|
return f"« 𝙿𝚎𝚛𝚙𝙼𝚊𝚛𝚔𝚎𝚝𝙲𝚊𝚌𝚑𝚎 [{self.last_update}] {self.long_funding:,.20f} / {self.short_funding:,.20f} »"
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f"{self}"
|
||||||
|
|
||||||
|
|
||||||
|
# # 🥭 MarketCache class
|
||||||
|
#
|
||||||
|
# `MarketCache` stores cached details of price, root bank, and perp market, for a particular market.
|
||||||
|
#
|
||||||
|
class MarketCache:
|
||||||
|
def __init__(self, price: typing.Optional[PriceCache], root_bank: typing.Optional[RootBankCache], perp_market: typing.Optional[PerpMarketCache]):
|
||||||
|
self.price: typing.Optional[PriceCache] = price
|
||||||
|
self.root_bank: typing.Optional[RootBankCache] = root_bank
|
||||||
|
self.perp_market: typing.Optional[PerpMarketCache] = perp_market
|
||||||
|
|
||||||
|
def adjusted_price(self, token: Token, quote_token: Token) -> TokenValue:
|
||||||
|
if token == quote_token:
|
||||||
|
# The price of 1 unit of the shared quote token is always 1.
|
||||||
|
return TokenValue(token, Decimal(1))
|
||||||
|
|
||||||
|
if self.price is None:
|
||||||
|
raise Exception(f"Could not find price index of basket token {token.symbol}.")
|
||||||
|
|
||||||
|
price: Decimal = self.price.price
|
||||||
|
decimals_difference = token.decimals - quote_token.decimals
|
||||||
|
if decimals_difference != 0:
|
||||||
|
adjustment = 10 ** decimals_difference
|
||||||
|
price = price * adjustment
|
||||||
|
|
||||||
|
return TokenValue(quote_token, price)
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return f"""« 𝙼𝚊𝚛𝚔𝚎𝚝𝙲𝚊𝚌𝚑𝚎
|
||||||
|
{self.price}
|
||||||
|
{self.root_bank}
|
||||||
|
{self.perp_market}
|
||||||
|
»"""
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f"{self}"
|
return f"{self}"
|
||||||
|
@ -93,7 +138,6 @@ class PerpMarketCache:
|
||||||
#
|
#
|
||||||
# `Cache` stores cache details of prices, root banks and perp markets.
|
# `Cache` stores cache details of prices, root banks and perp markets.
|
||||||
#
|
#
|
||||||
|
|
||||||
class Cache(AddressableAccount):
|
class Cache(AddressableAccount):
|
||||||
def __init__(self, account_info: AccountInfo, version: Version, meta_data: Metadata,
|
def __init__(self, account_info: AccountInfo, version: Version, meta_data: Metadata,
|
||||||
price_cache: typing.Sequence[typing.Optional[PriceCache]],
|
price_cache: typing.Sequence[typing.Optional[PriceCache]],
|
||||||
|
@ -136,6 +180,9 @@ class Cache(AddressableAccount):
|
||||||
raise Exception(f"Cache account not found at address '{address}'")
|
raise Exception(f"Cache account not found at address '{address}'")
|
||||||
return Cache.parse(account_info)
|
return Cache.parse(account_info)
|
||||||
|
|
||||||
|
def market_cache_for_index(self, index: int) -> MarketCache:
|
||||||
|
return MarketCache(self.price_cache[index], self.root_bank_cache[index], self.perp_market_cache[index])
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
def _render_list(items, stub):
|
def _render_list(items, stub):
|
||||||
rendered = []
|
rendered = []
|
||||||
|
|
|
@ -23,7 +23,7 @@ from mango.perpmarketinfo import PerpMarketInfo
|
||||||
|
|
||||||
from ..account import Account, AccountBasketBaseToken
|
from ..account import Account, AccountBasketBaseToken
|
||||||
from ..accounttokenvalues import AccountTokenValues, PricedAccountTokenValues
|
from ..accounttokenvalues import AccountTokenValues, PricedAccountTokenValues
|
||||||
from ..cache import Cache, PerpMarketCache
|
from ..cache import Cache, MarketCache
|
||||||
from ..context import Context
|
from ..context import Context
|
||||||
from ..group import Group
|
from ..group import Group
|
||||||
from ..lotsizeconverter import NullLotSizeConverter
|
from ..lotsizeconverter import NullLotSizeConverter
|
||||||
|
@ -112,10 +112,12 @@ class HealthCalculator:
|
||||||
# if (asset.deposit.value != 0) or (asset.borrow.value != 0) or (asset.net_value.value != 0):
|
# if (asset.deposit.value != 0) or (asset.borrow.value != 0) or (asset.net_value.value != 0):
|
||||||
report: AccountTokenValues = AccountTokenValues.from_account_basket_base_token(
|
report: AccountTokenValues = AccountTokenValues.from_account_basket_base_token(
|
||||||
asset, open_orders_by_address, group)
|
asset, open_orders_by_address, group)
|
||||||
price: TokenValue = group.token_price_from_cache(cache, report.base_token)
|
# print("report", report)
|
||||||
perp_market_cache: typing.Optional[PerpMarketCache] = group.perp_market_cache_from_cache(
|
# price: TokenValue = group.token_price_from_cache(cache, report.base_token)
|
||||||
cache, report.base_token)
|
market_cache: MarketCache = group.market_cache_from_cache(cache, report.base_token)
|
||||||
priced_report: PricedAccountTokenValues = report.priced(price, perp_market_cache)
|
# print("Market cache", market_cache)
|
||||||
|
priced_report: PricedAccountTokenValues = report.priced(market_cache)
|
||||||
|
# print("priced_report", priced_report)
|
||||||
priced_reports += [priced_report]
|
priced_reports += [priced_report]
|
||||||
|
|
||||||
quote_token_free_in_open_orders: TokenValue = TokenValue(group.shared_quote_token.token, Decimal(0))
|
quote_token_free_in_open_orders: TokenValue = TokenValue(group.shared_quote_token.token, Decimal(0))
|
||||||
|
@ -123,20 +125,29 @@ class HealthCalculator:
|
||||||
for priced_report in priced_reports:
|
for priced_report in priced_reports:
|
||||||
quote_token_free_in_open_orders += priced_report.quote_token_free
|
quote_token_free_in_open_orders += priced_report.quote_token_free
|
||||||
quote_token_total_in_open_orders += priced_report.quote_token_total
|
quote_token_total_in_open_orders += priced_report.quote_token_total
|
||||||
|
# print("quote_token_free_in_open_orders", quote_token_free_in_open_orders)
|
||||||
|
# print("quote_token_total_in_open_orders", quote_token_total_in_open_orders)
|
||||||
|
|
||||||
quote_report: AccountTokenValues = AccountTokenValues(account.shared_quote_token.token_info.token,
|
quote_report: AccountTokenValues = AccountTokenValues(account.shared_quote_token.token_info.token,
|
||||||
account.shared_quote_token.token_info.token,
|
account.shared_quote_token.token_info.token,
|
||||||
|
account.shared_quote_token.raw_deposit,
|
||||||
account.shared_quote_token.deposit,
|
account.shared_quote_token.deposit,
|
||||||
|
account.shared_quote_token.raw_borrow,
|
||||||
account.shared_quote_token.borrow,
|
account.shared_quote_token.borrow,
|
||||||
TokenValue(group.shared_quote_token.token, Decimal(0)),
|
TokenValue(group.shared_quote_token.token, Decimal(0)),
|
||||||
TokenValue(group.shared_quote_token.token, Decimal(0)),
|
TokenValue(group.shared_quote_token.token, Decimal(0)),
|
||||||
quote_token_free_in_open_orders,
|
quote_token_free_in_open_orders,
|
||||||
quote_token_total_in_open_orders,
|
quote_token_total_in_open_orders,
|
||||||
TokenValue(group.shared_quote_token.token, Decimal(0)),
|
TokenValue(group.shared_quote_token.token, Decimal(0)),
|
||||||
Decimal(0), Decimal(0), Decimal(0),
|
Decimal(0), Decimal(0),
|
||||||
|
TokenValue(group.shared_quote_token.token, Decimal(0)),
|
||||||
|
TokenValue(group.shared_quote_token.token, Decimal(0)),
|
||||||
|
Decimal(0), Decimal(0),
|
||||||
NullLotSizeConverter())
|
NullLotSizeConverter())
|
||||||
|
# print("quote_report", quote_report)
|
||||||
|
|
||||||
health: Decimal = quote_report.net_value.value
|
health: Decimal = quote_report.net_value.value
|
||||||
|
# print("Health (start)", health)
|
||||||
for priced_report in priced_reports:
|
for priced_report in priced_reports:
|
||||||
market_index = group.find_token_market_index(priced_report.base_token)
|
market_index = group.find_token_market_index(priced_report.base_token)
|
||||||
spot_market: typing.Optional[SpotMarketInfo] = group.spot_markets[market_index]
|
spot_market: typing.Optional[SpotMarketInfo] = group.spot_markets[market_index]
|
||||||
|
@ -147,8 +158,9 @@ class HealthCalculator:
|
||||||
|
|
||||||
spot_weight = spot_market.init_asset_weight if base_value > 0 else spot_market.init_liab_weight
|
spot_weight = spot_market.init_asset_weight if base_value > 0 else spot_market.init_liab_weight
|
||||||
spot_health = base_value.value * spot_weight
|
spot_health = base_value.value * spot_weight
|
||||||
|
# print("Weights", base_value.value, "*", spot_weight, spot_health)
|
||||||
|
|
||||||
perp_base, _ = self._calculate_pessimistic_perp_value(priced_report)
|
perp_base, perp_quote = priced_report.if_worst_execution()
|
||||||
perp_market: typing.Optional[PerpMarketInfo] = group.perp_markets[market_index]
|
perp_market: typing.Optional[PerpMarketInfo] = group.perp_markets[market_index]
|
||||||
perp_health: Decimal = Decimal(0)
|
perp_health: Decimal = Decimal(0)
|
||||||
if perp_market is not None:
|
if perp_market is not None:
|
||||||
|
@ -158,7 +170,12 @@ class HealthCalculator:
|
||||||
health += spot_health
|
health += spot_health
|
||||||
health += perp_health
|
health += perp_health
|
||||||
health += quote_value.value
|
health += quote_value.value
|
||||||
|
health += perp_quote.value
|
||||||
health += priced_report.raw_perp_quote_position
|
health += priced_report.raw_perp_quote_position
|
||||||
|
# print("Health (now)", health, spot_health, perp_health, quote_value.value,
|
||||||
|
# perp_quote.value, priced_report.raw_perp_quote_position)
|
||||||
|
|
||||||
|
# print("Health (returning)", health)
|
||||||
|
|
||||||
return health
|
return health
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ from solana.publickey import PublicKey
|
||||||
|
|
||||||
from .accountinfo import AccountInfo
|
from .accountinfo import AccountInfo
|
||||||
from .addressableaccount import AddressableAccount
|
from .addressableaccount import AddressableAccount
|
||||||
from .cache import Cache, PriceCache, PerpMarketCache
|
from .cache import Cache, PerpMarketCache, MarketCache
|
||||||
from .context import Context
|
from .context import Context
|
||||||
from .layouts import layouts
|
from .layouts import layouts
|
||||||
from .lotsizeconverter import LotSizeConverter, RaisingLotSizeConverter
|
from .lotsizeconverter import LotSizeConverter, RaisingLotSizeConverter
|
||||||
|
@ -275,26 +275,16 @@ class Group(AddressableAccount):
|
||||||
raise Exception(f"Could not find token info for symbol '{symbol}' in group {self.address}")
|
raise Exception(f"Could not find token info for symbol '{symbol}' in group {self.address}")
|
||||||
|
|
||||||
def token_price_from_cache(self, cache: Cache, token: Token) -> TokenValue:
|
def token_price_from_cache(self, cache: Cache, token: Token) -> TokenValue:
|
||||||
if token == self.shared_quote_token.token:
|
market_cache: MarketCache = self.market_cache_from_cache(cache, token)
|
||||||
# The price of 1 unit of the shared quote token is always 1.
|
return market_cache.adjusted_price(token, self.shared_quote_token.token)
|
||||||
return TokenValue(token, Decimal(1))
|
|
||||||
|
|
||||||
token_index: int = self.find_token_market_index(token)
|
|
||||||
cached_price: typing.Optional[PriceCache] = cache.price_cache[token_index]
|
|
||||||
if cached_price is None:
|
|
||||||
raise Exception(f"Could not find price index of basket token {token.symbol}.")
|
|
||||||
|
|
||||||
price: Decimal = cached_price.price
|
|
||||||
decimals_difference = token.decimals - self.shared_quote_token.decimals
|
|
||||||
if decimals_difference != 0:
|
|
||||||
adjustment = 10 ** decimals_difference
|
|
||||||
price = price * adjustment
|
|
||||||
|
|
||||||
return TokenValue(self.shared_quote_token.token, price)
|
|
||||||
|
|
||||||
def perp_market_cache_from_cache(self, cache: Cache, token: Token) -> typing.Optional[PerpMarketCache]:
|
def perp_market_cache_from_cache(self, cache: Cache, token: Token) -> typing.Optional[PerpMarketCache]:
|
||||||
|
market_cache: MarketCache = self.market_cache_from_cache(cache, token)
|
||||||
|
return market_cache.perp_market
|
||||||
|
|
||||||
|
def market_cache_from_cache(self, cache: Cache, token: Token) -> MarketCache:
|
||||||
token_index: int = self.find_token_market_index(token)
|
token_index: int = self.find_token_market_index(token)
|
||||||
return cache.perp_market_cache[token_index]
|
return cache.market_cache_for_index(token_index)
|
||||||
|
|
||||||
def fetch_balances(self, context: Context, root_address: PublicKey) -> typing.Sequence[TokenValue]:
|
def fetch_balances(self, context: Context, root_address: PublicKey) -> typing.Sequence[TokenValue]:
|
||||||
balances: typing.List[TokenValue] = []
|
balances: typing.List[TokenValue] = []
|
||||||
|
|
|
@ -175,8 +175,8 @@ class FloatI80F48Adapter(construct.Adapter):
|
||||||
|
|
||||||
def _decode(self, obj, context, path) -> Decimal:
|
def _decode(self, obj, context, path) -> Decimal:
|
||||||
# How many decimal places precision should we allow for an I80F48? We could:
|
# How many decimal places precision should we allow for an I80F48? We could:
|
||||||
# return round(Decimal(obj) / self.divisor, 12)
|
return round(Decimal(obj) / self.divisor, 20)
|
||||||
return Decimal(obj) / self.divisor
|
# return Decimal(obj) / self.divisor
|
||||||
|
|
||||||
def _encode(self, obj, context, path) -> bytes:
|
def _encode(self, obj, context, path) -> bytes:
|
||||||
return bytes(obj)
|
return bytes(obj)
|
||||||
|
|
|
@ -41,7 +41,7 @@ class Token:
|
||||||
def shift_to_decimals(self, value: Decimal) -> Decimal:
|
def shift_to_decimals(self, value: Decimal) -> Decimal:
|
||||||
divisor = Decimal(10 ** self.decimals)
|
divisor = Decimal(10 ** self.decimals)
|
||||||
shifted = value / divisor
|
shifted = value / divisor
|
||||||
return round(shifted, int(self.decimals))
|
return shifted
|
||||||
|
|
||||||
def shift_to_native(self, value: Decimal) -> Decimal:
|
def shift_to_native(self, value: Decimal) -> Decimal:
|
||||||
multiplier = Decimal(10 ** self.decimals)
|
multiplier = Decimal(10 ** self.decimals)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from ..fakes import fake_context
|
from ..fakes import fake_context
|
||||||
from ..data import load_data_from_directory
|
from ..data import load_cache, load_data_from_directory
|
||||||
|
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from mango.calculators.healthcalculator import HealthType, HealthCalculator
|
from mango.calculators.healthcalculator import HealthType, HealthCalculator
|
||||||
|
@ -15,15 +15,26 @@ def test_empty():
|
||||||
assert health == Decimal("0")
|
assert health == Decimal("0")
|
||||||
|
|
||||||
|
|
||||||
|
def test_1deposit():
|
||||||
|
context = fake_context()
|
||||||
|
group, cache, account, open_orders = load_data_from_directory("tests/testdata/1deposit")
|
||||||
|
|
||||||
|
actual = HealthCalculator(context, HealthType.INITIAL)
|
||||||
|
health = actual.calculate(account, open_orders, group, cache)
|
||||||
|
# Typescript says: 37904260000.05905822642118252475
|
||||||
|
assert health == Decimal("37904.2600000591928892771752953600134")
|
||||||
|
|
||||||
|
|
||||||
def test_perp_account_no_spot_openorders():
|
def test_perp_account_no_spot_openorders():
|
||||||
context = fake_context()
|
context = fake_context()
|
||||||
group, cache, account, open_orders = load_data_from_directory("tests/testdata/perp_account_no_spot_openorders")
|
group, cache, account, open_orders = load_data_from_directory("tests/testdata/perp_account_no_spot_openorders")
|
||||||
|
|
||||||
actual = HealthCalculator(context, HealthType.INITIAL)
|
actual = HealthCalculator(context, HealthType.INITIAL)
|
||||||
health = actual.calculate(account, open_orders, group, cache)
|
health = actual.calculate(account, open_orders, group, cache)
|
||||||
|
|
||||||
# Typescript says: 341025333625.51856223547208912805
|
# Typescript says: 341025333625.51856223547208912805
|
||||||
# TODO: This is significantly different from Typescript answer
|
# TODO: This is significantly different from Typescript answer
|
||||||
assert health == Decimal("358923.807024760000053942349040880810")
|
assert health == Decimal("7036880.69722811087924538653007346763")
|
||||||
|
|
||||||
|
|
||||||
def test_perp_account_no_spot_openorders_unhealthy():
|
def test_perp_account_no_spot_openorders_unhealthy():
|
||||||
|
@ -35,7 +46,7 @@ def test_perp_account_no_spot_openorders_unhealthy():
|
||||||
health = actual.calculate(account, open_orders, group, cache)
|
health = actual.calculate(account, open_orders, group, cache)
|
||||||
# Typescript says: -848086876487.04950427436299875694
|
# Typescript says: -848086876487.04950427436299875694
|
||||||
# TODO: This is significantly different from Typescript answer
|
# TODO: This is significantly different from Typescript answer
|
||||||
assert health == Decimal("-183567.651235339999859541052273925740")
|
assert health == Decimal("1100318.49506000114695611699892507857")
|
||||||
|
|
||||||
|
|
||||||
def test_account1():
|
def test_account1():
|
||||||
|
@ -46,7 +57,7 @@ def test_account1():
|
||||||
health = actual.calculate(account, open_orders, group, cache)
|
health = actual.calculate(account, open_orders, group, cache)
|
||||||
# Typescript says: 454884281.15520619643754685058
|
# Typescript says: 454884281.15520619643754685058
|
||||||
# TODO: This is slightly different from Typescript answer
|
# TODO: This is slightly different from Typescript answer
|
||||||
assert health == Decimal("455.035346700961950901638175537300417")
|
assert health == Decimal("2578453.62441460502856112835667758626")
|
||||||
|
|
||||||
|
|
||||||
def test_account2():
|
def test_account2():
|
||||||
|
@ -57,4 +68,4 @@ def test_account2():
|
||||||
health = actual.calculate(account, open_orders, group, cache)
|
health = actual.calculate(account, open_orders, group, cache)
|
||||||
# Typescript says: 7516159604.84918334545095675026
|
# Typescript says: 7516159604.84918334545095675026
|
||||||
# TODO: This is slightly different from Typescript answer
|
# TODO: This is slightly different from Typescript answer
|
||||||
assert health == Decimal("7518.40764303100646658275181266617450")
|
assert health == Decimal("-34471.8824121736505777079119365978915")
|
||||||
|
|
|
@ -13,21 +13,30 @@ def test_construction():
|
||||||
info = "some name"
|
info = "some name"
|
||||||
in_margin_basket = [False, False, False, False, False]
|
in_margin_basket = [False, False, False, False, False]
|
||||||
active_in_basket = [False, True, False, True, True]
|
active_in_basket = [False, True, False, True, True]
|
||||||
quote_deposit = fake_token_value(Decimal(50))
|
raw_quote_deposit = Decimal(50)
|
||||||
quote_borrow = fake_token_value(Decimal(5))
|
quote_deposit = fake_token_value(raw_quote_deposit)
|
||||||
quote = mango.AccountBasketToken(fake_token_info(), quote_deposit, quote_borrow)
|
raw_quote_borrow = Decimal(5)
|
||||||
deposit1 = fake_token_value(Decimal(1))
|
quote_borrow = fake_token_value(raw_quote_borrow)
|
||||||
deposit2 = fake_token_value(Decimal(2))
|
quote = mango.AccountBasketToken(fake_token_info(), raw_quote_deposit,
|
||||||
deposit3 = fake_token_value(Decimal(3))
|
quote_deposit, raw_quote_borrow, quote_borrow)
|
||||||
borrow1 = fake_token_value(Decimal("0.1"))
|
raw_deposit1 = Decimal(1)
|
||||||
borrow2 = fake_token_value(Decimal("0.2"))
|
deposit1 = fake_token_value(raw_deposit1)
|
||||||
borrow3 = fake_token_value(Decimal("0.3"))
|
raw_deposit2 = Decimal(2)
|
||||||
|
deposit2 = fake_token_value(raw_deposit2)
|
||||||
|
raw_deposit3 = Decimal(3)
|
||||||
|
deposit3 = fake_token_value(raw_deposit3)
|
||||||
|
raw_borrow1 = Decimal("0.1")
|
||||||
|
borrow1 = fake_token_value(raw_borrow1)
|
||||||
|
raw_borrow2 = Decimal("0.2")
|
||||||
|
borrow2 = fake_token_value(raw_borrow2)
|
||||||
|
raw_borrow3 = Decimal("0.3")
|
||||||
|
borrow3 = fake_token_value(raw_borrow3)
|
||||||
basket = [
|
basket = [
|
||||||
mango.AccountBasketBaseToken(fake_token_info(), fake_token_info(), deposit1, borrow1,
|
mango.AccountBasketBaseToken(fake_token_info(), fake_token_info(), raw_deposit1, deposit1, raw_borrow1, borrow1,
|
||||||
fake_seeded_public_key("spot openorders 1"), fake_seeded_public_key("perp1")),
|
fake_seeded_public_key("spot openorders 1"), fake_seeded_public_key("perp1")),
|
||||||
mango.AccountBasketBaseToken(fake_token_info(), fake_token_info(), deposit2, borrow2,
|
mango.AccountBasketBaseToken(fake_token_info(), fake_token_info(), raw_deposit2, deposit2, raw_borrow2, borrow2,
|
||||||
fake_seeded_public_key("spot openorders 2"), fake_seeded_public_key("perp2")),
|
fake_seeded_public_key("spot openorders 2"), fake_seeded_public_key("perp2")),
|
||||||
mango.AccountBasketBaseToken(fake_token_info(), fake_token_info(), deposit3, borrow3,
|
mango.AccountBasketBaseToken(fake_token_info(), fake_token_info(), raw_deposit3, deposit3, raw_borrow3, borrow3,
|
||||||
fake_seeded_public_key("spot openorders 3"), fake_seeded_public_key("perp3")),
|
fake_seeded_public_key("spot openorders 3"), fake_seeded_public_key("perp3")),
|
||||||
]
|
]
|
||||||
msrm_amount = Decimal(0)
|
msrm_amount = Decimal(0)
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
from .context import mango
|
||||||
|
from .fakes import fake_account_info, fake_seeded_public_key
|
||||||
|
from .data import load_cache
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
|
|
||||||
|
def test_cache_constructor():
|
||||||
|
account_info = fake_account_info(fake_seeded_public_key("cache"))
|
||||||
|
meta_data = mango.Metadata(mango.layouts.DATA_TYPE.parse(bytearray(b'\x07')), mango.Version.V1, True)
|
||||||
|
timestamp = datetime.now()
|
||||||
|
price_cache = [mango.PriceCache(Decimal(26), timestamp)]
|
||||||
|
root_bank_cache = [mango.RootBankCache(Decimal("0.00001"), Decimal("0.00001"), timestamp)]
|
||||||
|
perp_market_cache = [mango.PerpMarketCache(Decimal("0.00002"), Decimal("0.00002"), timestamp)]
|
||||||
|
actual = mango.Cache(account_info, mango.Version.V1, meta_data, price_cache, root_bank_cache, perp_market_cache)
|
||||||
|
|
||||||
|
assert actual is not None
|
||||||
|
assert actual.logger is not None
|
||||||
|
assert actual.account_info == account_info
|
||||||
|
assert actual.address == fake_seeded_public_key("cache")
|
||||||
|
assert actual.meta_data == meta_data
|
||||||
|
assert actual.meta_data.data_type == mango.layouts.DATA_TYPE.Cache
|
||||||
|
assert actual.price_cache == price_cache
|
||||||
|
assert actual.root_bank_cache == root_bank_cache
|
||||||
|
assert actual.perp_market_cache == perp_market_cache
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_cache():
|
||||||
|
cache = load_cache("tests/testdata/1deposit/cache.json")
|
||||||
|
|
||||||
|
#
|
||||||
|
# These values are all verified with the same file loaded in the TypeScript client.
|
||||||
|
#
|
||||||
|
|
||||||
|
assert cache.price_cache[0].price == Decimal("0.33642499999999841975")
|
||||||
|
assert cache.price_cache[1].price == Decimal("47380.32499999999999928946")
|
||||||
|
assert cache.price_cache[2].price == Decimal("3309.69549999999999911893")
|
||||||
|
assert cache.price_cache[3].price == Decimal("0.17261599999999788224")
|
||||||
|
assert cache.price_cache[4].price == Decimal("8.79379999999999739657")
|
||||||
|
assert cache.price_cache[5].price == Decimal("1")
|
||||||
|
assert cache.price_cache[6].price == Decimal("1.00039999999999906777")
|
||||||
|
assert cache.price_cache[7] is None
|
||||||
|
assert cache.price_cache[8] is None
|
||||||
|
assert cache.price_cache[9] is None
|
||||||
|
assert cache.price_cache[10] is None
|
||||||
|
assert cache.price_cache[11] is None
|
||||||
|
assert cache.price_cache[12] is None
|
||||||
|
assert cache.price_cache[13] is None
|
||||||
|
assert cache.price_cache[14] is None
|
||||||
|
|
||||||
|
assert cache.root_bank_cache[0].deposit_index == Decimal("1001923.86460821722014813417")
|
||||||
|
assert cache.root_bank_cache[0].borrow_index == Decimal("1002515.45257855337824182129")
|
||||||
|
assert cache.root_bank_cache[1].deposit_index == Decimal("1000007.37249653914441083202")
|
||||||
|
assert cache.root_bank_cache[1].borrow_index == Decimal("1000166.98522159213999316307")
|
||||||
|
assert cache.root_bank_cache[2].deposit_index == Decimal("1000000.19554886875829424753")
|
||||||
|
assert cache.root_bank_cache[2].borrow_index == Decimal("1000001.13273253565107623331")
|
||||||
|
assert cache.root_bank_cache[3].deposit_index == Decimal("1000037.82149923799070379005")
|
||||||
|
assert cache.root_bank_cache[3].borrow_index == Decimal("1000044.28925241010965052624")
|
||||||
|
assert cache.root_bank_cache[4].deposit_index == Decimal("1000000.0000132182767842437")
|
||||||
|
assert cache.root_bank_cache[4].borrow_index == Decimal("1000000.14235973938041368569")
|
||||||
|
assert cache.root_bank_cache[5].deposit_index == Decimal("1000000.35244386506945346582")
|
||||||
|
assert cache.root_bank_cache[5].borrow_index == Decimal("1000000.66156146420993522383")
|
||||||
|
assert cache.root_bank_cache[6].deposit_index == Decimal("1000473.25161608998580575758")
|
||||||
|
assert cache.root_bank_cache[6].borrow_index == Decimal("1000524.37279217702128875089")
|
||||||
|
assert cache.root_bank_cache[7] is None
|
||||||
|
assert cache.root_bank_cache[7] is None
|
||||||
|
assert cache.root_bank_cache[8] is None
|
||||||
|
assert cache.root_bank_cache[8] is None
|
||||||
|
assert cache.root_bank_cache[9] is None
|
||||||
|
assert cache.root_bank_cache[9] is None
|
||||||
|
assert cache.root_bank_cache[10] is None
|
||||||
|
assert cache.root_bank_cache[10] is None
|
||||||
|
assert cache.root_bank_cache[11] is None
|
||||||
|
assert cache.root_bank_cache[11] is None
|
||||||
|
assert cache.root_bank_cache[12] is None
|
||||||
|
assert cache.root_bank_cache[12] is None
|
||||||
|
assert cache.root_bank_cache[13] is None
|
||||||
|
assert cache.root_bank_cache[13] is None
|
||||||
|
assert cache.root_bank_cache[14] is None
|
||||||
|
assert cache.root_bank_cache[14] is None
|
||||||
|
assert cache.root_bank_cache[15].deposit_index == Decimal("1000154.42276607534055088422")
|
||||||
|
assert cache.root_bank_cache[15].borrow_index == Decimal("1000219.00868743509063563124")
|
||||||
|
|
||||||
|
assert cache.perp_market_cache[0] is None
|
||||||
|
assert cache.perp_market_cache[1].long_funding == Decimal("-751864.70031280454435673732")
|
||||||
|
assert cache.perp_market_cache[1].short_funding == Decimal("-752275.3557979761382519257")
|
||||||
|
assert cache.perp_market_cache[2].long_funding == Decimal("0")
|
||||||
|
assert cache.perp_market_cache[2].short_funding == Decimal("0")
|
||||||
|
assert cache.perp_market_cache[3].long_funding == Decimal("-636425.51790158202868497028")
|
||||||
|
assert cache.perp_market_cache[3].short_funding == Decimal("-636425.51790158202868497028")
|
||||||
|
assert cache.perp_market_cache[4] is None
|
||||||
|
assert cache.perp_market_cache[5] is None
|
||||||
|
assert cache.perp_market_cache[6] is None
|
||||||
|
assert cache.perp_market_cache[7] is None
|
||||||
|
assert cache.perp_market_cache[8] is None
|
||||||
|
assert cache.perp_market_cache[9] is None
|
||||||
|
assert cache.perp_market_cache[10] is None
|
||||||
|
assert cache.perp_market_cache[11] is None
|
||||||
|
assert cache.perp_market_cache[12] is None
|
||||||
|
assert cache.perp_market_cache[13] is None
|
||||||
|
assert cache.perp_market_cache[14] is None
|
|
@ -54,8 +54,8 @@ def test_root_bank_constructor():
|
||||||
assert actual.last_updated == timestamp
|
assert actual.last_updated == timestamp
|
||||||
|
|
||||||
|
|
||||||
def test_root_bank_loaded():
|
def test_load_root_bank():
|
||||||
actual = load_root_bank("tests/testdata/empty/root_bank0.json")
|
actual = load_root_bank("tests/testdata/1deposit/root_bank0.json")
|
||||||
|
|
||||||
assert actual is not None
|
assert actual is not None
|
||||||
assert actual.logger is not None
|
assert actual.logger is not None
|
||||||
|
@ -63,16 +63,10 @@ def test_root_bank_loaded():
|
||||||
assert actual.meta_data.version == mango.Version.V1
|
assert actual.meta_data.version == mango.Version.V1
|
||||||
assert actual.meta_data.data_type == mango.layouts.DATA_TYPE.RootBank
|
assert actual.meta_data.data_type == mango.layouts.DATA_TYPE.RootBank
|
||||||
assert actual.meta_data.is_initialized
|
assert actual.meta_data.is_initialized
|
||||||
# Typescript says: 0.69999999999999928946
|
assert actual.optimal_util == Decimal("0.69999999999999928946")
|
||||||
assert actual.optimal_util == Decimal("0.699999999999999289457264239899814129")
|
assert actual.optimal_rate == Decimal("0.05999999999999872102")
|
||||||
# Typescript says: 0.05999999999999872102
|
|
||||||
assert actual.optimal_rate == Decimal("0.0599999999999987210230756318196654320")
|
|
||||||
# Typescript says: 1.5
|
|
||||||
assert actual.max_rate == Decimal("1.5")
|
assert actual.max_rate == Decimal("1.5")
|
||||||
assert actual.node_banks[0] == PublicKey("J2Lmnc1e4frMnBEJARPoHtfpcohLfN67HdK1inXjTFSM")
|
assert actual.node_banks[0] == PublicKey("J2Lmnc1e4frMnBEJARPoHtfpcohLfN67HdK1inXjTFSM")
|
||||||
# Typescript says: 1000154.42276607355830719825
|
assert actual.deposit_index == Decimal("1000154.42276607355830719825")
|
||||||
assert actual.deposit_index == Decimal("1000154.42276607355830719825462438166")
|
assert actual.borrow_index == Decimal("1000219.00867863010088498754")
|
||||||
# Typescript says: 1000219.00867863010088498754
|
|
||||||
assert actual.borrow_index == Decimal("1000219.00867863010088498754157626536")
|
|
||||||
# Typescript says: "Mon, 04 Oct 2021 14:58:05 GMT"
|
|
||||||
assert actual.last_updated == datetime(2021, 10, 4, 14, 58, 5, 0, timezone.utc)
|
assert actual.last_updated == datetime(2021, 10, 4, 14, 58, 5, 0, timezone.utc)
|
||||||
|
|
Loading…
Reference in New Issue