Added PerpEventQueue.events_for_account() and PerpEventQueue.fills_for_account(), UnseenAccountFillEventTracker and tests.

This commit is contained in:
Geoff Taylor 2022-01-31 15:44:46 +00:00
parent 0e8e815c81
commit e372fb932c
3 changed files with 162 additions and 0 deletions

View File

@ -177,6 +177,7 @@ from .perpeventqueue import PerpEventQueue as PerpEventQueue
from .perpeventqueue import PerpFillEvent as PerpFillEvent
from .perpeventqueue import PerpOutEvent as PerpOutEvent
from .perpeventqueue import PerpUnknownEvent as PerpUnknownEvent
from .perpeventqueue import UnseenAccountFillEventTracker as UnseenAccountFillEventTracker
from .perpeventqueue import UnseenPerpEventChangesTracker as UnseenPerpEventChangesTracker
from .perpmarket import PerpMarket as PerpMarket
from .perpmarket import PerpMarketStub as PerpMarketStub

View File

@ -76,6 +76,10 @@ class PerpFillEvent(PerpEvent):
self.taker_order_id: Decimal = taker_order_id
self.taker_client_order_id: Decimal = taker_client_order_id
@property
def key(self) -> str:
return f"{self.maker_order_id}/{self.taker_order_id}"
@property
def accounts_to_crank(self) -> typing.Sequence[PublicKey]:
return [self.maker, self.taker]
@ -253,6 +257,20 @@ class PerpEventQueue(AddressableAccount):
return distinct
def events_for_account(self, mango_account_address: PublicKey) -> typing.Sequence[PerpEvent]:
events: typing.List[PerpEvent] = []
for event in [*self.processed_events, *self.unprocessed_events]:
if mango_account_address in event.accounts_to_crank:
events += [event]
return events
def fills_for_account(self, mango_account_address: PublicKey) -> typing.Sequence[PerpFillEvent]:
fills: typing.List[PerpFillEvent] = []
for event in self.events_for_account(mango_account_address):
if isinstance(event, PerpFillEvent):
fills += [event]
return fills
@ property
def capacity(self) -> int:
return len(self.unprocessed_events) + len(self.processed_events)
@ -298,3 +316,40 @@ class UnseenPerpEventChangesTracker:
self.last_sequence_number = new_sequence_number
return unseen
# # 🥭 UnseenAccountFillEventTracker class
#
# `UnseenAccountFillEventTracker` tracks fills for a specific Mango Account in a specific
# `PerpEventQueue`. When an updated version of the `PerpEventQueue` is passed to `unseen()`,
# any new fills are returned.
#
class UnseenAccountFillEventTracker:
def __init__(self, initial: PerpEventQueue, mango_account_address: PublicKey) -> None:
self.mango_account_address: PublicKey = mango_account_address
self.last_sequence_number: Decimal = initial.sequence_number
initial_fills: typing.Sequence[PerpFillEvent] = initial.fills_for_account(mango_account_address)
self.last_key: str = initial_fills[-1].key if len(initial_fills) > 0 else ""
def unseen(self, event_queue: PerpEventQueue) -> typing.Sequence[PerpEvent]:
fills: typing.Sequence[PerpFillEvent] = event_queue.fills_for_account(self.mango_account_address)
if len(fills) == 0:
return []
last_key_position: int = -1
for counter, fill in enumerate(fills):
if fill.key == self.last_key:
last_key_position = counter
break
unseen: typing.Sequence[PerpFillEvent]
if last_key_position == -1:
# We haven't seen any of these fills
unseen = fills
elif last_key_position == len(fills) - 1:
unseen = []
else:
unseen = fills[last_key_position + 1:]
self.last_key = unseen[-1].key
return unseen

View File

