Improved Group and Account output. Tied OpenOrders and PerpOpenOrders together with a PlacedOrdersContainer protocol.
This commit is contained in:
parent
5b6d5f2291
commit
e721d6c8e8
|
@ -82,7 +82,7 @@ def build_latest_account_observer(context: mango.Context, account: mango.Account
|
|||
return account_subscription, latest_account_observer
|
||||
|
||||
|
||||
def build_latest_spot_open_orders_observer(manager: mango.WebSocketSubscriptionManager, disposer: mango.DisposePropagator, spot_market: mango.SpotMarket) -> mango.LatestItemObserverSubscriber[mango.OpenOrders]:
|
||||
def build_latest_spot_open_orders_observer(manager: mango.WebSocketSubscriptionManager, disposer: mango.DisposePropagator, spot_market: mango.SpotMarket) -> mango.LatestItemObserverSubscriber[mango.PlacedOrdersContainer]:
|
||||
market_index = group.find_spot_market_index(spot_market.address)
|
||||
spot_open_orders_address = account.spot_open_orders[market_index]
|
||||
if spot_open_orders_address is None:
|
||||
|
@ -93,13 +93,14 @@ def build_latest_spot_open_orders_observer(manager: mango.WebSocketSubscriptionM
|
|||
manager.add(spot_open_orders_subscription)
|
||||
initial_spot_open_orders = mango.OpenOrders.load(
|
||||
context, spot_open_orders_address, spot_market.base.decimals, spot_market.quote.decimals)
|
||||
latest_open_orders_observer = mango.LatestItemObserverSubscriber(initial_spot_open_orders)
|
||||
latest_open_orders_observer = mango.LatestItemObserverSubscriber[mango.PlacedOrdersContainer](
|
||||
initial_spot_open_orders)
|
||||
spot_open_orders_subscription.publisher.subscribe(latest_open_orders_observer)
|
||||
add_file_health("open_orders_subscription", spot_open_orders_subscription.publisher, disposer)
|
||||
return latest_open_orders_observer
|
||||
|
||||
|
||||
def build_latest_serum_open_orders_observer(manager: mango.WebSocketSubscriptionManager, disposer: mango.DisposePropagator, serum_market: mango.SerumMarket, context: mango.Context, wallet: mango.Wallet) -> mango.LatestItemObserverSubscriber[mango.OpenOrders]:
|
||||
def build_latest_serum_open_orders_observer(manager: mango.WebSocketSubscriptionManager, disposer: mango.DisposePropagator, serum_market: mango.SerumMarket, context: mango.Context, wallet: mango.Wallet) -> mango.LatestItemObserverSubscriber[mango.PlacedOrdersContainer]:
|
||||
all_open_orders = mango.OpenOrders.load_for_market_and_owner(
|
||||
context, serum_market.address, wallet.address, context.dex_program_id, serum_market.base.decimals, serum_market.quote.decimals)
|
||||
if len(all_open_orders) > 0:
|
||||
|
@ -124,24 +125,19 @@ def build_latest_serum_open_orders_observer(manager: mango.WebSocketSubscription
|
|||
|
||||
manager.add(serum_open_orders_subscription)
|
||||
|
||||
latest_serum_open_orders_observer = mango.LatestItemObserverSubscriber(initial_serum_open_orders)
|
||||
latest_serum_open_orders_observer = mango.LatestItemObserverSubscriber[mango.PlacedOrdersContainer](
|
||||
initial_serum_open_orders)
|
||||
serum_open_orders_subscription.publisher.subscribe(latest_serum_open_orders_observer)
|
||||
add_file_health("open_orders_subscription", serum_open_orders_subscription.publisher, disposer)
|
||||
return latest_serum_open_orders_observer
|
||||
|
||||
|
||||
def build_latest_perp_open_orders_observer(disposer: mango.DisposePropagator, perp_market: mango.PerpMarket, account: mango.Account, account_subscription: mango.WebSocketSubscription[mango.Account]) -> mango.LatestItemObserverSubscriber[mango.OpenOrders]:
|
||||
def build_latest_perp_open_orders_observer(disposer: mango.DisposePropagator, perp_market: mango.PerpMarket, account: mango.Account, account_subscription: mango.WebSocketSubscription[mango.Account]) -> mango.LatestItemObserverSubscriber[mango.PlacedOrdersContainer]:
|
||||
index = group.find_perp_market_index(perp_market.address)
|
||||
|
||||
def _build_open_orders_from_account(account: mango.Account) -> mango.OpenOrders:
|
||||
perp_account = account.perp_accounts[index]
|
||||
perp_open_orders = perp_account.open_orders
|
||||
return mango.OpenOrders.from_perps_account_layout(context, account, perp_market, perp_open_orders)
|
||||
|
||||
initial_open_orders = _build_open_orders_from_account(account)
|
||||
latest_open_orders_observer = mango.LatestItemObserverSubscriber(initial_open_orders)
|
||||
initial_open_orders = account.perp_accounts[index].open_orders
|
||||
latest_open_orders_observer = mango.LatestItemObserverSubscriber[mango.PlacedOrdersContainer](initial_open_orders)
|
||||
account_subscription.publisher.subscribe(
|
||||
on_next=lambda account: latest_open_orders_observer.on_next(_build_open_orders_from_account(account)))
|
||||
on_next=lambda updated_account: latest_open_orders_observer.on_next(updated_account.perp_accounts[index].open_orders))
|
||||
add_file_health("open_orders_subscription", account_subscription.publisher, disposer)
|
||||
return latest_open_orders_observer
|
||||
|
||||
|
|
|
@ -30,18 +30,21 @@ from .marketoperations import MarketOperations, NullMarketOperations
|
|||
from .metadata import Metadata
|
||||
from .notification import NotificationTarget, TelegramNotificationTarget, DiscordNotificationTarget, MailjetNotificationTarget, CsvFileNotificationTarget, FilteringNotificationTarget, NotificationHandler, parse_subscription_target
|
||||
from .observables import DisposePropagator, NullObserverSubscriber, PrintingObserverSubscriber, TimestampedPrintingObserverSubscriber, CollectingObserverSubscriber, LatestItemObserverSubscriber, CaptureFirstItem, FunctionObserver, create_backpressure_skipping_observer, debug_print_item, log_subscription_error, observable_pipeline_error_reporter, EventSource, FileToucherObserver
|
||||
from .openorders import OpenOrders, PlacedOrder
|
||||
from .openorders import OpenOrders
|
||||
from .oracle import OracleSource, Price, Oracle, OracleProvider, SupportedOracleFeature
|
||||
from .orderbookside import OrderBookSide
|
||||
from .orders import Order, OrderType, Side
|
||||
from .ownedtokenvalue import OwnedTokenValue
|
||||
from .oraclefactory import create_oracle_provider
|
||||
from .perpaccount import PerpAccount
|
||||
from .perpeventqueue import PerpEvent, PerpFillEvent, PerpOutEvent, PerpUnknownEvent, PerpEventQueue, UnseenPerpEventChangesTracker
|
||||
from .perpmarket import PerpMarket, PerpMarketStub
|
||||
from .perpmarketdetails import PerpMarketDetails
|
||||
from .perpmarketinfo import PerpMarketInfo
|
||||
from .perpmarketinstructionbuilder import PerpMarketInstructionBuilder
|
||||
from .perpmarketoperations import PerpMarketOperations
|
||||
from .perpopenorders import PerpOpenOrders
|
||||
from .placedorder import PlacedOrder, PlacedOrdersContainer
|
||||
from .reconnectingwebsocket import ReconnectingWebsocket
|
||||
from .retrier import RetryWithPauses, retry_context
|
||||
from .rootbank import NodeBank, RootBank
|
||||
|
|
|
@ -26,6 +26,7 @@ from .encoding import encode_key
|
|||
from .group import Group
|
||||
from .layouts import layouts
|
||||
from .metadata import Metadata
|
||||
from .perpaccount import PerpAccount
|
||||
from .tokenvalue import TokenValue
|
||||
from .version import Version
|
||||
|
||||
|
@ -40,7 +41,7 @@ class Account(AddressableAccount):
|
|||
meta_data: Metadata, group: Group, owner: PublicKey, in_margin_basket: typing.Sequence[Decimal],
|
||||
deposits: typing.Sequence[typing.Optional[TokenValue]], borrows: typing.Sequence[typing.Optional[TokenValue]],
|
||||
net_assets: typing.Sequence[typing.Optional[TokenValue]], spot_open_orders: typing.Sequence[PublicKey],
|
||||
perp_accounts: typing.Sequence[typing.Any], msrm_amount: Decimal, being_liquidated: bool,
|
||||
perp_accounts: typing.Sequence[PerpAccount], msrm_amount: Decimal, being_liquidated: bool,
|
||||
is_bankrupt: bool):
|
||||
super().__init__(account_info)
|
||||
self.version: Version = version
|
||||
|
@ -53,7 +54,7 @@ class Account(AddressableAccount):
|
|||
self.borrows: typing.Sequence[typing.Optional[TokenValue]] = borrows
|
||||
self.net_assets: typing.Sequence[typing.Optional[TokenValue]] = net_assets
|
||||
self.spot_open_orders: typing.Sequence[PublicKey] = spot_open_orders
|
||||
self.perp_accounts: typing.Sequence[layouts.PERP_ACCOUNT] = perp_accounts
|
||||
self.perp_accounts: typing.Sequence[PerpAccount] = perp_accounts
|
||||
self.msrm_amount: Decimal = msrm_amount
|
||||
self.being_liquidated: bool = being_liquidated
|
||||
self.is_bankrupt: bool = is_bankrupt
|
||||
|
@ -81,10 +82,10 @@ class Account(AddressableAccount):
|
|||
net_assets += [None]
|
||||
|
||||
spot_open_orders: typing.Sequence[PublicKey] = layout.spot_open_orders
|
||||
perp_accounts: typing.Sequence[typing.Any] = layout.perp_accounts
|
||||
perp_accounts: typing.Sequence[PerpAccount] = list(map(PerpAccount.from_layout, layout.perp_accounts))
|
||||
msrm_amount: Decimal = layout.msrm_amount
|
||||
being_liquidated: bool = layout.being_liquidated
|
||||
is_bankrupt: bool = layout.is_bankrupt
|
||||
being_liquidated: bool = bool(layout.being_liquidated)
|
||||
is_bankrupt: bool = bool(layout.is_bankrupt)
|
||||
|
||||
return Account(account_info, version, meta_data, group, owner, in_margin_basket, deposits, borrows, net_assets, spot_open_orders, perp_accounts, msrm_amount, being_liquidated, is_bankrupt)
|
||||
|
||||
|
@ -96,7 +97,7 @@ class Account(AddressableAccount):
|
|||
f"Account data length ({len(data)}) does not match expected size ({layouts.MANGO_ACCOUNT.sizeof()})")
|
||||
|
||||
layout = layouts.MANGO_ACCOUNT.parse(data)
|
||||
return Account.from_layout(layout, account_info, Version.V1, group)
|
||||
return Account.from_layout(layout, account_info, Version.V3, group)
|
||||
|
||||
@staticmethod
|
||||
def load(context: Context, address: PublicKey, group: Group) -> "Account":
|
||||
|
@ -138,24 +139,34 @@ class Account(AddressableAccount):
|
|||
return Account.load_all_for_owner(context, owner, group)[0]
|
||||
|
||||
def __str__(self):
|
||||
deposits = "\n ".join(
|
||||
[f"{deposit}" for deposit in self.deposits if deposit is not None and deposit.value != Decimal(0)] or ["None"])
|
||||
borrows = "\n ".join(
|
||||
[f"{borrow}" for borrow in self.borrows if borrow is not None and borrow.value != Decimal(0)] or ["None"])
|
||||
net_assets = "\n ".join(
|
||||
[f"{net_asset}" for net_asset in self.net_assets if net_asset is not None and net_asset.value != Decimal(0)] or ["None"])
|
||||
def _render_list(items, stub):
|
||||
rendered = []
|
||||
for index, item in enumerate(items):
|
||||
rendered += [f"{index}: {(item or stub)}".replace("\n", "\n ")]
|
||||
return rendered
|
||||
available_deposit_count = len([deposit for deposit in self.deposits if deposit is not None])
|
||||
deposits = "\n ".join(_render_list(self.deposits, "« No Deposit »"))
|
||||
available_borrow_count = len([borrow for borrow in self.borrows if borrow is not None])
|
||||
borrows = "\n ".join(_render_list(self.borrows, "« No Borrow »"))
|
||||
net_assets = "\n ".join(_render_list(self.net_assets, "« No Net Assets »"))
|
||||
spot_open_orders = ", ".join([f"{oo}" for oo in self.spot_open_orders if oo is not None])
|
||||
perp_accounts = ", ".join(
|
||||
[f"{perp}".replace("\n", "\n ") for perp in self.perp_accounts if perp.open_orders.free_slot_bits != 0xFFFFFFFF])
|
||||
indices_in_basket = []
|
||||
for index, value in enumerate(self.in_margin_basket):
|
||||
if value != 0:
|
||||
indices_in_basket += [index]
|
||||
in_margin_basket = ", ".join([f"{self.group.tokens[index].token.symbol}" for index in indices_in_basket])
|
||||
return f"""« 𝙰𝚌𝚌𝚘𝚞𝚗𝚝 {self.version} [{self.address}]
|
||||
{self.meta_data}
|
||||
Bankrupt? {self.is_bankrupt}
|
||||
Being Liquidated? {self.being_liquidated}
|
||||
Owner: {self.owner}
|
||||
Group: « 𝙶𝚛𝚘𝚞𝚙 '{self.group.name}' {self.group.version} [{self.group.address}] »
|
||||
Deposits:
|
||||
In Basket: {in_margin_basket}
|
||||
Bankrupt? {self.is_bankrupt}
|
||||
Being Liquidated? {self.being_liquidated}
|
||||
Deposits [{available_deposit_count} available]:
|
||||
{deposits}
|
||||
Borrows:
|
||||
Borrows [{available_borrow_count} available]:
|
||||
{borrows}
|
||||
Net Assets:
|
||||
{net_assets}
|
||||
|
|
|
@ -113,7 +113,7 @@ class Group(AddressableAccount):
|
|||
|
||||
layout = layouts.GROUP.parse(data)
|
||||
name = context.lookup_group_name(account_info.address)
|
||||
return Group.from_layout(context, layout, name, account_info, Version.V1, context.token_lookup, context.market_lookup)
|
||||
return Group.from_layout(context, layout, name, account_info, Version.V3, context.token_lookup, context.market_lookup)
|
||||
|
||||
@staticmethod
|
||||
def load(context: Context, address: typing.Optional[PublicKey] = None) -> "Group":
|
||||
|
@ -156,9 +156,6 @@ class Group(AddressableAccount):
|
|||
return balances
|
||||
|
||||
def __str__(self):
|
||||
print("Token 14", self.tokens[14])
|
||||
print("Token 14 is None", self.tokens[14] is None)
|
||||
|
||||
def _render_list(items, stub):
|
||||
rendered = []
|
||||
for index, item in enumerate(items):
|
||||
|
|
|
@ -646,10 +646,6 @@ def build_spot_place_order_instructions(context: Context, wallet: Wallet, group:
|
|||
relevant_open_orders += [AccountMeta(is_signer=False, is_writable=False,
|
||||
pubkey=oo_address or SYSTEM_PROGRAM_ADDRESS)]
|
||||
|
||||
print("root_bank.address", root_bank.address)
|
||||
print("node_bank.address", node_bank.address)
|
||||
print("open_orders_address", open_orders_address)
|
||||
print("account.address", account.address)
|
||||
fee_discount_address_meta: typing.List[AccountMeta] = []
|
||||
if fee_discount_address is not None:
|
||||
fee_discount_address_meta = [AccountMeta(is_signer=False, is_writable=False, pubkey=fee_discount_address)]
|
||||
|
|
|
@ -30,7 +30,7 @@ class ModelState:
|
|||
group_watcher: mango.LatestItemObserverSubscriber[mango.Group],
|
||||
price_watcher: mango.LatestItemObserverSubscriber[mango.Price],
|
||||
perp_market_watcher: typing.Optional[mango.LatestItemObserverSubscriber[mango.PerpMarketDetails]],
|
||||
open_orders_watcher: mango.LatestItemObserverSubscriber[mango.OpenOrders]
|
||||
placed_orders_container_watcher: mango.LatestItemObserverSubscriber[mango.PlacedOrdersContainer]
|
||||
):
|
||||
self.logger: logging.Logger = logging.getLogger(self.__class__.__name__)
|
||||
self.market: mango.Market = market
|
||||
|
@ -39,7 +39,8 @@ class ModelState:
|
|||
self.price_watcher: mango.LatestItemObserverSubscriber[mango.Price] = price_watcher
|
||||
self.perp_market_watcher: typing.Optional[mango.LatestItemObserverSubscriber[mango.PerpMarketDetails]
|
||||
] = perp_market_watcher
|
||||
self.open_orders_watcher: mango.LatestItemObserverSubscriber[mango.OpenOrders] = open_orders_watcher
|
||||
self.placed_orders_container_watcher: mango.LatestItemObserverSubscriber[
|
||||
mango.PlacedOrdersContainer] = placed_orders_container_watcher
|
||||
|
||||
@property
|
||||
def group(self) -> mango.Group:
|
||||
|
@ -55,17 +56,13 @@ class ModelState:
|
|||
return None
|
||||
return self.perp_market_watcher.latest
|
||||
|
||||
@property
|
||||
def open_orders(self) -> mango.OpenOrders:
|
||||
return self.open_orders_watcher.latest
|
||||
|
||||
@property
|
||||
def price(self) -> mango.Price:
|
||||
return self.price_watcher.latest
|
||||
|
||||
@property
|
||||
def placed_orders(self) -> typing.Sequence[mango.PlacedOrder]:
|
||||
return self.open_orders.placed_orders
|
||||
def existing_orders(self) -> typing.Sequence[mango.PlacedOrder]:
|
||||
return self.placed_orders_container_watcher.latest.placed_orders
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"""« 𝙼𝚘𝚍𝚎𝚕𝚂𝚝𝚊𝚝𝚎 for market '{self.market.symbol}' »"""
|
||||
|
|
|
@ -37,17 +37,17 @@ class OrderTracker:
|
|||
|
||||
def existing_orders(self, model_state: ModelState) -> typing.Sequence[mango.Order]:
|
||||
live_orders: typing.List[mango.Order] = []
|
||||
for placed_order in model_state.placed_orders:
|
||||
details = self._find_tracked(placed_order.client_id)
|
||||
for existing_order in model_state.existing_orders:
|
||||
details = self._find_tracked(existing_order.client_id)
|
||||
if details is None:
|
||||
self.logger.warning(f"Could not find existing order with client ID {placed_order.client_id}")
|
||||
self.logger.warning(f"Could not find existing order with client ID {existing_order.client_id}")
|
||||
# Return a stub order so that the Reconciler has the chance to cancel it.
|
||||
stub = mango.Order.from_ids(placed_order.id, placed_order.client_id, placed_order.side)
|
||||
stub = mango.Order.from_ids(existing_order.id, existing_order.client_id, existing_order.side)
|
||||
live_orders += [stub]
|
||||
else:
|
||||
if details.id != placed_order.id:
|
||||
if details.id != existing_order.id:
|
||||
self.tracked.remove(details)
|
||||
details = details.with_id(placed_order.id)
|
||||
details = details.with_id(existing_order.id)
|
||||
self.tracked += [details]
|
||||
|
||||
live_orders += [details]
|
||||
|
|
|
@ -21,7 +21,6 @@ from pyserum.open_orders_account import OpenOrdersAccount
|
|||
from solana.publickey import PublicKey
|
||||
from solana.rpc.types import MemcmpOpts
|
||||
|
||||
from .account import Account
|
||||
from .accountflags import AccountFlags
|
||||
from .accountinfo import AccountInfo
|
||||
from .addressableaccount import AddressableAccount
|
||||
|
@ -29,36 +28,9 @@ from .context import Context
|
|||
from .encoding import encode_key
|
||||
from .group import Group
|
||||
from .layouts import layouts
|
||||
from .orders import Side
|
||||
from .perpmarket import PerpMarket
|
||||
from .placedorder import PlacedOrder
|
||||
from .version import Version
|
||||
|
||||
|
||||
class PlacedOrder(typing.NamedTuple):
|
||||
id: int
|
||||
client_id: int
|
||||
side: Side
|
||||
|
||||
@staticmethod
|
||||
def build_from_open_orders_data(free_slot_bits: Decimal, is_bid_bits: Decimal, order_ids: typing.Sequence[Decimal], client_order_ids: typing.Sequence[Decimal]):
|
||||
int_free_slot_bits = int(free_slot_bits)
|
||||
int_is_bid_bits = int(is_bid_bits)
|
||||
placed_orders: typing.List[PlacedOrder] = []
|
||||
for index in range(len(order_ids)):
|
||||
if not (int_free_slot_bits & (1 << index)):
|
||||
order_id = int(order_ids[index])
|
||||
client_id = int(client_order_ids[index])
|
||||
side = Side.BUY if int_is_bid_bits & (1 << index) else Side.SELL
|
||||
placed_orders += [PlacedOrder(id=order_id, client_id=client_id, side=side)]
|
||||
return placed_orders
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"{self}"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"« 𝙿𝚕𝚊𝚌𝚎𝚍𝙾𝚛𝚍𝚎𝚛 {self.side} [{self.id}] {self.client_id} »"
|
||||
|
||||
|
||||
# # 🥭 OpenOrders class
|
||||
#
|
||||
|
||||
|
@ -107,18 +79,6 @@ class OpenOrders(AddressableAccount):
|
|||
layout.owner, base_token_free, base_token_total, quote_token_free,
|
||||
quote_token_total, placed_orders, layout.referrer_rebate_accrued)
|
||||
|
||||
@staticmethod
|
||||
def from_perps_account_layout(context: Context, account: Account, perp_market: PerpMarket, perp_open_orders: layouts.PERP_OPEN_ORDERS) -> "OpenOrders":
|
||||
account_flags = AccountFlags(Version.UNSPECIFIED, True, False,
|
||||
True, False, False, False, False, False)
|
||||
placed_orders = PlacedOrder.build_from_open_orders_data(
|
||||
perp_open_orders.free_slot_bits, perp_open_orders.is_bid_bits, perp_open_orders.orders, perp_open_orders.client_order_ids)
|
||||
open_orders = OpenOrders(account.account_info, Version.V1, context.program_id,
|
||||
account_flags, perp_market.address, account.address,
|
||||
Decimal(0), Decimal(0), Decimal(0), Decimal(0),
|
||||
placed_orders, Decimal(0))
|
||||
return open_orders
|
||||
|
||||
@staticmethod
|
||||
def parse(account_info: AccountInfo, base_decimals: Decimal, quote_decimals: Decimal) -> "OpenOrders":
|
||||
data = account_info.data
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
# # ⚠ 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)
|
||||
|
||||
|
||||
from decimal import Decimal
|
||||
|
||||
from .layouts import layouts
|
||||
from .perpopenorders import PerpOpenOrders
|
||||
|
||||
|
||||
# # 🥭 PerpAccount class
|
||||
#
|
||||
# Perp accounts aren't directly addressable. They exist as a sub-object of a full Mango `Account` object.
|
||||
#
|
||||
class PerpAccount:
|
||||
def __init__(self, base_position: Decimal, quote_position: Decimal, long_settled_funding: Decimal,
|
||||
short_settled_funding: Decimal, mngo_accrued: Decimal, open_orders: PerpOpenOrders):
|
||||
self.base_position: Decimal = base_position
|
||||
self.quote_position: Decimal = quote_position
|
||||
self.long_settled_funding: Decimal = long_settled_funding
|
||||
self.short_settled_funding: Decimal = short_settled_funding
|
||||
self.mngo_accrued: Decimal = mngo_accrued
|
||||
self.open_orders: PerpOpenOrders = open_orders
|
||||
|
||||
@staticmethod
|
||||
def from_layout(layout: layouts.PERP_ACCOUNT) -> "PerpAccount":
|
||||
base_position: Decimal = layout.base_position
|
||||
quote_position: Decimal = layout.quote_position
|
||||
long_settled_funding: Decimal = layout.long_settled_funding
|
||||
short_settled_funding: Decimal = layout.short_settled_funding
|
||||
mngo_accrued: Decimal = layout.mngo_accrued
|
||||
|
||||
open_orders: PerpOpenOrders = PerpOpenOrders.from_layout(layout.open_orders)
|
||||
|
||||
return PerpAccount(base_position, quote_position, long_settled_funding, short_settled_funding, mngo_accrued, open_orders)
|
||||
|
||||
def __str__(self) -> str:
|
||||
open_orders = f"{self.open_orders}".replace("\n", "\n ")
|
||||
return f"""« 𝙿𝚎𝚛𝚙𝙰𝚌𝚌𝚘𝚞𝚗𝚝
|
||||
Base Position: {self.base_position}
|
||||
Quote Position: {self.quote_position}
|
||||
Long Settled Funding: {self.long_settled_funding}
|
||||
Short Settled Funding: {self.short_settled_funding}
|
||||
MNGO Accrued: {self.mngo_accrued}
|
||||
OpenOrders:
|
||||
{open_orders}
|
||||
»"""
|
|
@ -0,0 +1,55 @@
|
|||
# # ⚠ 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 .layouts import layouts
|
||||
from .openorders import PlacedOrder
|
||||
|
||||
|
||||
# # 🥭 PerpOpenOrders class
|
||||
#
|
||||
class PerpOpenOrders:
|
||||
def __init__(self, bids_quantity: Decimal, asks_quantity: Decimal, free_slot_bits: Decimal,
|
||||
is_bid_bits: Decimal, placed_orders: typing.Sequence[PlacedOrder]):
|
||||
self.bids_quantity: Decimal = bids_quantity
|
||||
self.asks_quantity: Decimal = asks_quantity
|
||||
self.free_slot_bits: Decimal = free_slot_bits
|
||||
self.is_bid_bits: Decimal = is_bid_bits
|
||||
self.placed_orders: typing.Sequence[PlacedOrder] = placed_orders
|
||||
|
||||
@staticmethod
|
||||
def from_layout(layout: layouts.PERP_OPEN_ORDERS) -> "PerpOpenOrders":
|
||||
bids_quantity: Decimal = layout.bids_quantity
|
||||
asks_quantity: Decimal = layout.asks_quantity
|
||||
free_slot_bits: Decimal = layout.free_slot_bits
|
||||
is_bid_bits: Decimal = layout.is_bid_bits
|
||||
|
||||
placed_orders = PlacedOrder.build_from_open_orders_data(
|
||||
layout.free_slot_bits, layout.is_bid_bits, layout.orders, layout.client_order_ids)
|
||||
return PerpOpenOrders(bids_quantity, asks_quantity, free_slot_bits, is_bid_bits, placed_orders)
|
||||
|
||||
def __str__(self) -> str:
|
||||
placed_orders = "\n ".join(map(str, self.placed_orders)) or "None"
|
||||
|
||||
return f"""« 𝙿𝚎𝚛𝚙𝙾𝚙𝚎𝚗𝙾𝚛𝚍𝚎𝚛𝚜
|
||||
Bids Quantity: {self.bids_quantity}
|
||||
Asks Quantity: {self.asks_quantity}
|
||||
Orders:
|
||||
{placed_orders}
|
||||
»"""
|
|
@ -0,0 +1,65 @@
|
|||
# # ⚠ 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 .orders import Side
|
||||
|
||||
# # 🥭 PlacedOrder tuple
|
||||
#
|
||||
# A `PlacedOrder` is a representation of all the data available from an Open Orders account pertaining to a
|
||||
# particular order.
|
||||
#
|
||||
# The information is usually split across 3 collections - 'is bid', 'orders' and 'client ID's. That can be a
|
||||
# little awkward to use, so this tuple packages it all together, per order.
|
||||
#
|
||||
|
||||
|
||||
class PlacedOrder(typing.NamedTuple):
|
||||
id: int
|
||||
client_id: int
|
||||
side: Side
|
||||
|
||||
@staticmethod
|
||||
def build_from_open_orders_data(free_slot_bits: Decimal, is_bid_bits: Decimal, order_ids: typing.Sequence[Decimal], client_order_ids: typing.Sequence[Decimal]):
|
||||
int_free_slot_bits = int(free_slot_bits)
|
||||
int_is_bid_bits = int(is_bid_bits)
|
||||
placed_orders: typing.List[PlacedOrder] = []
|
||||
for index in range(len(order_ids)):
|
||||
if not (int_free_slot_bits & (1 << index)):
|
||||
order_id = int(order_ids[index])
|
||||
client_id = int(client_order_ids[index])
|
||||
side = Side.BUY if int_is_bid_bits & (1 << index) else Side.SELL
|
||||
placed_orders += [PlacedOrder(id=order_id, client_id=client_id, side=side)]
|
||||
return placed_orders
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"{self}"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"« 𝙿𝚕𝚊𝚌𝚎𝚍𝙾𝚛𝚍𝚎𝚛 {self.side} [{self.id}] {self.client_id} »"
|
||||
|
||||
|
||||
# # 🥭 PlacedOrdersContainer protocol
|
||||
#
|
||||
# The `PlacedOrdersContainer` protocol exposes commonality between the regular Serum `OpenOrders` class and the
|
||||
# internally-different `PerpOpenOrders` class. Both have their own `placed_orders` member, but are otherwise
|
||||
# different enough that a common abstract base class would be a bit kludgy.
|
||||
#
|
||||
class PlacedOrdersContainer(typing.Protocol):
|
||||
placed_orders: typing.Sequence[PlacedOrder]
|
Loading…
Reference in New Issue