From 6593ef7b3b48936769152cc1572a9df1a81d4c57 Mon Sep 17 00:00:00 2001 From: skynetcap <100323448+skynetcap@users.noreply.github.com> Date: Sat, 18 Nov 2023 22:30:10 -0800 Subject: [PATCH] Phoenix: implement Market deserialization --- .../mmorrell/phoenix/model/PhoenixMarket.java | 88 ++++++++++++++++++- .../phoenix/model/PhoenixTraderState.java | 41 +++++++++ phoenix/src/test/java/PhoenixTest.java | 9 ++ 3 files changed, 135 insertions(+), 3 deletions(-) create mode 100644 phoenix/src/main/java/com/mmorrell/phoenix/model/PhoenixTraderState.java diff --git a/phoenix/src/main/java/com/mmorrell/phoenix/model/PhoenixMarket.java b/phoenix/src/main/java/com/mmorrell/phoenix/model/PhoenixMarket.java index d087761..19e43e8 100644 --- a/phoenix/src/main/java/com/mmorrell/phoenix/model/PhoenixMarket.java +++ b/phoenix/src/main/java/com/mmorrell/phoenix/model/PhoenixMarket.java @@ -34,6 +34,12 @@ public class PhoenixMarket { @Getter public static List> bidListSanitized; + @Getter + public static List> askList; + + @Getter + public static List> askListSanitized; + public static PhoenixMarket readPhoenixMarket(byte[] data, PhoenixMarketHeader header) { PhoenixMarket phoenixMarket = PhoenixMarket.builder() .baseLotsPerBaseUnit(Utils.readInt64(data, START_OFFSET)) @@ -46,9 +52,23 @@ public static PhoenixMarket readPhoenixMarket(byte[] data, PhoenixMarketHeader h long bidsSize = 16 + 16 + (16 + FIFOOrderId.FIFO_ORDER_ID_SIZE + FIFORestingOrder.FIFO_RESTING_ORDER_SIZE) * header.getBidsSize(); - byte[] bidBuffer = Arrays.copyOfRange(data, 880, (int) bidsSize); + var asksSize = + 16 + 16 + (16 + FIFOOrderId.FIFO_ORDER_ID_SIZE + FIFORestingOrder.FIFO_RESTING_ORDER_SIZE) * header.getAsksSize(); + byte[] askBuffer = Arrays.copyOfRange(data, 880 + (int) bidsSize, 880 + (int) bidsSize + (int) asksSize); + + var tradersSize = 16 + 16 + (16 + 32 + PhoenixTraderState.PHOENIX_TRADER_STATE_SIZE) * header.getNumSeats(); + byte[] traderBuffer = Arrays.copyOfRange(data, 880 + (int) bidsSize + (int) asksSize, + 880 + (int) bidsSize + (int) asksSize + (int) tradersSize); + + readBidBuffer(bidBuffer); + readAskBuffer(askBuffer); + + return phoenixMarket; + } + + private static void readBidBuffer(byte[] bidBuffer) { int offset = 0; offset += 16; // skip rbtree header offset += 8; // Skip node allocator size @@ -110,7 +130,69 @@ public static PhoenixMarket readPhoenixMarket(byte[] data, PhoenixMarketHeader h bidListSanitized.add(entry); } } + } - return phoenixMarket; - } + private static void readAskBuffer(byte[] bidBuffer) { + int offset = 0; + offset += 16; // skip rbtree header + offset += 8; // Skip node allocator size + + int bumpIndex = PhoenixUtil.readInt32(bidBuffer, offset); + offset += 4; + + int freeListHead = PhoenixUtil.readInt32(bidBuffer, offset); + offset += 4; + + askList = new ArrayList<>(); + askListSanitized = new ArrayList<>(); + + List> freeListPointersList = new ArrayList<>(); + + for (int index = 0; offset < bidBuffer.length && index < bumpIndex; index++) { + List registers = new ArrayList<>(); + for (int i = 0; i < 4; i++) { + registers.add(PhoenixUtil.readInt32(bidBuffer, offset)); + offset += 4; + } + + FIFOOrderId fifoOrderId = FIFOOrderId.readFifoOrderId( + Arrays.copyOfRange(bidBuffer, offset, offset + 16) + ); + offset += FIFOOrderId.FIFO_ORDER_ID_SIZE; + + FIFORestingOrder fifoRestingOrder = FIFORestingOrder.readFifoRestingOrder( + Arrays.copyOfRange(bidBuffer, offset, offset + 32) + ); + offset += FIFORestingOrder.FIFO_RESTING_ORDER_SIZE; + + askList.add(new Pair<>(fifoOrderId, fifoRestingOrder)); + freeListPointersList.add(new Pair<>(index, registers.get(0))); + } + + Set freeNodes = new HashSet<>(); + int indexToRemove = freeListHead - 1; + int counter = 0; + + while (freeListHead != 0) { + var next = freeListPointersList.get(freeListHead - 1); + indexToRemove = next.component1(); + freeListHead = next.component2(); + + freeNodes.add(indexToRemove); + counter += 1; + + if (counter > bumpIndex) { + log.error("Infinite Loop Detected"); + } + } + + var askOrdersList = askList; + for (int i = 0; i < askList.size(); i++) { + Pair entry = askOrdersList.get(i); + if (!freeNodes.contains(i)) { + // tree.set kv + askListSanitized.add(entry); + } + } + } } diff --git a/phoenix/src/main/java/com/mmorrell/phoenix/model/PhoenixTraderState.java b/phoenix/src/main/java/com/mmorrell/phoenix/model/PhoenixTraderState.java new file mode 100644 index 0000000..77b31c6 --- /dev/null +++ b/phoenix/src/main/java/com/mmorrell/phoenix/model/PhoenixTraderState.java @@ -0,0 +1,41 @@ +package com.mmorrell.phoenix.model; + +import lombok.Builder; +import lombok.Data; +import org.bitcoinj.core.Utils; + +/** + * export const traderStateBeet = new beet.BeetArgsStruct( + * [ + * ["quoteLotsLocked", beet.u64], + * ["quoteLotsFree", beet.u64], + * ["baseLotsLocked", beet.u64], + * ["baseLotsFree", beet.u64], + * ["padding", beet.uniformFixedSizeArray(beet.u64, 8)], + * ], + * "TraderState" + * ); + */ +@Data +@Builder +public class PhoenixTraderState { + + public static final int PHOENIX_TRADER_STATE_SIZE = 32 + 64; + + private long quoteLotsLocked; + private long quoteLotsFree; + private long baseLotsLocked; + private long baseLotsFree; + + public static PhoenixTraderState readPhoenixTraderState(byte[] data) { + return PhoenixTraderState.builder() + .quoteLotsLocked(Utils.readInt64(data, 0)) + .quoteLotsFree(Utils.readInt64(data, 8)) + .baseLotsLocked(Utils.readInt64(data, 16)) + .baseLotsFree(Utils.readInt64(data, 24)) + .build(); + } + + // + 64 bytes of padding + +} diff --git a/phoenix/src/test/java/PhoenixTest.java b/phoenix/src/test/java/PhoenixTest.java index 514bb93..8e72570 100644 --- a/phoenix/src/test/java/PhoenixTest.java +++ b/phoenix/src/test/java/PhoenixTest.java @@ -142,6 +142,15 @@ public void phoenixGetMarketDetailTest() throws RpcException, IOException { (double) fifoOrderIdFIFORestingOrderPair.getFirst().getPriceInTicks() / phoenixMarket.getTickSizeInQuoteLotsPerBaseUnit(), (double) fifoOrderIdFIFORestingOrderPair.getSecond().getNumBaseLots() / phoenixMarket.getBaseLotsPerBaseUnit())); }); + + var asks = phoenixMarket.getAskListSanitized().stream().sorted( + (o1, o2) -> Math.toIntExact(o1.component1().getPriceInTicks() - o2.getFirst().getPriceInTicks()) + ).toList(); + asks.forEach(fifoOrderIdFIFORestingOrderPair -> { + log.info(String.format("Ask: $%.2f, Size: %.2f SOL", + (double) fifoOrderIdFIFORestingOrderPair.getFirst().getPriceInTicks() / phoenixMarket.getTickSizeInQuoteLotsPerBaseUnit(), + (double) fifoOrderIdFIFORestingOrderPair.getSecond().getNumBaseLots() / phoenixMarket.getBaseLotsPerBaseUnit())); + }); } @Test