Improved perp cranking and added public key sorting tests.
This commit is contained in:
parent
59394060e9
commit
6352844ed1
|
@ -122,6 +122,8 @@ from .marketoperations import MarketOperations as MarketOperations
|
||||||
from .marketoperations import NullMarketInstructionBuilder as NullMarketInstructionBuilder
|
from .marketoperations import NullMarketInstructionBuilder as NullMarketInstructionBuilder
|
||||||
from .marketoperations import NullMarketOperations as NullMarketOperations
|
from .marketoperations import NullMarketOperations as NullMarketOperations
|
||||||
from .metadata import Metadata as Metadata
|
from .metadata import Metadata as Metadata
|
||||||
|
from .modelstate import EventQueue as EventQueue
|
||||||
|
from .modelstate import NullEventQueue as NullEventQueue
|
||||||
from .modelstate import ModelState as ModelState
|
from .modelstate import ModelState as ModelState
|
||||||
from .notification import CompoundNotificationTarget as CompoundNotificationTarget
|
from .notification import CompoundNotificationTarget as CompoundNotificationTarget
|
||||||
from .notification import ConsoleNotificationTarget as ConsoleNotificationTarget
|
from .notification import ConsoleNotificationTarget as ConsoleNotificationTarget
|
||||||
|
@ -239,6 +241,9 @@ from .watchers import build_perp_open_orders_watcher as build_perp_open_orders_w
|
||||||
from .watchers import build_price_watcher as build_price_watcher
|
from .watchers import build_price_watcher as build_price_watcher
|
||||||
from .watchers import build_serum_inventory_watcher as build_serum_inventory_watcher
|
from .watchers import build_serum_inventory_watcher as build_serum_inventory_watcher
|
||||||
from .watchers import build_orderbook_watcher as build_orderbook_watcher
|
from .watchers import build_orderbook_watcher as build_orderbook_watcher
|
||||||
|
from .watchers import build_serum_event_queue_watcher as build_serum_event_queue_watcher
|
||||||
|
from .watchers import build_spot_event_queue_watcher as build_spot_event_queue_watcher
|
||||||
|
from .watchers import build_perp_event_queue_watcher as build_perp_event_queue_watcher
|
||||||
from .websocketsubscription import IndividualWebSocketSubscriptionManager as IndividualWebSocketSubscriptionManager
|
from .websocketsubscription import IndividualWebSocketSubscriptionManager as IndividualWebSocketSubscriptionManager
|
||||||
from .websocketsubscription import SharedWebSocketSubscriptionManager as SharedWebSocketSubscriptionManager
|
from .websocketsubscription import SharedWebSocketSubscriptionManager as SharedWebSocketSubscriptionManager
|
||||||
from .websocketsubscription import WebSocketAccountSubscription as WebSocketAccountSubscription
|
from .websocketsubscription import WebSocketAccountSubscription as WebSocketAccountSubscription
|
||||||
|
|
|
@ -97,7 +97,7 @@ Ignore:
|
||||||
place_order = self.market_instruction_builder.build_place_order_instructions(to_place_with_client_id)
|
place_order = self.market_instruction_builder.build_place_order_instructions(to_place_with_client_id)
|
||||||
place_orders += place_order
|
place_orders += place_order
|
||||||
|
|
||||||
crank = self.market_instruction_builder.build_crank_instructions([])
|
crank = self.market_instruction_builder.build_crank_instructions(model_state.accounts_to_crank)
|
||||||
settle = self.market_instruction_builder.build_settle_instructions()
|
settle = self.market_instruction_builder.build_settle_instructions()
|
||||||
|
|
||||||
redeem = mango.CombinableInstructions.empty()
|
redeem = mango.CombinableInstructions.empty()
|
||||||
|
|
|
@ -88,7 +88,7 @@ class PollingModelStateBuilder(ModelStateBuilder):
|
||||||
|
|
||||||
def from_values(self, order_owner: PublicKey, market: mango.Market, group: mango.Group, account: mango.Account,
|
def from_values(self, order_owner: PublicKey, market: mango.Market, group: mango.Group, account: mango.Account,
|
||||||
price: mango.Price, placed_orders_container: mango.PlacedOrdersContainer,
|
price: mango.Price, placed_orders_container: mango.PlacedOrdersContainer,
|
||||||
inventory: mango.Inventory, orderbook: mango.OrderBook) -> ModelState:
|
inventory: mango.Inventory, orderbook: mango.OrderBook, event_queue: mango.EventQueue) -> ModelState:
|
||||||
group_watcher: mango.ManualUpdateWatcher[mango.Group] = mango.ManualUpdateWatcher(group)
|
group_watcher: mango.ManualUpdateWatcher[mango.Group] = mango.ManualUpdateWatcher(group)
|
||||||
account_watcher: mango.ManualUpdateWatcher[mango.Account] = mango.ManualUpdateWatcher(account)
|
account_watcher: mango.ManualUpdateWatcher[mango.Account] = mango.ManualUpdateWatcher(account)
|
||||||
price_watcher: mango.ManualUpdateWatcher[mango.Price] = mango.ManualUpdateWatcher(price)
|
price_watcher: mango.ManualUpdateWatcher[mango.Price] = mango.ManualUpdateWatcher(price)
|
||||||
|
@ -96,9 +96,11 @@ class PollingModelStateBuilder(ModelStateBuilder):
|
||||||
mango.PlacedOrdersContainer] = mango.ManualUpdateWatcher(placed_orders_container)
|
mango.PlacedOrdersContainer] = mango.ManualUpdateWatcher(placed_orders_container)
|
||||||
inventory_watcher: mango.ManualUpdateWatcher[mango.Inventory] = mango.ManualUpdateWatcher(inventory)
|
inventory_watcher: mango.ManualUpdateWatcher[mango.Inventory] = mango.ManualUpdateWatcher(inventory)
|
||||||
orderbook_watcher: mango.ManualUpdateWatcher[mango.OrderBook] = mango.ManualUpdateWatcher(orderbook)
|
orderbook_watcher: mango.ManualUpdateWatcher[mango.OrderBook] = mango.ManualUpdateWatcher(orderbook)
|
||||||
|
event_queue_watcher: mango.ManualUpdateWatcher[mango.EventQueue] = mango.ManualUpdateWatcher(event_queue)
|
||||||
|
|
||||||
return ModelState(order_owner, market, group_watcher, account_watcher, price_watcher,
|
return ModelState(order_owner, market, group_watcher, account_watcher, price_watcher,
|
||||||
placed_orders_container_watcher, inventory_watcher, orderbook_watcher)
|
placed_orders_container_watcher, inventory_watcher, orderbook_watcher,
|
||||||
|
event_queue_watcher)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return "« PollingModelStateBuilder »"
|
return "« PollingModelStateBuilder »"
|
||||||
|
@ -145,7 +147,8 @@ class SerumPollingModelStateBuilder(PollingModelStateBuilder):
|
||||||
self.base_inventory_token_account.address,
|
self.base_inventory_token_account.address,
|
||||||
self.quote_inventory_token_account.address,
|
self.quote_inventory_token_account.address,
|
||||||
self.market.bids_address,
|
self.market.bids_address,
|
||||||
self.market.asks_address
|
self.market.asks_address,
|
||||||
|
self.market.event_queue_address
|
||||||
]
|
]
|
||||||
account_infos: typing.Sequence[mango.AccountInfo] = mango.AccountInfo.load_multiple(context, addresses)
|
account_infos: typing.Sequence[mango.AccountInfo] = mango.AccountInfo.load_multiple(context, addresses)
|
||||||
group: mango.Group = mango.Group.parse_with_context(context, account_infos[0])
|
group: mango.Group = mango.Group.parse_with_context(context, account_infos[0])
|
||||||
|
@ -162,6 +165,8 @@ class SerumPollingModelStateBuilder(PollingModelStateBuilder):
|
||||||
|
|
||||||
orderbook: mango.OrderBook = self.market.parse_account_infos_to_orderbook(account_infos[6], account_infos[7])
|
orderbook: mango.OrderBook = self.market.parse_account_infos_to_orderbook(account_infos[6], account_infos[7])
|
||||||
|
|
||||||
|
event_queue: mango.EventQueue = mango.SerumEventQueue.parse(account_infos[8])
|
||||||
|
|
||||||
price: mango.Price = self.oracle.fetch_price(context)
|
price: mango.Price = self.oracle.fetch_price(context)
|
||||||
|
|
||||||
available: Decimal = (base_inventory_token_account.value.value * price.mid_price) + \
|
available: Decimal = (base_inventory_token_account.value.value * price.mid_price) + \
|
||||||
|
@ -173,7 +178,7 @@ class SerumPollingModelStateBuilder(PollingModelStateBuilder):
|
||||||
base_inventory_token_account.value,
|
base_inventory_token_account.value,
|
||||||
quote_inventory_token_account.value)
|
quote_inventory_token_account.value)
|
||||||
|
|
||||||
return self.from_values(self.order_owner, self.market, group, account, price, placed_orders_container, inventory, orderbook)
|
return self.from_values(self.order_owner, self.market, group, account, price, placed_orders_container, inventory, orderbook, event_queue)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"""« SerumPollingModelStateBuilder for market '{self.market.symbol}' »"""
|
return f"""« SerumPollingModelStateBuilder for market '{self.market.symbol}' »"""
|
||||||
|
@ -214,6 +219,7 @@ class SpotPollingModelStateBuilder(PollingModelStateBuilder):
|
||||||
self.account_address,
|
self.account_address,
|
||||||
self.market.bids_address,
|
self.market.bids_address,
|
||||||
self.market.asks_address,
|
self.market.asks_address,
|
||||||
|
self.market.event_queue_address,
|
||||||
*self.all_open_orders_addresses
|
*self.all_open_orders_addresses
|
||||||
]
|
]
|
||||||
account_infos: typing.Sequence[mango.AccountInfo] = mango.AccountInfo.load_multiple(context, addresses)
|
account_infos: typing.Sequence[mango.AccountInfo] = mango.AccountInfo.load_multiple(context, addresses)
|
||||||
|
@ -225,7 +231,7 @@ class SpotPollingModelStateBuilder(PollingModelStateBuilder):
|
||||||
self.all_open_orders_addresses = account.spot_open_orders
|
self.all_open_orders_addresses = account.spot_open_orders
|
||||||
|
|
||||||
spot_open_orders_account_infos_by_address = {
|
spot_open_orders_account_infos_by_address = {
|
||||||
str(account_info.address): account_info for account_info in account_infos[5:]}
|
str(account_info.address): account_info for account_info in account_infos[6:]}
|
||||||
|
|
||||||
all_open_orders: typing.Dict[str, mango.OpenOrders] = {}
|
all_open_orders: typing.Dict[str, mango.OpenOrders] = {}
|
||||||
for basket_token in account.slots:
|
for basket_token in account.slots:
|
||||||
|
@ -256,9 +262,11 @@ class SpotPollingModelStateBuilder(PollingModelStateBuilder):
|
||||||
|
|
||||||
orderbook: mango.OrderBook = self.market.parse_account_infos_to_orderbook(account_infos[3], account_infos[4])
|
orderbook: mango.OrderBook = self.market.parse_account_infos_to_orderbook(account_infos[3], account_infos[4])
|
||||||
|
|
||||||
|
event_queue: mango.EventQueue = mango.SerumEventQueue.parse(account_infos[5])
|
||||||
|
|
||||||
price: mango.Price = self.oracle.fetch_price(context)
|
price: mango.Price = self.oracle.fetch_price(context)
|
||||||
|
|
||||||
return self.from_values(self.order_owner, self.market, group, account, price, placed_orders_container, inventory, orderbook)
|
return self.from_values(self.order_owner, self.market, group, account, price, placed_orders_container, inventory, orderbook, event_queue)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"""« SpotPollingModelStateBuilder for market '{self.market.symbol}' »"""
|
return f"""« SpotPollingModelStateBuilder for market '{self.market.symbol}' »"""
|
||||||
|
@ -294,7 +302,8 @@ class PerpPollingModelStateBuilder(PollingModelStateBuilder):
|
||||||
self.cache_address,
|
self.cache_address,
|
||||||
self.account_address,
|
self.account_address,
|
||||||
self.market.underlying_perp_market.bids,
|
self.market.underlying_perp_market.bids,
|
||||||
self.market.underlying_perp_market.asks
|
self.market.underlying_perp_market.asks,
|
||||||
|
self.market.event_queue_address
|
||||||
]
|
]
|
||||||
account_infos: typing.Sequence[mango.AccountInfo] = mango.AccountInfo.load_multiple(context, addresses)
|
account_infos: typing.Sequence[mango.AccountInfo] = mango.AccountInfo.load_multiple(context, addresses)
|
||||||
group: mango.Group = mango.Group.parse_with_context(context, account_infos[0])
|
group: mango.Group = mango.Group.parse_with_context(context, account_infos[0])
|
||||||
|
@ -320,9 +329,11 @@ class PerpPollingModelStateBuilder(PollingModelStateBuilder):
|
||||||
|
|
||||||
orderbook: mango.OrderBook = self.market.parse_account_infos_to_orderbook(account_infos[3], account_infos[4])
|
orderbook: mango.OrderBook = self.market.parse_account_infos_to_orderbook(account_infos[3], account_infos[4])
|
||||||
|
|
||||||
|
event_queue: mango.EventQueue = mango.PerpEventQueue.parse(account_infos[5], self.market.lot_size_converter)
|
||||||
|
|
||||||
price: mango.Price = self.oracle.fetch_price(context)
|
price: mango.Price = self.oracle.fetch_price(context)
|
||||||
|
|
||||||
return self.from_values(self.order_owner, self.market, group, account, price, placed_orders_container, inventory, orderbook)
|
return self.from_values(self.order_owner, self.market, group, account, price, placed_orders_container, inventory, orderbook, event_queue)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"""« PerpPollingModelStateBuilder for market '{self.market.symbol}' »"""
|
return f"""« PerpPollingModelStateBuilder for market '{self.market.symbol}' »"""
|
||||||
|
|
|
@ -130,7 +130,9 @@ def _websocket_model_state_builder_factory(context: mango.Context, disposer: man
|
||||||
context, websocket_manager, health_check, disposer, wallet, market, price_watcher)
|
context, websocket_manager, health_check, disposer, wallet, market, price_watcher)
|
||||||
latest_open_orders_observer: mango.Watcher[mango.PlacedOrdersContainer] = mango.build_serum_open_orders_watcher(
|
latest_open_orders_observer: mango.Watcher[mango.PlacedOrdersContainer] = mango.build_serum_open_orders_watcher(
|
||||||
context, websocket_manager, health_check, market, wallet)
|
context, websocket_manager, health_check, market, wallet)
|
||||||
latest_orderbook_watcher = mango.build_orderbook_watcher(
|
latest_orderbook_watcher: mango.Watcher[mango.OrderBook] = mango.build_orderbook_watcher(
|
||||||
|
context, websocket_manager, health_check, market)
|
||||||
|
latest_event_queue_watcher: mango.Watcher[mango.EventQueue] = mango.build_serum_event_queue_watcher(
|
||||||
context, websocket_manager, health_check, market)
|
context, websocket_manager, health_check, market)
|
||||||
elif isinstance(market, mango.SpotMarket):
|
elif isinstance(market, mango.SpotMarket):
|
||||||
market_index: int = group.slot_by_spot_market_address(market.address).index
|
market_index: int = group.slot_by_spot_market_address(market.address).index
|
||||||
|
@ -155,6 +157,8 @@ def _websocket_model_state_builder_factory(context: mango.Context, disposer: man
|
||||||
market, latest_account_observer, group_watcher, all_open_orders_watchers, cache_watcher)
|
market, latest_account_observer, group_watcher, all_open_orders_watchers, cache_watcher)
|
||||||
latest_orderbook_watcher = mango.build_orderbook_watcher(
|
latest_orderbook_watcher = mango.build_orderbook_watcher(
|
||||||
context, websocket_manager, health_check, market)
|
context, websocket_manager, health_check, market)
|
||||||
|
latest_event_queue_watcher = mango.build_spot_event_queue_watcher(
|
||||||
|
context, websocket_manager, health_check, market)
|
||||||
elif isinstance(market, mango.PerpMarket):
|
elif isinstance(market, mango.PerpMarket):
|
||||||
order_owner = account.address
|
order_owner = account.address
|
||||||
inventory_watcher = mango.PerpInventoryAccountWatcher(
|
inventory_watcher = mango.PerpInventoryAccountWatcher(
|
||||||
|
@ -163,10 +167,12 @@ def _websocket_model_state_builder_factory(context: mango.Context, disposer: man
|
||||||
context, websocket_manager, health_check, market, account, group, account_subscription)
|
context, websocket_manager, health_check, market, account, group, account_subscription)
|
||||||
latest_orderbook_watcher = mango.build_orderbook_watcher(
|
latest_orderbook_watcher = mango.build_orderbook_watcher(
|
||||||
context, websocket_manager, health_check, market)
|
context, websocket_manager, health_check, market)
|
||||||
|
latest_event_queue_watcher = mango.build_perp_event_queue_watcher(
|
||||||
|
context, websocket_manager, health_check, market)
|
||||||
else:
|
else:
|
||||||
raise Exception(f"Could not determine type of market {market.symbol}")
|
raise Exception(f"Could not determine type of market {market.symbol}")
|
||||||
|
|
||||||
model_state = ModelState(order_owner, market, group_watcher, latest_account_observer,
|
model_state = ModelState(order_owner, market, group_watcher, latest_account_observer,
|
||||||
latest_price_observer, latest_open_orders_observer,
|
latest_price_observer, latest_open_orders_observer,
|
||||||
inventory_watcher, latest_orderbook_watcher)
|
inventory_watcher, latest_orderbook_watcher, latest_event_queue_watcher)
|
||||||
return WebsocketModelStateBuilder(model_state)
|
return WebsocketModelStateBuilder(model_state)
|
||||||
|
|
|
@ -77,7 +77,7 @@ class MarketInstructionBuilder(metaclass=abc.ABCMeta):
|
||||||
"MarketInstructionBuilder.build_settle_instructions() is not implemented on the base type.")
|
"MarketInstructionBuilder.build_settle_instructions() is not implemented on the base type.")
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def build_crank_instructions(self, open_orders_addresses: typing.Sequence[PublicKey], limit: Decimal = Decimal(32)) -> CombinableInstructions:
|
def build_crank_instructions(self, addresses: typing.Sequence[PublicKey], limit: Decimal = Decimal(32)) -> CombinableInstructions:
|
||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
"MarketInstructionBuilder.build_crank_instructions() is not implemented on the base type.")
|
"MarketInstructionBuilder.build_crank_instructions() is not implemented on the base type.")
|
||||||
|
|
||||||
|
@ -164,7 +164,7 @@ class NullMarketInstructionBuilder(MarketInstructionBuilder):
|
||||||
def build_settle_instructions(self) -> CombinableInstructions:
|
def build_settle_instructions(self) -> CombinableInstructions:
|
||||||
return CombinableInstructions.empty()
|
return CombinableInstructions.empty()
|
||||||
|
|
||||||
def build_crank_instructions(self, addresses_to_crank: typing.Sequence[PublicKey], limit: Decimal = Decimal(32)) -> CombinableInstructions:
|
def build_crank_instructions(self, addresses: typing.Sequence[PublicKey], limit: Decimal = Decimal(32)) -> CombinableInstructions:
|
||||||
return CombinableInstructions.empty()
|
return CombinableInstructions.empty()
|
||||||
|
|
||||||
def build_redeem_instructions(self) -> CombinableInstructions:
|
def build_redeem_instructions(self) -> CombinableInstructions:
|
||||||
|
|
|
@ -30,6 +30,25 @@ from .placedorder import PlacedOrdersContainer
|
||||||
from .watcher import Watcher
|
from .watcher import Watcher
|
||||||
|
|
||||||
|
|
||||||
|
# # 🥭 EventQueue protocol
|
||||||
|
#
|
||||||
|
# The `EventQueue` protocol just says the object has a property `accounts_to_crank` that returns a `Sequence` of `PublicKey`s.
|
||||||
|
#
|
||||||
|
# This is to share the interface between a `SerumEventQueue` and a `PerpEventQueue` for cranking, when the
|
||||||
|
# underlying objects are really quite different.
|
||||||
|
#
|
||||||
|
class EventQueue(typing.Protocol):
|
||||||
|
@property
|
||||||
|
def accounts_to_crank(self) -> typing.Sequence[PublicKey]:
|
||||||
|
raise NotImplementedError("EventQueue.accounts_to_crank is not implemented on the Protocol.")
|
||||||
|
|
||||||
|
|
||||||
|
class NullEventQueue:
|
||||||
|
@property
|
||||||
|
def accounts_to_crank(self) -> typing.Sequence[PublicKey]:
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
# # 🥭 ModelState class
|
# # 🥭 ModelState class
|
||||||
#
|
#
|
||||||
# Provides simple access to the latest state of market and account data.
|
# Provides simple access to the latest state of market and account data.
|
||||||
|
@ -43,7 +62,8 @@ class ModelState:
|
||||||
price_watcher: Watcher[Price],
|
price_watcher: Watcher[Price],
|
||||||
placed_orders_container_watcher: Watcher[PlacedOrdersContainer],
|
placed_orders_container_watcher: Watcher[PlacedOrdersContainer],
|
||||||
inventory_watcher: Watcher[Inventory],
|
inventory_watcher: Watcher[Inventory],
|
||||||
orderbook: Watcher[OrderBook]
|
orderbook: Watcher[OrderBook],
|
||||||
|
event_queue: Watcher[EventQueue]
|
||||||
) -> None:
|
) -> None:
|
||||||
self._logger: logging.Logger = logging.getLogger(self.__class__.__name__)
|
self._logger: logging.Logger = logging.getLogger(self.__class__.__name__)
|
||||||
self.order_owner: PublicKey = order_owner
|
self.order_owner: PublicKey = order_owner
|
||||||
|
@ -55,6 +75,7 @@ class ModelState:
|
||||||
PlacedOrdersContainer] = placed_orders_container_watcher
|
PlacedOrdersContainer] = placed_orders_container_watcher
|
||||||
self.inventory_watcher: Watcher[Inventory] = inventory_watcher
|
self.inventory_watcher: Watcher[Inventory] = inventory_watcher
|
||||||
self.orderbook_watcher: Watcher[OrderBook] = orderbook
|
self.orderbook_watcher: Watcher[OrderBook] = orderbook
|
||||||
|
self.event_queue_watcher: Watcher[EventQueue] = event_queue
|
||||||
|
|
||||||
self.not_quoting: bool = False
|
self.not_quoting: bool = False
|
||||||
self.state: typing.Dict[str, typing.Any] = {}
|
self.state: typing.Dict[str, typing.Any] = {}
|
||||||
|
@ -105,6 +126,10 @@ class ModelState:
|
||||||
def spread(self) -> Decimal:
|
def spread(self) -> Decimal:
|
||||||
return self.orderbook.spread
|
return self.orderbook.spread
|
||||||
|
|
||||||
|
@property
|
||||||
|
def accounts_to_crank(self) -> typing.Sequence[PublicKey]:
|
||||||
|
return self.event_queue_watcher.latest.accounts_to_crank
|
||||||
|
|
||||||
def current_orders(self) -> typing.Sequence[Order]:
|
def current_orders(self) -> typing.Sequence[Order]:
|
||||||
self.orderbook
|
self.orderbook
|
||||||
all_orders = [*self.bids, *self.asks]
|
all_orders = [*self.bids, *self.asks]
|
||||||
|
|
|
@ -237,6 +237,22 @@ class PerpEventQueue(AddressableAccount):
|
||||||
raise Exception(f"PerpEventQueue account not found at address '{address}'")
|
raise Exception(f"PerpEventQueue account not found at address '{address}'")
|
||||||
return PerpEventQueue.parse(account_info, lot_size_converter)
|
return PerpEventQueue.parse(account_info, lot_size_converter)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def accounts_to_crank(self) -> typing.Sequence[PublicKey]:
|
||||||
|
to_crank: typing.List[PublicKey] = []
|
||||||
|
for event_to_crank in self.unprocessed_events:
|
||||||
|
to_crank += event_to_crank.accounts_to_crank
|
||||||
|
|
||||||
|
seen = []
|
||||||
|
distinct = []
|
||||||
|
for account in to_crank:
|
||||||
|
account_str = account.to_base58()
|
||||||
|
if account_str not in seen:
|
||||||
|
distinct += [account]
|
||||||
|
seen += [account_str]
|
||||||
|
|
||||||
|
return distinct
|
||||||
|
|
||||||
@ property
|
@ property
|
||||||
def capacity(self) -> int:
|
def capacity(self) -> int:
|
||||||
return len(self.unprocessed_events) + len(self.processed_events)
|
return len(self.unprocessed_events) + len(self.processed_events)
|
||||||
|
|
|
@ -112,6 +112,10 @@ class PerpMarket(LoadedMarket):
|
||||||
def asks_address(self) -> PublicKey:
|
def asks_address(self) -> PublicKey:
|
||||||
return self.underlying_perp_market.asks
|
return self.underlying_perp_market.asks
|
||||||
|
|
||||||
|
@property
|
||||||
|
def event_queue_address(self) -> PublicKey:
|
||||||
|
return self.underlying_perp_market.event_queue
|
||||||
|
|
||||||
def parse_account_info_to_orders(self, account_info: AccountInfo) -> typing.Sequence[Order]:
|
def parse_account_info_to_orders(self, account_info: AccountInfo) -> typing.Sequence[Order]:
|
||||||
side: PerpOrderBookSide = PerpOrderBookSide.parse(account_info, self.underlying_perp_market)
|
side: PerpOrderBookSide = PerpOrderBookSide.parse(account_info, self.underlying_perp_market)
|
||||||
return side.orders()
|
return side.orders()
|
||||||
|
@ -124,8 +128,7 @@ class PerpMarket(LoadedMarket):
|
||||||
return FundingRate.from_stats_data(self.symbol, self.lot_size_converter, oldest_stats, newest_stats)
|
return FundingRate.from_stats_data(self.symbol, self.lot_size_converter, oldest_stats, newest_stats)
|
||||||
|
|
||||||
def unprocessed_events(self, context: Context) -> typing.Sequence[PerpEvent]:
|
def unprocessed_events(self, context: Context) -> typing.Sequence[PerpEvent]:
|
||||||
event_queue: PerpEventQueue = PerpEventQueue.load(
|
event_queue: PerpEventQueue = PerpEventQueue.load(context, self.event_queue_address, self.lot_size_converter)
|
||||||
context, self.underlying_perp_market.event_queue, self.lot_size_converter)
|
|
||||||
return event_queue.unprocessed_events
|
return event_queue.unprocessed_events
|
||||||
|
|
||||||
def accounts_to_crank(self, context: Context, additional_account_to_crank: typing.Optional[PublicKey]) -> typing.Sequence[PublicKey]:
|
def accounts_to_crank(self, context: Context, additional_account_to_crank: typing.Optional[PublicKey]) -> typing.Sequence[PublicKey]:
|
||||||
|
|
|
@ -71,22 +71,20 @@ class PerpMarketInstructionBuilder(MarketInstructionBuilder):
|
||||||
def build_settle_instructions(self) -> CombinableInstructions:
|
def build_settle_instructions(self) -> CombinableInstructions:
|
||||||
return CombinableInstructions.empty()
|
return CombinableInstructions.empty()
|
||||||
|
|
||||||
def build_crank_instructions(self, account_addresses: typing.Sequence[PublicKey], limit: Decimal = Decimal(32)) -> CombinableInstructions:
|
def build_crank_instructions(self, addresses: typing.Sequence[PublicKey], limit: Decimal = Decimal(32)) -> CombinableInstructions:
|
||||||
if self.perp_market.underlying_perp_market is None:
|
if self.perp_market.underlying_perp_market is None:
|
||||||
raise Exception(f"PerpMarket {self.perp_market.symbol} has not been loaded.")
|
raise Exception(f"PerpMarket {self.perp_market.symbol} has not been loaded.")
|
||||||
|
|
||||||
addresses_to_crank: typing.Sequence[PublicKey] = [*account_addresses, self.account.address]
|
distinct_addresses: typing.List[PublicKey] = [self.account.address]
|
||||||
distinct_addresses_to_crank: typing.List[PublicKey] = []
|
for address in addresses:
|
||||||
for address in addresses_to_crank:
|
if address not in distinct_addresses:
|
||||||
if address not in distinct_addresses_to_crank:
|
distinct_addresses += [address]
|
||||||
distinct_addresses_to_crank += [address]
|
|
||||||
|
|
||||||
limited_addresses_to_crank = distinct_addresses_to_crank[0:min(
|
limited_addresses = distinct_addresses[0:min(int(limit), len(distinct_addresses))]
|
||||||
int(limit), len(distinct_addresses_to_crank))]
|
limited_addresses.sort(key=encode_public_key_for_sorting)
|
||||||
|
self._logger.debug(f"About to crank {len(limited_addresses)} addresses: {limited_addresses}")
|
||||||
|
|
||||||
limited_addresses_to_crank.sort(key=encode_public_key_for_sorting)
|
return build_mango_consume_events_instructions(self.context, self.group, self.perp_market.underlying_perp_market, limited_addresses, limit)
|
||||||
|
|
||||||
return build_mango_consume_events_instructions(self.context, self.group, self.perp_market.underlying_perp_market, limited_addresses_to_crank, limit)
|
|
||||||
|
|
||||||
def build_redeem_instructions(self) -> CombinableInstructions:
|
def build_redeem_instructions(self) -> CombinableInstructions:
|
||||||
return build_redeem_accrued_mango_instructions(self.context, self.wallet, self.perp_market, self.group, self.account, self.mngo_token_bank)
|
return build_redeem_accrued_mango_instructions(self.context, self.wallet, self.perp_market, self.group, self.account, self.mngo_token_bank)
|
||||||
|
@ -141,7 +139,7 @@ class PerpMarketOperations(MarketOperations):
|
||||||
|
|
||||||
def crank(self, limit: Decimal = Decimal(32)) -> typing.Sequence[str]:
|
def crank(self, limit: Decimal = Decimal(32)) -> typing.Sequence[str]:
|
||||||
signers: CombinableInstructions = CombinableInstructions.from_wallet(self.wallet)
|
signers: CombinableInstructions = CombinableInstructions.from_wallet(self.wallet)
|
||||||
accounts_to_crank = self.perp_market.accounts_to_crank(self.context, None)
|
accounts_to_crank = self.perp_market.accounts_to_crank(self.context, self.account.address)
|
||||||
crank = self.market_instruction_builder.build_crank_instructions(accounts_to_crank, limit)
|
crank = self.market_instruction_builder.build_crank_instructions(accounts_to_crank, limit)
|
||||||
return (signers + crank).execute(self.context)
|
return (signers + crank).execute(self.context)
|
||||||
|
|
||||||
|
|
|
@ -160,6 +160,22 @@ class SerumEventQueue(AddressableAccount):
|
||||||
raise Exception(f"SerumEventQueue account not found at address '{address}'")
|
raise Exception(f"SerumEventQueue account not found at address '{address}'")
|
||||||
return SerumEventQueue.parse(account_info)
|
return SerumEventQueue.parse(account_info)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def accounts_to_crank(self) -> typing.Sequence[PublicKey]:
|
||||||
|
to_crank: typing.List[PublicKey] = []
|
||||||
|
for event_to_crank in self.unprocessed_events:
|
||||||
|
to_crank += [event_to_crank.public_key]
|
||||||
|
|
||||||
|
seen = []
|
||||||
|
distinct = []
|
||||||
|
for account in to_crank:
|
||||||
|
account_str = account.to_base58()
|
||||||
|
if account_str not in seen:
|
||||||
|
distinct += [account]
|
||||||
|
seen += [account_str]
|
||||||
|
|
||||||
|
return distinct
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def capacity(self) -> int:
|
def capacity(self) -> int:
|
||||||
return len(self.unprocessed_events) + len(self.processed_events)
|
return len(self.unprocessed_events) + len(self.processed_events)
|
||||||
|
|
|
@ -54,12 +54,16 @@ class SerumMarket(LoadedMarket):
|
||||||
def asks_address(self) -> PublicKey:
|
def asks_address(self) -> PublicKey:
|
||||||
return self.underlying_serum_market.state.asks()
|
return self.underlying_serum_market.state.asks()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def event_queue_address(self) -> PublicKey:
|
||||||
|
return self.underlying_serum_market.state.event_queue()
|
||||||
|
|
||||||
def parse_account_info_to_orders(self, account_info: AccountInfo) -> typing.Sequence[Order]:
|
def parse_account_info_to_orders(self, account_info: AccountInfo) -> typing.Sequence[Order]:
|
||||||
orderbook: PySerumOrderBook = PySerumOrderBook.from_bytes(self.underlying_serum_market.state, account_info.data)
|
orderbook: PySerumOrderBook = PySerumOrderBook.from_bytes(self.underlying_serum_market.state, account_info.data)
|
||||||
return list(map(Order.from_serum_order, orderbook.orders()))
|
return list(map(Order.from_serum_order, orderbook.orders()))
|
||||||
|
|
||||||
def unprocessed_events(self, context: Context) -> typing.Sequence[SerumEvent]:
|
def unprocessed_events(self, context: Context) -> typing.Sequence[SerumEvent]:
|
||||||
event_queue: SerumEventQueue = SerumEventQueue.load(context, self.underlying_serum_market.state.event_queue())
|
event_queue: SerumEventQueue = SerumEventQueue.load(context, self.event_queue_address)
|
||||||
return event_queue.unprocessed_events
|
return event_queue.unprocessed_events
|
||||||
|
|
||||||
def find_openorders_address_for_owner(self, context: Context, owner: PublicKey) -> typing.Optional[PublicKey]:
|
def find_openorders_address_for_owner(self, context: Context, owner: PublicKey) -> typing.Optional[PublicKey]:
|
||||||
|
|
|
@ -120,23 +120,20 @@ class SerumMarketInstructionBuilder(MarketInstructionBuilder):
|
||||||
return CombinableInstructions.empty()
|
return CombinableInstructions.empty()
|
||||||
return build_serum_settle_instructions(self.context, self.wallet, self.raw_market, self.open_orders_address, self.base_token_account.address, self.quote_token_account.address)
|
return build_serum_settle_instructions(self.context, self.wallet, self.raw_market, self.open_orders_address, self.base_token_account.address, self.quote_token_account.address)
|
||||||
|
|
||||||
def build_crank_instructions(self, open_orders_addresses: typing.Sequence[PublicKey], limit: Decimal = Decimal(32)) -> CombinableInstructions:
|
def build_crank_instructions(self, addresses: typing.Sequence[PublicKey], limit: Decimal = Decimal(32)) -> CombinableInstructions:
|
||||||
if self.open_orders_address is None:
|
if self.open_orders_address is None:
|
||||||
self._logger.debug("Returning empty crank instructions - no serum OpenOrders address provided.")
|
self._logger.debug("Returning empty crank instructions - no serum OpenOrders address provided.")
|
||||||
return CombinableInstructions.empty()
|
return CombinableInstructions.empty()
|
||||||
|
|
||||||
open_orders_to_crank: typing.Sequence[PublicKey] = [*open_orders_addresses, self.open_orders_address]
|
distinct_addresses: typing.List[PublicKey] = [self.open_orders_address]
|
||||||
distinct_open_orders_addresses: typing.List[PublicKey] = []
|
for oo in addresses:
|
||||||
for oo in open_orders_to_crank:
|
if oo not in distinct_addresses:
|
||||||
if oo not in distinct_open_orders_addresses:
|
distinct_addresses += [oo]
|
||||||
distinct_open_orders_addresses += [oo]
|
|
||||||
|
|
||||||
limited_open_orders_addresses = distinct_open_orders_addresses[0:min(
|
limited_addresses = distinct_addresses[0:min(int(limit), len(distinct_addresses))]
|
||||||
int(limit), len(distinct_open_orders_addresses))]
|
limited_addresses.sort(key=encode_public_key_for_sorting)
|
||||||
|
|
||||||
limited_open_orders_addresses.sort(key=encode_public_key_for_sorting)
|
return build_serum_consume_events_instructions(self.context, self.serum_market.address, self.raw_market.state.event_queue(), limited_addresses, int(limit))
|
||||||
|
|
||||||
return build_serum_consume_events_instructions(self.context, self.serum_market.address, self.raw_market.state.event_queue(), limited_open_orders_addresses, int(limit))
|
|
||||||
|
|
||||||
def build_create_openorders_instructions(self) -> CombinableInstructions:
|
def build_create_openorders_instructions(self) -> CombinableInstructions:
|
||||||
create_open_orders = build_create_serum_open_orders_instructions(self.context, self.wallet, self.raw_market)
|
create_open_orders = build_create_serum_open_orders_instructions(self.context, self.wallet, self.raw_market)
|
||||||
|
|
|
@ -55,12 +55,16 @@ class SpotMarket(LoadedMarket):
|
||||||
def asks_address(self) -> PublicKey:
|
def asks_address(self) -> PublicKey:
|
||||||
return self.underlying_serum_market.state.asks()
|
return self.underlying_serum_market.state.asks()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def event_queue_address(self) -> PublicKey:
|
||||||
|
return self.underlying_serum_market.state.event_queue()
|
||||||
|
|
||||||
def parse_account_info_to_orders(self, account_info: AccountInfo) -> typing.Sequence[Order]:
|
def parse_account_info_to_orders(self, account_info: AccountInfo) -> typing.Sequence[Order]:
|
||||||
orderbook: PySerumOrderBook = PySerumOrderBook.from_bytes(self.underlying_serum_market.state, account_info.data)
|
orderbook: PySerumOrderBook = PySerumOrderBook.from_bytes(self.underlying_serum_market.state, account_info.data)
|
||||||
return list(map(Order.from_serum_order, orderbook.orders()))
|
return list(map(Order.from_serum_order, orderbook.orders()))
|
||||||
|
|
||||||
def unprocessed_events(self, context: Context) -> typing.Sequence[SerumEvent]:
|
def unprocessed_events(self, context: Context) -> typing.Sequence[SerumEvent]:
|
||||||
event_queue: SerumEventQueue = SerumEventQueue.load(context, self.underlying_serum_market.state.event_queue())
|
event_queue: SerumEventQueue = SerumEventQueue.load(context, self.event_queue_address)
|
||||||
return event_queue.unprocessed_events
|
return event_queue.unprocessed_events
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
|
|
|
@ -108,23 +108,20 @@ class SpotMarketInstructionBuilder(MarketInstructionBuilder):
|
||||||
self.raw_market, self.group, self.open_orders_address,
|
self.raw_market, self.group, self.open_orders_address,
|
||||||
base_rootbank, base_nodebank, quote_rootbank, quote_nodebank)
|
base_rootbank, base_nodebank, quote_rootbank, quote_nodebank)
|
||||||
|
|
||||||
def build_crank_instructions(self, open_orders_addresses: typing.Sequence[PublicKey], limit: Decimal = Decimal(32)) -> CombinableInstructions:
|
def build_crank_instructions(self, addresses: typing.Sequence[PublicKey], limit: Decimal = Decimal(32)) -> CombinableInstructions:
|
||||||
if self.open_orders_address is None:
|
if self.open_orders_address is None:
|
||||||
self._logger.debug("Returning empty crank instructions - no spot OpenOrders address provided.")
|
self._logger.debug("Returning empty crank instructions - no spot OpenOrders address provided.")
|
||||||
return CombinableInstructions.empty()
|
return CombinableInstructions.empty()
|
||||||
|
|
||||||
open_orders_to_crank: typing.Sequence[PublicKey] = [*open_orders_addresses, self.open_orders_address]
|
distinct_addresses: typing.List[PublicKey] = [self.open_orders_address]
|
||||||
distinct_open_orders_addresses: typing.List[PublicKey] = []
|
for oo in addresses:
|
||||||
for oo in open_orders_to_crank:
|
if oo not in distinct_addresses:
|
||||||
if oo not in distinct_open_orders_addresses:
|
distinct_addresses += [oo]
|
||||||
distinct_open_orders_addresses += [oo]
|
|
||||||
|
|
||||||
limited_open_orders_addresses = distinct_open_orders_addresses[0:min(
|
limited_addresses = distinct_addresses[0:min(int(limit), len(distinct_addresses))]
|
||||||
int(limit), len(distinct_open_orders_addresses))]
|
limited_addresses.sort(key=encode_public_key_for_sorting)
|
||||||
|
|
||||||
limited_open_orders_addresses.sort(key=encode_public_key_for_sorting)
|
return build_serum_consume_events_instructions(self.context, self.spot_market.address, self.raw_market.state.event_queue(), limited_addresses, int(limit))
|
||||||
|
|
||||||
return build_serum_consume_events_instructions(self.context, self.spot_market.address, self.raw_market.state.event_queue(), limited_open_orders_addresses, int(limit))
|
|
||||||
|
|
||||||
def build_redeem_instructions(self) -> CombinableInstructions:
|
def build_redeem_instructions(self) -> CombinableInstructions:
|
||||||
return CombinableInstructions.empty()
|
return CombinableInstructions.empty()
|
||||||
|
|
|
@ -32,14 +32,17 @@ from .instrumentvalue import InstrumentValue
|
||||||
from .inventory import Inventory
|
from .inventory import Inventory
|
||||||
from .loadedmarket import LoadedMarket
|
from .loadedmarket import LoadedMarket
|
||||||
from .market import Market, InventorySource
|
from .market import Market, InventorySource
|
||||||
|
from .modelstate import EventQueue
|
||||||
from .observables import DisposePropagator, LatestItemObserverSubscriber
|
from .observables import DisposePropagator, LatestItemObserverSubscriber
|
||||||
from .openorders import OpenOrders
|
from .openorders import OpenOrders
|
||||||
from .oracle import Price
|
from .oracle import Price
|
||||||
from .oracle import OracleProvider
|
from .oracle import OracleProvider
|
||||||
from .oraclefactory import create_oracle_provider
|
from .oraclefactory import create_oracle_provider
|
||||||
from .orders import OrderBook
|
from .orders import OrderBook
|
||||||
|
from .perpeventqueue import PerpEventQueue
|
||||||
from .perpmarket import PerpMarket
|
from .perpmarket import PerpMarket
|
||||||
from .placedorder import PlacedOrdersContainer
|
from .placedorder import PlacedOrdersContainer
|
||||||
|
from .serumeventqueue import SerumEventQueue
|
||||||
from .serummarket import SerumMarket
|
from .serummarket import SerumMarket
|
||||||
from .spotmarket import SpotMarket
|
from .spotmarket import SpotMarket
|
||||||
from .spotmarketoperations import SpotMarketInstructionBuilder, SpotMarketOperations
|
from .spotmarketoperations import SpotMarketInstructionBuilder, SpotMarketOperations
|
||||||
|
@ -240,3 +243,36 @@ def build_orderbook_watcher(context: Context, manager: WebSocketSubscriptionMana
|
||||||
health_check.add("orderbook_bids_subscription", bids_subscription.publisher)
|
health_check.add("orderbook_bids_subscription", bids_subscription.publisher)
|
||||||
health_check.add("orderbook_asks_subscription", asks_subscription.publisher)
|
health_check.add("orderbook_asks_subscription", asks_subscription.publisher)
|
||||||
return orderbook_observer
|
return orderbook_observer
|
||||||
|
|
||||||
|
|
||||||
|
def build_serum_event_queue_watcher(context: Context, manager: WebSocketSubscriptionManager, health_check: HealthCheck, serum_market: SerumMarket) -> Watcher[EventQueue]:
|
||||||
|
initial: EventQueue = SerumEventQueue.load(context, serum_market.event_queue_address)
|
||||||
|
subscription = WebSocketAccountSubscription[EventQueue](
|
||||||
|
context, serum_market.event_queue_address, lambda account_info: SerumEventQueue.parse(account_info))
|
||||||
|
manager.add(subscription)
|
||||||
|
latest_observer = LatestItemObserverSubscriber[EventQueue](initial)
|
||||||
|
subscription.publisher.subscribe(latest_observer)
|
||||||
|
health_check.add("event_queue_subscription", subscription.publisher)
|
||||||
|
return latest_observer
|
||||||
|
|
||||||
|
|
||||||
|
def build_spot_event_queue_watcher(context: Context, manager: WebSocketSubscriptionManager, health_check: HealthCheck, spot_market: SpotMarket) -> Watcher[EventQueue]:
|
||||||
|
initial: EventQueue = SerumEventQueue.load(context, spot_market.event_queue_address)
|
||||||
|
subscription = WebSocketAccountSubscription[EventQueue](
|
||||||
|
context, spot_market.event_queue_address, lambda account_info: SerumEventQueue.parse(account_info))
|
||||||
|
manager.add(subscription)
|
||||||
|
latest_observer = LatestItemObserverSubscriber[EventQueue](initial)
|
||||||
|
subscription.publisher.subscribe(latest_observer)
|
||||||
|
health_check.add("event_queue_subscription", subscription.publisher)
|
||||||
|
return latest_observer
|
||||||
|
|
||||||
|
|
||||||
|
def build_perp_event_queue_watcher(context: Context, manager: WebSocketSubscriptionManager, health_check: HealthCheck, perp_market: PerpMarket) -> Watcher[EventQueue]:
|
||||||
|
initial: EventQueue = PerpEventQueue.load(context, perp_market.event_queue_address, perp_market.lot_size_converter)
|
||||||
|
subscription = WebSocketAccountSubscription[EventQueue](
|
||||||
|
context, perp_market.event_queue_address, lambda account_info: PerpEventQueue.parse(account_info, perp_market.lot_size_converter))
|
||||||
|
manager.add(subscription)
|
||||||
|
latest_observer = LatestItemObserverSubscriber[EventQueue](initial)
|
||||||
|
subscription.publisher.subscribe(latest_observer)
|
||||||
|
health_check.add("event_queue_subscription", subscription.publisher)
|
||||||
|
return latest_observer
|
||||||
|
|
|
@ -255,7 +255,8 @@ def fake_model_state(order_owner: typing.Optional[PublicKey] = None,
|
||||||
price: typing.Optional[mango.Price] = None,
|
price: typing.Optional[mango.Price] = None,
|
||||||
placed_orders_container: typing.Optional[mango.PlacedOrdersContainer] = None,
|
placed_orders_container: typing.Optional[mango.PlacedOrdersContainer] = None,
|
||||||
inventory: typing.Optional[mango.Inventory] = None,
|
inventory: typing.Optional[mango.Inventory] = None,
|
||||||
orderbook: typing.Optional[mango.OrderBook] = None) -> mango.ModelState:
|
orderbook: typing.Optional[mango.OrderBook] = None,
|
||||||
|
event_queue: typing.Optional[mango.EventQueue] = None) -> mango.ModelState:
|
||||||
order_owner = order_owner or fake_seeded_public_key("order owner")
|
order_owner = order_owner or fake_seeded_public_key("order owner")
|
||||||
market = market or fake_loaded_market()
|
market = market or fake_loaded_market()
|
||||||
group = group or fake_group()
|
group = group or fake_group()
|
||||||
|
@ -264,6 +265,7 @@ def fake_model_state(order_owner: typing.Optional[PublicKey] = None,
|
||||||
placed_orders_container = placed_orders_container or fake_placed_orders_container()
|
placed_orders_container = placed_orders_container or fake_placed_orders_container()
|
||||||
inventory = inventory or fake_inventory()
|
inventory = inventory or fake_inventory()
|
||||||
orderbook = orderbook or mango.OrderBook("FAKE", NullLotSizeConverter(), fake_bids(), fake_asks())
|
orderbook = orderbook or mango.OrderBook("FAKE", NullLotSizeConverter(), fake_bids(), fake_asks())
|
||||||
|
event_queue = event_queue or mango.NullEventQueue()
|
||||||
group_watcher: mango.ManualUpdateWatcher[mango.Group] = mango.ManualUpdateWatcher(group)
|
group_watcher: mango.ManualUpdateWatcher[mango.Group] = mango.ManualUpdateWatcher(group)
|
||||||
account_watcher: mango.ManualUpdateWatcher[mango.Account] = mango.ManualUpdateWatcher(account)
|
account_watcher: mango.ManualUpdateWatcher[mango.Account] = mango.ManualUpdateWatcher(account)
|
||||||
price_watcher: mango.ManualUpdateWatcher[mango.Price] = mango.ManualUpdateWatcher(price)
|
price_watcher: mango.ManualUpdateWatcher[mango.Price] = mango.ManualUpdateWatcher(price)
|
||||||
|
@ -271,10 +273,11 @@ def fake_model_state(order_owner: typing.Optional[PublicKey] = None,
|
||||||
mango.PlacedOrdersContainer] = mango.ManualUpdateWatcher(placed_orders_container)
|
mango.PlacedOrdersContainer] = mango.ManualUpdateWatcher(placed_orders_container)
|
||||||
inventory_watcher: mango.ManualUpdateWatcher[mango.Inventory] = mango.ManualUpdateWatcher(inventory)
|
inventory_watcher: mango.ManualUpdateWatcher[mango.Inventory] = mango.ManualUpdateWatcher(inventory)
|
||||||
orderbook_watcher: mango.ManualUpdateWatcher[mango.OrderBook] = mango.ManualUpdateWatcher(orderbook)
|
orderbook_watcher: mango.ManualUpdateWatcher[mango.OrderBook] = mango.ManualUpdateWatcher(orderbook)
|
||||||
|
event_queue_watcher: mango.ManualUpdateWatcher[mango.EventQueue] = mango.ManualUpdateWatcher(event_queue)
|
||||||
|
|
||||||
return mango.ModelState(order_owner, market, group_watcher,
|
return mango.ModelState(order_owner, market, group_watcher, account_watcher, price_watcher,
|
||||||
account_watcher, price_watcher, placed_orders_container_watcher,
|
placed_orders_container_watcher, inventory_watcher, orderbook_watcher,
|
||||||
inventory_watcher, orderbook_watcher)
|
event_queue_watcher)
|
||||||
|
|
||||||
|
|
||||||
def fake_mango_instruction() -> mango.MangoInstruction:
|
def fake_mango_instruction() -> mango.MangoInstruction:
|
||||||
|
|
|
@ -234,5 +234,75 @@ def test_publickey_sorting() -> None:
|
||||||
|
|
||||||
test_keys.sort(key=mango.encode_public_key_for_sorting)
|
test_keys.sort(key=mango.encode_public_key_for_sorting)
|
||||||
|
|
||||||
for counter in range(100):
|
for counter in range(len(test_keys)):
|
||||||
|
assert test_keys[counter] == expected[counter], f"Index {counter} - {test_keys[counter]} does not match expected {expected[counter]}"
|
||||||
|
|
||||||
|
|
||||||
|
# This is the same test but with results from sorting in a BPF Solana runtime.
|
||||||
|
#
|
||||||
|
# This runtime is very limited so 14 is the maximum number of keys that I've been able to sort there.
|
||||||
|
#
|
||||||
|
def test_publickey_bpf_sorting() -> None:
|
||||||
|
test_keys = [
|
||||||
|
PublicKey("FaQzFknCYS1Dt1T8d7wXr6a8dxSZwgb1YyiR1pmWJBC"),
|
||||||
|
PublicKey("AuAYgwDerZryPif7Zw1ZqACYgJFRqmKwy3ZqASr2Wu7d"),
|
||||||
|
PublicKey("79bShRscEARLjd1gzYz7LMC76ft87AQZxcRk3ZPg7thf"),
|
||||||
|
PublicKey("HMbNVVb6uqqhuTaQU4RmKDeG3VZ1pvRn3PgnhfevbjWJ"),
|
||||||
|
PublicKey("FFzYWt9K2ZDeyxpDbvKbma6232baDHoCFGL1gZAKiox1"),
|
||||||
|
PublicKey("CpFj2d5uYjeh34FKh6iYRTE2dL3N9NaSrZtyZVZ7eQwa"),
|
||||||
|
PublicKey("FyjuBBN5fUHjtpB5LbSVW1mMocWCBebWJEecr1YN1TaQ"),
|
||||||
|
PublicKey("2zBRHRBd8mieLZ4nvYpJqw8D9Bc9tRhgnrBvLxw5rswv"),
|
||||||
|
PublicKey("GFQFVSEYN9ho4UwA4KJefTGrrnV8xKwyT2U5VoY4ABRw"),
|
||||||
|
PublicKey("E9UL1uyqoU6KNvQhn4dRhJ85jzu9zSQ8mnKK6zG6K2LH"),
|
||||||
|
PublicKey("789UPSUbj9TYSm12e66qo8PCE7RvDygqFKyy7qvCjtBD"),
|
||||||
|
PublicKey("Hgbt3PYF3CPjJxwgurNPtU7PxKWZawxbVYAY3PHuqcRY"),
|
||||||
|
PublicKey("jD2gcCANgvQM54brip9jT9L3PHfG3YmoBALQeWQ2QFt"),
|
||||||
|
PublicKey("8ZuYuQdGesgcKs6UaiHZNo44iijnsFJ4zUEF3KymUhjw")
|
||||||
|
]
|
||||||
|
|
||||||
|
expected = [
|
||||||
|
PublicKey("FaQzFknCYS1Dt1T8d7wXr6a8dxSZwgb1YyiR1pmWJBC"),
|
||||||
|
PublicKey("jD2gcCANgvQM54brip9jT9L3PHfG3YmoBALQeWQ2QFt"),
|
||||||
|
PublicKey("2zBRHRBd8mieLZ4nvYpJqw8D9Bc9tRhgnrBvLxw5rswv"),
|
||||||
|
PublicKey("789UPSUbj9TYSm12e66qo8PCE7RvDygqFKyy7qvCjtBD"),
|
||||||
|
PublicKey("79bShRscEARLjd1gzYz7LMC76ft87AQZxcRk3ZPg7thf"),
|
||||||
|
PublicKey("8ZuYuQdGesgcKs6UaiHZNo44iijnsFJ4zUEF3KymUhjw"),
|
||||||
|
PublicKey("AuAYgwDerZryPif7Zw1ZqACYgJFRqmKwy3ZqASr2Wu7d"),
|
||||||
|
PublicKey("CpFj2d5uYjeh34FKh6iYRTE2dL3N9NaSrZtyZVZ7eQwa"),
|
||||||
|
PublicKey("E9UL1uyqoU6KNvQhn4dRhJ85jzu9zSQ8mnKK6zG6K2LH"),
|
||||||
|
PublicKey("FFzYWt9K2ZDeyxpDbvKbma6232baDHoCFGL1gZAKiox1"),
|
||||||
|
PublicKey("FyjuBBN5fUHjtpB5LbSVW1mMocWCBebWJEecr1YN1TaQ"),
|
||||||
|
PublicKey("GFQFVSEYN9ho4UwA4KJefTGrrnV8xKwyT2U5VoY4ABRw"),
|
||||||
|
PublicKey("HMbNVVb6uqqhuTaQU4RmKDeG3VZ1pvRn3PgnhfevbjWJ"),
|
||||||
|
PublicKey("Hgbt3PYF3CPjJxwgurNPtU7PxKWZawxbVYAY3PHuqcRY")
|
||||||
|
]
|
||||||
|
|
||||||
|
test_keys.sort(key=mango.encode_public_key_for_sorting)
|
||||||
|
|
||||||
|
for counter in range(len(test_keys)):
|
||||||
|
assert test_keys[counter] == expected[counter], f"Index {counter} - {test_keys[counter]} does not match expected {expected[counter]}"
|
||||||
|
|
||||||
|
|
||||||
|
# This is a short test to help with debugging, with results from the BPF Solana runtime.
|
||||||
|
#
|
||||||
|
def test_publickey_short_sorting() -> None:
|
||||||
|
test_keys = [
|
||||||
|
PublicKey("AuAYgwDerZryPif7Zw1ZqACYgJFRqmKwy3ZqASr2Wu7d"),
|
||||||
|
PublicKey("79bShRscEARLjd1gzYz7LMC76ft87AQZxcRk3ZPg7thf"),
|
||||||
|
PublicKey("HMbNVVb6uqqhuTaQU4RmKDeG3VZ1pvRn3PgnhfevbjWJ"),
|
||||||
|
PublicKey("FFzYWt9K2ZDeyxpDbvKbma6232baDHoCFGL1gZAKiox1"),
|
||||||
|
PublicKey("CpFj2d5uYjeh34FKh6iYRTE2dL3N9NaSrZtyZVZ7eQwa"),
|
||||||
|
]
|
||||||
|
|
||||||
|
expected = [
|
||||||
|
PublicKey("79bShRscEARLjd1gzYz7LMC76ft87AQZxcRk3ZPg7thf"),
|
||||||
|
PublicKey("AuAYgwDerZryPif7Zw1ZqACYgJFRqmKwy3ZqASr2Wu7d"),
|
||||||
|
PublicKey("CpFj2d5uYjeh34FKh6iYRTE2dL3N9NaSrZtyZVZ7eQwa"),
|
||||||
|
PublicKey("FFzYWt9K2ZDeyxpDbvKbma6232baDHoCFGL1gZAKiox1"),
|
||||||
|
PublicKey("HMbNVVb6uqqhuTaQU4RmKDeG3VZ1pvRn3PgnhfevbjWJ")
|
||||||
|
]
|
||||||
|
|
||||||
|
test_keys.sort(key=mango.encode_public_key_for_sorting)
|
||||||
|
|
||||||
|
for counter in range(len(test_keys)):
|
||||||
assert test_keys[counter] == expected[counter], f"Index {counter} - {test_keys[counter]} does not match expected {expected[counter]}"
|
assert test_keys[counter] == expected[counter], f"Index {counter} - {test_keys[counter]} does not match expected {expected[counter]}"
|
||||||
|
|
Loading…
Reference in New Issue