Some fixes for health calculations WIP.

This commit is contained in:
Geoff Taylor 2021-11-04 15:36:14 +00:00
parent 95e2496ad3
commit 4a24f3df55
13 changed files with 362 additions and 110 deletions

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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:

View File

@ -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 = []

View File

@ -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

View File

@ -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] = []

View File

@ -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)

View File

@ -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)

View File

@ -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")

View File

@ -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)

102
tests/test_cache.py Normal file
View File

@ -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

View File

@ -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)