@ -5,6 +5,7 @@ from solana.publickey import PublicKey
from .context import mango
from .fakes import fake_account_info, fake_seeded_public_key
from datetime import datetime
from decimal import Decimal
@ -50,6 +51,17 @@ class TstPE(mango.PerpEvent):
return f"« TstPE [{self.event_type}] »"
class TstFillPE(mango.PerpFillEvent):
def __init__(self, maker: PublicKey, maker_id: int, taker: PublicKey, taker_id: int):
super().__init__(0, Decimal(0), datetime.now(), mango.Side.BUY,
Decimal(1), Decimal(1), Decimal(1), Decimal(1), True,
maker, Decimal(maker_id), Decimal(0),
taker, Decimal(taker_id), Decimal(0))
def __str__(self) -> str:
return f"« TstFillPE [{self.maker_order_id} / {self.taker_order_id}] »"
def test_unseen_with_no_changes() -> None:
initial = _fake_pev(Decimal(5), Decimal(2), Decimal(7), [], [TstPE(), TstPE(), TstPE(), TstPE(), TstPE()])
actual: mango.UnseenPerpEventChangesTracker = mango.UnseenPerpEventChangesTracker(initial)
@ -123,3 +135,97 @@ def test_unseen_with_two_unprocessed_changes_wrapping_around() -> None:
assert len(unseen) == 2
assert unseen[0] == marker1
assert unseen[1] == marker2
def test_fills_for_account() -> None:
user1 = fake_seeded_public_key("user1")
user2 = fake_seeded_public_key("user2")
user3 = fake_seeded_public_key("user3")
order1 = TstFillPE(user1, 11, user2, 21)
order2 = TstFillPE(user2, 12, user3, 22)
order3 = TstFillPE(user3, 13, user1, 23)
pev = _fake_pev(Decimal(4), Decimal(0), Decimal(7), [], [order1, order2, order3])
my_fills = pev.fills_for_account(user1)
assert len(my_fills) == 2
assert my_fills[0] == order1
assert my_fills[1] == order3
def test_no_fills_for_account() -> None:
user1 = fake_seeded_public_key("user1")
user2 = fake_seeded_public_key("user2")
user3 = fake_seeded_public_key("user3")
user4 = fake_seeded_public_key("user4")
order1 = TstFillPE(user1, 11, user2, 21)
order2 = TstFillPE(user2, 12, user3, 22)
order3 = TstFillPE(user3, 13, user1, 23)
pev = _fake_pev(Decimal(4), Decimal(0), Decimal(7), [], [order1, order2, order3])
my_fills = pev.fills_for_account(user4)
assert len(my_fills) == 0
def test_unseen_fills_for_account() -> None:
user1 = fake_seeded_public_key("user1")
user2 = fake_seeded_public_key("user2")
user3 = fake_seeded_public_key("user3")
order1 = TstFillPE(user1, 11, user2, 21)
order2 = TstFillPE(user2, 12, user3, 22)
order3 = TstFillPE(user3, 13, user1, 23)
pev1 = _fake_pev(Decimal(4), Decimal(0), Decimal(7), [], [order1, order2, order3])
actual = mango.UnseenAccountFillEventTracker(pev1, user1)
order4 = TstFillPE(user3, 14, user2, 24)
order5 = TstFillPE(user1, 15, user3, 25)
order6 = TstFillPE(user2, 16, user1, 26)
pev2 = _fake_pev(Decimal(4), Decimal(0), Decimal(7), [], [order1, order2, order3, order4, order5, order6])
my_unseen_fills = actual.unseen(pev2)
assert len(my_unseen_fills) == 2
assert my_unseen_fills[0] == order5
assert my_unseen_fills[1] == order6
def test_no_unseen_fills_for_account() -> None:
# Exactly the same test as before but with user4 as the account we're watching for.
user1 = fake_seeded_public_key("user1")
user2 = fake_seeded_public_key("user2")
user3 = fake_seeded_public_key("user3")
user4 = fake_seeded_public_key("user4")
order1 = TstFillPE(user1, 11, user2, 21)
order2 = TstFillPE(user2, 12, user3, 22)
order3 = TstFillPE(user3, 13, user1, 23)
pev1 = _fake_pev(Decimal(4), Decimal(0), Decimal(7), [], [order1, order2, order3])
actual = mango.UnseenAccountFillEventTracker(pev1, user4)
order4 = TstFillPE(user3, 14, user2, 24)
order5 = TstFillPE(user1, 15, user3, 25)
order6 = TstFillPE(user2, 16, user1, 26)
pev2 = _fake_pev(Decimal(4), Decimal(0), Decimal(7), [], [order1, order2, order3, order4, order5, order6])
my_unseen_fills = actual.unseen(pev2)
assert len(my_unseen_fills) == 0
def test_no_changes_in_unseen_fills_for_account() -> None:
user1 = fake_seeded_public_key("user1")
user2 = fake_seeded_public_key("user2")
user3 = fake_seeded_public_key("user3")
user4 = fake_seeded_public_key("user4")
order1 = TstFillPE(user1, 11, user2, 21)
order2 = TstFillPE(user2, 12, user3, 22)
order3 = TstFillPE(user3, 13, user1, 23)
pev1 = _fake_pev(Decimal(4), Decimal(0), Decimal(7), [], [order1, order2, order3])
actual = mango.UnseenAccountFillEventTracker(pev1, user1)
order4 = TstFillPE(user3, 14, user2, 24)
order5 = TstFillPE(user4, 15, user3, 25)
order6 = TstFillPE(user2, 16, user4, 26)
pev2 = _fake_pev(Decimal(4), Decimal(0), Decimal(7), [], [order1, order2, order3, order4, order5, order6])
my_unseen_fills = actual.unseen(pev2)
assert len(my_unseen_fills) == 0