pyopenbook/pyserum/market/_internal/queue.py

94 lines
4.1 KiB
Python

import math
from enum import IntEnum
from typing import List, Optional, Sequence, Tuple, Union, cast
from construct import Container # type: ignore
from solana.publickey import PublicKey
from ..._layouts.queue import EVENT_LAYOUT, QUEUE_HEADER_LAYOUT, REQUEST_LAYOUT
from ..types import Event, EventFlags, Request, ReuqestFlags
class QueueType(IntEnum):
Event = 1
Request = 2
def __from_bytes(
buffer: Sequence[int], queue_type: QueueType, history: Optional[int]
) -> Tuple[Container, List[Union[Event, Request]]]:
header = QUEUE_HEADER_LAYOUT.parse(buffer)
layout_size = EVENT_LAYOUT.sizeof() if queue_type == QueueType.Event else REQUEST_LAYOUT.sizeof()
alloc_len = math.floor((len(buffer) - QUEUE_HEADER_LAYOUT.sizeof()) / layout_size)
nodes: List[Union[Event, Request]] = []
if history:
for i in range(min(history, alloc_len)):
node_index = (header.head + header.count + alloc_len - 1 - i) % alloc_len
offset = QUEUE_HEADER_LAYOUT.sizeof() + node_index * layout_size
nodes.append(__parse_queue_item(buffer[offset : offset + layout_size], queue_type)) # noqa: E203
else:
for i in range(header.count):
node_index = (header.head + i) % alloc_len
offset = QUEUE_HEADER_LAYOUT.sizeof() + node_index * layout_size
nodes.append(__parse_queue_item(buffer[offset : offset + layout_size], queue_type)) # noqa: E203
return header, nodes
def __parse_queue_item(buffer: Sequence[int], queue_type: QueueType) -> Union[Event, Request]:
if queue_type == QueueType.Event: # pylint: disable=no-else-return
parsed_item = EVENT_LAYOUT.parse(buffer)
parsed_event_flags = parsed_item.event_flags
event_flags = EventFlags(
fill=parsed_event_flags.fill,
out=parsed_event_flags.out,
bid=parsed_event_flags.bid,
maker=parsed_event_flags.maker,
)
return Event(
event_flags=event_flags,
open_order_slot=parsed_item.open_order_slot,
fee_tier=parsed_item.fee_tier,
native_quantity_released=parsed_item.native_quantity_released,
native_quantity_paid=parsed_item.native_quantity_paid,
native_fee_or_rebate=parsed_item.native_fee_or_rebate,
order_id=int.from_bytes(parsed_item.order_id, "little"),
public_key=PublicKey(parsed_item.public_key),
client_order_id=parsed_item.client_order_id,
)
else:
parsed_item = REQUEST_LAYOUT.parse(buffer)
parsed_request_flags = parsed_item.request_flags
request_flags = ReuqestFlags(
new_order=parsed_request_flags.new_order,
cancel_order=parsed_request_flags.cancel_order,
bid=parsed_request_flags.bid,
post_only=parsed_request_flags.post_only,
ioc=parsed_request_flags.ioc,
)
return Request(
request_flags=request_flags,
open_order_slot=parsed_item.open_order_slot,
fee_tier=parsed_item.fee_tier,
max_base_size_or_cancel_id=parsed_item.max_base_size_or_cancel_id,
native_quote_quantity_locked=parsed_item.native_quote_quantity_locked,
order_id=int.from_bytes(parsed_item.order_id, "little"),
open_orders=PublicKey(parsed_item.open_orders),
client_order_id=parsed_item.client_order_id,
)
def decode_request_queue(buffer: bytes, history: Optional[int] = None) -> List[Request]:
header, nodes = __from_bytes(buffer, QueueType.Request, history)
if not header.account_flags.initialized or not header.account_flags.request_queue:
raise Exception("Invalid requests queue, either not initialized or not a request queue.")
return cast(List[Request], nodes)
def decode_event_queue(buffer: bytes, history: Optional[int] = None) -> List[Event]:
header, nodes = __from_bytes(buffer, QueueType.Event, history)
if not header.account_flags.initialized or not header.account_flags.event_queue:
raise Exception("Invalid events queue, either not initialized or not a event queue.")
return cast(List[Event], nodes)