From 0247f3f5068b96df52a4f9424578d4c66ca6975c Mon Sep 17 00:00:00 2001 From: Kostya Date: Sat, 15 Aug 2026 16:25:40 +0000 Subject: Implement LMSR and order-book trading logic, wire MarketContext facade and new menu commands Fill in the previously stubbed cost/pricing math for both trading mechanisms, and give OrderBookTradingMechanism an actual matching engine with resting bid/ask books. Fold EventRepository/UserRepository access into a single MarketContext used by LocalGuessMarketContext, and move the repository package under infrastructure. Add Buy/EventDetails/LoadState/ SaveState/SettleEvent console commands and wire them into App/Menu. --- .../mechanism/LmsrTradingMechanismTest.java | 37 ++++++++++++++++ .../mechanism/OrderBookTradingMechanismTest.java | 51 ++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 service/src/test/java/market/guess/service/mechanism/LmsrTradingMechanismTest.java create mode 100644 service/src/test/java/market/guess/service/mechanism/OrderBookTradingMechanismTest.java (limited to 'service/src/test/java/market') diff --git a/service/src/test/java/market/guess/service/mechanism/LmsrTradingMechanismTest.java b/service/src/test/java/market/guess/service/mechanism/LmsrTradingMechanismTest.java new file mode 100644 index 0000000..f5c6af5 --- /dev/null +++ b/service/src/test/java/market/guess/service/mechanism/LmsrTradingMechanismTest.java @@ -0,0 +1,37 @@ +package market.guess.service.mechanism; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +import java.math.BigDecimal; +import org.junit.Test; + +public class LmsrTradingMechanismTest { + + @Test + public void openingCostMatchesLmsrFormula() { + var mechanism = new LmsrTradingMechanism(100, 2); + + assertEquals(new BigDecimal("69.31"), mechanism.openingCost()); + } + + @Test + public void pricesStartEven() { + var mechanism = new LmsrTradingMechanism(100, 2); + + assertArrayEquals( + new BigDecimal[] {new BigDecimal("0.50"), new BigDecimal("0.50")}, mechanism.prices()); + } + + @Test + public void buyingSharesShiftsCostAndPrice() { + var mechanism = new LmsrTradingMechanism(100, 2); + + var execution = mechanism.buy(0, 100); + + assertEquals(100, execution.filledQuantity()); + assertEquals(new BigDecimal("62.01"), execution.cost()); + assertArrayEquals( + new BigDecimal[] {new BigDecimal("0.73"), new BigDecimal("0.27")}, mechanism.prices()); + } +} diff --git a/service/src/test/java/market/guess/service/mechanism/OrderBookTradingMechanismTest.java b/service/src/test/java/market/guess/service/mechanism/OrderBookTradingMechanismTest.java new file mode 100644 index 0000000..b4b2da3 --- /dev/null +++ b/service/src/test/java/market/guess/service/mechanism/OrderBookTradingMechanismTest.java @@ -0,0 +1,51 @@ +package market.guess.service.mechanism; + +import static org.junit.Assert.assertEquals; + +import java.math.BigDecimal; +import market.guess.service.mechanism.OrderBookTradingMechanism.Side; +import org.junit.Test; + +public class OrderBookTradingMechanismTest { + + @Test + public void limitBuyConsumesRestingAsksInFifoOrderAndLeavesRemainder() { + var mechanism = new OrderBookTradingMechanism(false, 100, 1, 2); + var price = new BigDecimal("0.60"); + + mechanism.placeOrder(0, Side.SELL, 20, price); + mechanism.placeOrder(0, Side.SELL, 40, price); + + var execution = mechanism.placeOrder(0, Side.BUY, 50, price); + + assertEquals(50, execution.filledQuantity()); + assertEquals(new BigDecimal("30.00"), execution.cost()); + + var followUp = mechanism.buy(0, 100); + assertEquals(10, followUp.filledQuantity()); + } + + @Test + public void marketBuySweepsMultiplePriceLevelsAndPartiallyFillsWhenBookRunsOut() { + var mechanism = new OrderBookTradingMechanism(false, 100, 1, 2); + mechanism.placeOrder(0, Side.SELL, 10, new BigDecimal("0.50")); + mechanism.placeOrder(0, Side.SELL, 10, new BigDecimal("0.70")); + + var execution = mechanism.buy(0, 15); + + assertEquals(15, execution.filledQuantity()); + assertEquals(new BigDecimal("8.50"), execution.cost()); + + var exhausted = mechanism.buy(0, 100); + assertEquals(5, exhausted.filledQuantity()); + } + + @Test + public void pricesReflectMidpointOfBestBidAndAsk() { + var mechanism = new OrderBookTradingMechanism(false, 100, 1, 2); + mechanism.placeOrder(0, Side.BUY, 10, new BigDecimal("0.40")); + mechanism.placeOrder(0, Side.SELL, 10, new BigDecimal("0.60")); + + assertEquals(new BigDecimal("0.50"), mechanism.prices()[0]); + } +} -- cgit v1.2.3