2021-06-25 02:33:40 -07:00
|
|
|
|
# # ⚠ Warning
|
|
|
|
|
#
|
|
|
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
|
|
|
|
# LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
|
|
|
|
# NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
|
|
|
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
|
|
|
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
|
#
|
|
|
|
|
# [🥭 Mango Markets](https://mango.markets/) support is available at:
|
|
|
|
|
# [Docs](https://docs.mango.markets/)
|
|
|
|
|
# [Discord](https://discord.gg/67jySBhxrg)
|
|
|
|
|
# [Twitter](https://twitter.com/mangomarkets)
|
|
|
|
|
# [Github](https://github.com/blockworks-foundation)
|
|
|
|
|
# [Email](mailto:hello@blockworks.foundation)
|
|
|
|
|
|
|
|
|
|
import typing
|
|
|
|
|
|
|
|
|
|
from decimal import Decimal
|
|
|
|
|
from solana.publickey import PublicKey
|
|
|
|
|
from solana.rpc.types import MemcmpOpts
|
|
|
|
|
|
|
|
|
|
from .accountinfo import AccountInfo
|
|
|
|
|
from .addressableaccount import AddressableAccount
|
|
|
|
|
from .context import Context
|
|
|
|
|
from .encoding import encode_key
|
2021-06-25 07:50:37 -07:00
|
|
|
|
from .group import Group
|
2021-06-25 02:33:40 -07:00
|
|
|
|
from .layouts import layouts
|
|
|
|
|
from .metadata import Metadata
|
2021-10-04 10:27:07 -07:00
|
|
|
|
from .openorders import OpenOrders
|
2021-08-17 11:42:11 -07:00
|
|
|
|
from .orders import Side
|
2021-07-29 08:19:52 -07:00
|
|
|
|
from .perpaccount import PerpAccount
|
2021-08-30 16:30:22 -07:00
|
|
|
|
from .perpopenorders import PerpOpenOrders
|
|
|
|
|
from .placedorder import PlacedOrder
|
2021-08-07 10:42:39 -07:00
|
|
|
|
from .token import Token
|
2021-08-30 16:30:22 -07:00
|
|
|
|
from .tokeninfo import TokenInfo
|
2021-07-01 13:24:08 -07:00
|
|
|
|
from .tokenvalue import TokenValue
|
2021-06-25 02:33:40 -07:00
|
|
|
|
from .version import Version
|
|
|
|
|
|
2021-08-03 04:40:00 -07:00
|
|
|
|
|
2021-08-03 10:32:12 -07:00
|
|
|
|
# # 🥭 AccountBasketToken class
|
|
|
|
|
#
|
|
|
|
|
# `AccountBasketToken` gathers basket items together instead of separate arrays.
|
|
|
|
|
#
|
2021-08-03 04:40:00 -07:00
|
|
|
|
class AccountBasketToken:
|
2021-08-04 05:08:34 -07:00
|
|
|
|
def __init__(self, token_info: TokenInfo, deposit: TokenValue, borrow: TokenValue):
|
2021-08-03 04:40:00 -07:00
|
|
|
|
self.token_info: TokenInfo = token_info
|
|
|
|
|
self.deposit: TokenValue = deposit
|
|
|
|
|
self.borrow: TokenValue = borrow
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def net_value(self) -> TokenValue:
|
2021-08-04 05:08:34 -07:00
|
|
|
|
return self.deposit - self.borrow
|
|
|
|
|
|
|
|
|
|
def __str__(self) -> str:
|
|
|
|
|
return f"""« 𝙰𝚌𝚌𝚘𝚞𝚗𝚝𝙱𝚊𝚜𝚔𝚎𝚝𝚃𝚘𝚔𝚎𝚗 {self.token_info.token.symbol}
|
|
|
|
|
Net Value: {self.net_value}
|
|
|
|
|
Deposited: {self.deposit}
|
|
|
|
|
Borrowed: {self.borrow}
|
|
|
|
|
»"""
|
|
|
|
|
|
|
|
|
|
def __repr__(self) -> str:
|
|
|
|
|
return f"{self}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# # 🥭 AccountBasketBaseToken class
|
|
|
|
|
#
|
|
|
|
|
# `AccountBasketBaseToken` is a more specialised `AccountBasketToken` for all the base tokens in the
|
|
|
|
|
# account.
|
|
|
|
|
#
|
|
|
|
|
class AccountBasketBaseToken(AccountBasketToken):
|
2021-10-04 10:27:07 -07:00
|
|
|
|
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]):
|
2021-08-04 05:08:34 -07:00
|
|
|
|
super().__init__(token_info, deposit, borrow)
|
2021-10-04 10:27:07 -07:00
|
|
|
|
self.quote_token_info: TokenInfo = quote_token_info
|
2021-08-04 05:08:34 -07:00
|
|
|
|
self.spot_open_orders: typing.Optional[PublicKey] = spot_open_orders
|
2021-10-04 10:27:07 -07:00
|
|
|
|
self.perp_account: typing.Optional[PerpAccount] = perp_account
|
2021-08-03 04:40:00 -07:00
|
|
|
|
|
|
|
|
|
def __str__(self) -> str:
|
|
|
|
|
perp_account: str = "None"
|
|
|
|
|
if self.perp_account is not None:
|
|
|
|
|
perp_account = f"{self.perp_account}".replace("\n", "\n ")
|
2021-08-04 05:08:34 -07:00
|
|
|
|
return f"""« 𝙰𝚌𝚌𝚘𝚞𝚗𝚝𝙱𝚊𝚜𝚔𝚎𝚝𝙱𝚊𝚜𝚎𝚃𝚘𝚔𝚎𝚗 {self.token_info.token.symbol}
|
2021-08-03 04:40:00 -07:00
|
|
|
|
Net Value: {self.net_value}
|
|
|
|
|
Deposited: {self.deposit}
|
|
|
|
|
Borrowed: {self.borrow}
|
|
|
|
|
Spot OpenOrders: {self.spot_open_orders or "None"}
|
|
|
|
|
Perp Account:
|
|
|
|
|
{perp_account}
|
|
|
|
|
»"""
|
|
|
|
|
|
|
|
|
|
def __repr__(self) -> str:
|
|
|
|
|
return f"{self}"
|
|
|
|
|
|
2021-08-03 10:32:12 -07:00
|
|
|
|
|
2021-08-04 05:08:34 -07:00
|
|
|
|
TMappedAccountBasketValue = typing.TypeVar("TMappedAccountBasketValue")
|
|
|
|
|
|
|
|
|
|
|
2021-06-25 07:50:37 -07:00
|
|
|
|
# # 🥭 Account class
|
2021-06-25 02:33:40 -07:00
|
|
|
|
#
|
2021-06-25 07:50:37 -07:00
|
|
|
|
# `Account` holds information about the account for a particular user/wallet for a particualr `Group`.
|
2021-06-25 02:33:40 -07:00
|
|
|
|
#
|
2021-06-25 07:50:37 -07:00
|
|
|
|
class Account(AddressableAccount):
|
2021-06-25 02:33:40 -07:00
|
|
|
|
def __init__(self, account_info: AccountInfo, version: Version,
|
2021-10-04 10:27:07 -07:00
|
|
|
|
meta_data: Metadata, group_name: str, group_address: PublicKey, owner: PublicKey,
|
|
|
|
|
info: str, shared_quote_token: AccountBasketToken,
|
2021-08-04 09:50:38 -07:00
|
|
|
|
in_margin_basket: typing.Sequence[bool],
|
|
|
|
|
basket_indices: typing.Sequence[bool],
|
|
|
|
|
basket: typing.Sequence[AccountBasketBaseToken],
|
2021-08-04 05:08:34 -07:00
|
|
|
|
msrm_amount: Decimal, being_liquidated: bool, is_bankrupt: bool):
|
2021-06-25 02:33:40 -07:00
|
|
|
|
super().__init__(account_info)
|
|
|
|
|
self.version: Version = version
|
|
|
|
|
|
|
|
|
|
self.meta_data: Metadata = meta_data
|
2021-10-04 10:27:07 -07:00
|
|
|
|
self.group_name: str = group_name
|
|
|
|
|
self.group_address: PublicKey = group_address
|
2021-06-25 02:33:40 -07:00
|
|
|
|
self.owner: PublicKey = owner
|
2021-08-13 04:15:17 -07:00
|
|
|
|
self.info: str = info
|
2021-08-04 05:08:34 -07:00
|
|
|
|
self.shared_quote_token: AccountBasketToken = shared_quote_token
|
2021-08-04 09:50:38 -07:00
|
|
|
|
self.in_margin_basket: typing.Sequence[bool] = in_margin_basket
|
2021-08-04 05:08:34 -07:00
|
|
|
|
self.basket_indices: typing.Sequence[bool] = basket_indices
|
|
|
|
|
self.basket: typing.Sequence[AccountBasketBaseToken] = basket
|
2021-07-13 04:43:50 -07:00
|
|
|
|
self.msrm_amount: Decimal = msrm_amount
|
|
|
|
|
self.being_liquidated: bool = being_liquidated
|
2021-07-01 13:24:08 -07:00
|
|
|
|
self.is_bankrupt: bool = is_bankrupt
|
2021-06-25 02:33:40 -07:00
|
|
|
|
|
|
|
|
|
@staticmethod
|
2021-08-27 12:37:23 -07:00
|
|
|
|
def from_layout(layout: typing.Any, account_info: AccountInfo, version: Version, group: Group) -> "Account":
|
2021-06-25 02:33:40 -07:00
|
|
|
|
meta_data = Metadata.from_layout(layout.meta_data)
|
|
|
|
|
owner: PublicKey = layout.owner
|
2021-08-13 04:15:17 -07:00
|
|
|
|
info: str = layout.info
|
2021-08-05 07:35:08 -07:00
|
|
|
|
mngo_token_info = TokenInfo.find_by_symbol(group.tokens, "MNGO")
|
2021-08-04 05:08:34 -07:00
|
|
|
|
in_margin_basket: typing.Sequence[bool] = list([bool(in_basket) for in_basket in layout.in_margin_basket])
|
2021-08-04 09:50:38 -07:00
|
|
|
|
active_in_basket: typing.List[bool] = []
|
2021-08-04 05:08:34 -07:00
|
|
|
|
basket: typing.List[AccountBasketBaseToken] = []
|
2021-08-17 11:42:11 -07:00
|
|
|
|
placed_orders_all_markets: typing.List[typing.List[PlacedOrder]] = [[] for _ in range(len(group.tokens) - 1)]
|
|
|
|
|
for index, order_market in enumerate(layout.order_market):
|
|
|
|
|
if order_market != 0xFF:
|
|
|
|
|
side = Side.from_value(layout.order_side[index])
|
|
|
|
|
id = layout.order_ids[index]
|
|
|
|
|
client_id = layout.client_order_ids[index]
|
|
|
|
|
placed_order = PlacedOrder(id, client_id, side)
|
|
|
|
|
placed_orders_all_markets[int(order_market)] += [placed_order]
|
|
|
|
|
|
2021-10-02 04:28:16 -07:00
|
|
|
|
quote_token_info: typing.Optional[TokenInfo] = group.tokens[-1]
|
|
|
|
|
if quote_token_info is None:
|
|
|
|
|
raise Exception(f"Could not determine quote token in group {group.address}")
|
|
|
|
|
|
2021-08-04 05:08:34 -07:00
|
|
|
|
for index, token_info in enumerate(group.tokens[:-1]):
|
2021-08-04 09:50:38 -07:00
|
|
|
|
if token_info:
|
2021-07-01 13:24:08 -07:00
|
|
|
|
intrinsic_deposit = token_info.root_bank.deposit_index * layout.deposits[index]
|
2021-08-03 04:40:00 -07:00
|
|
|
|
deposit = TokenValue(token_info.token, token_info.token.shift_to_decimals(intrinsic_deposit))
|
2021-07-01 13:24:08 -07:00
|
|
|
|
intrinsic_borrow = token_info.root_bank.borrow_index * layout.borrows[index]
|
2021-08-03 04:40:00 -07:00
|
|
|
|
borrow = TokenValue(token_info.token, token_info.token.shift_to_decimals(intrinsic_borrow))
|
2021-08-17 11:42:11 -07:00
|
|
|
|
perp_open_orders = PerpOpenOrders(placed_orders_all_markets[index])
|
2021-10-02 04:28:16 -07:00
|
|
|
|
group_basket_market = group.markets[index]
|
|
|
|
|
if group_basket_market is None:
|
|
|
|
|
raise Exception(f"Could not find group basket market at index {index}.")
|
2021-08-17 11:42:11 -07:00
|
|
|
|
perp_account = PerpAccount.from_layout(
|
2021-10-02 04:28:16 -07:00
|
|
|
|
layout.perp_accounts[index],
|
|
|
|
|
token_info.token,
|
|
|
|
|
quote_token_info.token,
|
|
|
|
|
perp_open_orders,
|
|
|
|
|
group_basket_market.perp_lot_size_converter,
|
|
|
|
|
mngo_token_info.token)
|
2021-08-03 04:40:00 -07:00
|
|
|
|
spot_open_orders = layout.spot_open_orders[index]
|
2021-08-04 05:08:34 -07:00
|
|
|
|
basket_item: AccountBasketBaseToken = AccountBasketBaseToken(
|
2021-10-04 10:27:07 -07:00
|
|
|
|
token_info, quote_token_info, deposit, borrow, spot_open_orders, perp_account)
|
2021-08-03 04:40:00 -07:00
|
|
|
|
basket += [basket_item]
|
2021-08-04 09:50:38 -07:00
|
|
|
|
active_in_basket += [True]
|
|
|
|
|
else:
|
|
|
|
|
active_in_basket += [False]
|
2021-07-01 13:24:08 -07:00
|
|
|
|
|
2021-08-04 05:08:34 -07:00
|
|
|
|
intrinsic_quote_deposit = quote_token_info.root_bank.deposit_index * layout.deposits[-1]
|
|
|
|
|
quote_deposit = TokenValue(quote_token_info.token,
|
|
|
|
|
quote_token_info.token.shift_to_decimals(intrinsic_quote_deposit))
|
|
|
|
|
intrinsic_quote_borrow = quote_token_info.root_bank.borrow_index * layout.borrows[-1]
|
|
|
|
|
quote_borrow = TokenValue(quote_token_info.token,
|
|
|
|
|
quote_token_info.token.shift_to_decimals(intrinsic_quote_borrow))
|
|
|
|
|
quote: AccountBasketToken = AccountBasketToken(quote_token_info, quote_deposit, quote_borrow)
|
|
|
|
|
|
2021-07-13 04:43:50 -07:00
|
|
|
|
msrm_amount: Decimal = layout.msrm_amount
|
2021-07-29 08:19:52 -07:00
|
|
|
|
being_liquidated: bool = bool(layout.being_liquidated)
|
|
|
|
|
is_bankrupt: bool = bool(layout.is_bankrupt)
|
2021-06-25 02:33:40 -07:00
|
|
|
|
|
2021-10-04 10:27:07 -07:00
|
|
|
|
return Account(account_info, version, meta_data, group.name, group.address, owner, info, quote, in_margin_basket, active_in_basket, basket, msrm_amount, being_liquidated, is_bankrupt)
|
2021-06-25 02:33:40 -07:00
|
|
|
|
|
|
|
|
|
@staticmethod
|
2021-08-04 05:08:34 -07:00
|
|
|
|
def parse(account_info: AccountInfo, group: Group) -> "Account":
|
2021-06-25 02:33:40 -07:00
|
|
|
|
data = account_info.data
|
|
|
|
|
if len(data) != layouts.MANGO_ACCOUNT.sizeof():
|
|
|
|
|
raise Exception(
|
2021-07-12 02:57:05 -07:00
|
|
|
|
f"Account data length ({len(data)}) does not match expected size ({layouts.MANGO_ACCOUNT.sizeof()})")
|
2021-06-25 02:33:40 -07:00
|
|
|
|
|
|
|
|
|
layout = layouts.MANGO_ACCOUNT.parse(data)
|
2021-07-29 08:19:52 -07:00
|
|
|
|
return Account.from_layout(layout, account_info, Version.V3, group)
|
2021-06-25 02:33:40 -07:00
|
|
|
|
|
|
|
|
|
@staticmethod
|
2021-07-01 13:24:08 -07:00
|
|
|
|
def load(context: Context, address: PublicKey, group: Group) -> "Account":
|
2021-06-25 02:33:40 -07:00
|
|
|
|
account_info = AccountInfo.load(context, address)
|
|
|
|
|
if account_info is None:
|
2021-06-25 07:50:37 -07:00
|
|
|
|
raise Exception(f"Account account not found at address '{address}'")
|
2021-08-04 05:08:34 -07:00
|
|
|
|
return Account.parse(account_info, group)
|
2021-06-25 02:33:40 -07:00
|
|
|
|
|
|
|
|
|
@staticmethod
|
2021-06-25 07:50:37 -07:00
|
|
|
|
def load_all_for_owner(context: Context, owner: PublicKey, group: Group) -> typing.Sequence["Account"]:
|
2021-06-25 02:33:40 -07:00
|
|
|
|
# mango_group is just after the METADATA, which is the first entry.
|
|
|
|
|
group_offset = layouts.METADATA.sizeof()
|
|
|
|
|
# owner is just after mango_group in the layout, and it's a PublicKey which is 32 bytes.
|
|
|
|
|
owner_offset = group_offset + 32
|
|
|
|
|
filters = [
|
|
|
|
|
MemcmpOpts(
|
|
|
|
|
offset=group_offset,
|
|
|
|
|
bytes=encode_key(group.address)
|
|
|
|
|
),
|
|
|
|
|
MemcmpOpts(
|
|
|
|
|
offset=owner_offset,
|
|
|
|
|
bytes=encode_key(owner)
|
|
|
|
|
)
|
|
|
|
|
]
|
|
|
|
|
|
2021-08-26 02:31:02 -07:00
|
|
|
|
results = context.client.get_program_accounts(context.mango_program_address, memcmp_opts=filters)
|
2021-06-25 02:33:40 -07:00
|
|
|
|
accounts = []
|
2021-08-07 07:07:19 -07:00
|
|
|
|
for account_data in results:
|
2021-06-25 02:33:40 -07:00
|
|
|
|
address = PublicKey(account_data["pubkey"])
|
|
|
|
|
account_info = AccountInfo._from_response_values(account_data["account"], address)
|
2021-08-04 05:08:34 -07:00
|
|
|
|
account = Account.parse(account_info, group)
|
2021-06-25 02:33:40 -07:00
|
|
|
|
accounts += [account]
|
|
|
|
|
return accounts
|
|
|
|
|
|
2021-07-23 03:42:22 -07:00
|
|
|
|
@staticmethod
|
2021-09-02 09:19:17 -07:00
|
|
|
|
def load_for_owner_by_address(context: Context, owner: PublicKey, group: Group, account_address: typing.Optional[PublicKey]) -> "Account":
|
|
|
|
|
if account_address is not None:
|
|
|
|
|
return Account.load(context, account_address, group)
|
|
|
|
|
|
2021-07-31 06:43:39 -07:00
|
|
|
|
accounts: typing.Sequence[Account] = Account.load_all_for_owner(context, owner, group)
|
2021-09-02 09:19:17 -07:00
|
|
|
|
if len(accounts) > 1:
|
|
|
|
|
raise Exception(f"More than 1 Mango account for owner '{owner}' and which to choose not specified.")
|
|
|
|
|
|
|
|
|
|
return accounts[0]
|
2021-07-23 03:42:22 -07:00
|
|
|
|
|
2021-08-04 05:08:34 -07:00
|
|
|
|
@staticmethod
|
|
|
|
|
def _map_sequence_to_basket_indices(items: typing.Sequence[AccountBasketBaseToken], in_basket: typing.Sequence[bool], selector: typing.Callable[[typing.Any], TMappedAccountBasketValue]) -> typing.Sequence[typing.Optional[TMappedAccountBasketValue]]:
|
|
|
|
|
mapped_items: typing.List[typing.Optional[TMappedAccountBasketValue]] = []
|
|
|
|
|
basket_counter = 0
|
|
|
|
|
for available in in_basket:
|
|
|
|
|
if available:
|
|
|
|
|
mapped_items += [selector(items[basket_counter])]
|
|
|
|
|
basket_counter += 1
|
|
|
|
|
else:
|
|
|
|
|
mapped_items += [None]
|
|
|
|
|
|
|
|
|
|
return mapped_items
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def basket_tokens(self) -> typing.Sequence[typing.Optional[AccountBasketToken]]:
|
|
|
|
|
return [
|
|
|
|
|
*Account._map_sequence_to_basket_indices(self.basket, self.basket_indices, lambda item: item),
|
|
|
|
|
self.shared_quote_token
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def deposits(self) -> typing.Sequence[typing.Optional[TokenValue]]:
|
|
|
|
|
return list(map(lambda basket_token: basket_token.deposit if basket_token else None, self.basket_tokens))
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def borrows(self) -> typing.Sequence[typing.Optional[TokenValue]]:
|
|
|
|
|
return list(map(lambda basket_token: basket_token.borrow if basket_token else None, self.basket_tokens))
|
|
|
|
|
|
2021-08-03 04:40:00 -07:00
|
|
|
|
@property
|
2021-08-04 05:08:34 -07:00
|
|
|
|
def net_assets(self) -> typing.Sequence[typing.Optional[TokenValue]]:
|
|
|
|
|
return list(map(lambda basket_token: basket_token.net_value if basket_token else None, self.basket_tokens))
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def spot_open_orders(self) -> typing.Sequence[typing.Optional[PublicKey]]:
|
|
|
|
|
return Account._map_sequence_to_basket_indices(self.basket, self.basket_indices, lambda item: item.spot_open_orders)
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def perp_accounts(self) -> typing.Sequence[typing.Optional[PerpAccount]]:
|
|
|
|
|
return Account._map_sequence_to_basket_indices(self.basket, self.basket_indices, lambda item: item.perp_account)
|
|
|
|
|
|
2021-09-02 10:51:43 -07:00
|
|
|
|
def find_basket_token(self, token: Token) -> AccountBasketBaseToken:
|
|
|
|
|
for bt in self.basket:
|
|
|
|
|
if bt.token_info.token == token:
|
|
|
|
|
return bt
|
|
|
|
|
|
|
|
|
|
raise Exception(f"Could not find token {token} in basket in group {self.address}")
|
|
|
|
|
|
2021-10-04 10:27:07 -07:00
|
|
|
|
def load_all_spot_open_orders(self, context: Context) -> typing.Dict[str, OpenOrders]:
|
|
|
|
|
spot_open_orders_addresses = list(
|
|
|
|
|
[basket_token.spot_open_orders for basket_token in self.basket if basket_token.spot_open_orders is not None])
|
|
|
|
|
spot_open_orders_account_infos = AccountInfo.load_multiple(context, spot_open_orders_addresses)
|
|
|
|
|
spot_open_orders_account_infos_by_address = {
|
|
|
|
|
str(account_info.address): account_info for account_info in spot_open_orders_account_infos}
|
|
|
|
|
spot_open_orders: typing.Dict[str, OpenOrders] = {}
|
|
|
|
|
for basket_token in self.basket:
|
|
|
|
|
if basket_token.spot_open_orders is not None:
|
|
|
|
|
account_info = spot_open_orders_account_infos_by_address[str(basket_token.spot_open_orders)]
|
|
|
|
|
oo = OpenOrders.parse(account_info, basket_token.token_info.token.decimals,
|
|
|
|
|
self.shared_quote_token.token_info.decimals)
|
|
|
|
|
spot_open_orders[str(basket_token.spot_open_orders)] = oo
|
|
|
|
|
return spot_open_orders
|
|
|
|
|
|
2021-08-04 05:08:34 -07:00
|
|
|
|
def update_spot_open_orders_for_market(self, spot_market_index: int, spot_open_orders: PublicKey) -> None:
|
|
|
|
|
indexable_basket: typing.Sequence[typing.Optional[AccountBasketBaseToken]] = Account._map_sequence_to_basket_indices(
|
|
|
|
|
self.basket, self.basket_indices, lambda item: item)
|
|
|
|
|
item_to_update = indexable_basket[spot_market_index]
|
|
|
|
|
if item_to_update is None:
|
|
|
|
|
raise Exception(f"Could not find AccountBasketItem in Account {self.address} at index {spot_market_index}.")
|
|
|
|
|
item_to_update.spot_open_orders = spot_open_orders
|
2021-08-03 04:40:00 -07:00
|
|
|
|
|
2021-08-01 10:03:46 -07:00
|
|
|
|
def __str__(self) -> str:
|
2021-08-13 04:15:17 -07:00
|
|
|
|
info = f"'{self.info}'" if self.info else "(𝑢𝑛-𝑛𝑎𝑚𝑒𝑑)"
|
2021-08-07 10:42:39 -07:00
|
|
|
|
shared_quote_token: str = f"{self.shared_quote_token}".replace("\n", "\n ")
|
2021-08-03 04:40:00 -07:00
|
|
|
|
basket_count = len(self.basket)
|
|
|
|
|
basket = "\n ".join([f"{item}".replace("\n", "\n ") for item in self.basket])
|
|
|
|
|
|
2021-08-07 10:42:39 -07:00
|
|
|
|
tokens_in_basket: typing.Sequence[typing.Optional[Token]] = Account._map_sequence_to_basket_indices(
|
2021-08-04 05:08:34 -07:00
|
|
|
|
self.basket, self.basket_indices, lambda item: item.token_info.token)
|
|
|
|
|
symbols_in_basket = list([tok.symbol for tok in tokens_in_basket if tok is not None])
|
2021-08-01 10:03:46 -07:00
|
|
|
|
in_margin_basket = ", ".join(symbols_in_basket) or "None"
|
2021-08-13 04:15:17 -07:00
|
|
|
|
return f"""« 𝙰𝚌𝚌𝚘𝚞𝚗𝚝 {info}, {self.version} [{self.address}]
|
2021-06-25 02:33:40 -07:00
|
|
|
|
{self.meta_data}
|
|
|
|
|
Owner: {self.owner}
|
2021-10-04 10:27:07 -07:00
|
|
|
|
Group: « 𝙶𝚛𝚘𝚞𝚙 '{self.group_name}' [{self.group_address}] »
|
2021-08-03 04:40:00 -07:00
|
|
|
|
MSRM: {self.msrm_amount}
|
2021-07-29 08:19:52 -07:00
|
|
|
|
Bankrupt? {self.is_bankrupt}
|
|
|
|
|
Being Liquidated? {self.being_liquidated}
|
2021-08-07 10:42:39 -07:00
|
|
|
|
Shared Quote Token:
|
|
|
|
|
{shared_quote_token}
|
2021-08-03 04:40:00 -07:00
|
|
|
|
In Basket: {in_margin_basket}
|
|
|
|
|
Basket [{basket_count} in basket]:
|
|
|
|
|
{basket}
|
2021-06-25 02:33:40 -07:00
|
|
|
|
»"""
|
2021-07-12 02:57:05 -07:00
|
|
|
|
|
|
|
|
|
def __repr__(self) -> str:
|
|
|
|
|
return f"{self}"
|