summaryrefslogtreecommitdiffstats
path: root/ui-console/src
diff options
context:
space:
mode:
authorKostya <mail@sartin.in>2026-08-17 11:15:59 +0000
committerKostya <mail@sartin.in>2026-08-17 11:15:59 +0000
commit13a221ffaefc169d028c7eab6f21ac88052d1d40 (patch)
tree51e4fd676ba043de73e967b854be683c5836ef24 /ui-console/src
parenteed9265be711432f40247f149c07f9e33511192a (diff)
downloadguess-market-13a221ffaefc169d028c7eab6f21ac88052d1d40.tar.gz
guess-market-13a221ffaefc169d028c7eab6f21ac88052d1d40.tar.xz
guess-market-13a221ffaefc169d028c7eab6f21ac88052d1d40.zip
Replace order matching with risk/matching/fulfillment/settlement pipeline
Move XML loading infrastructure and models under service/catalog, and split order execution into a real pipeline: RiskEngine checks the order, MatchingEngine matches it, FulfillmentContext books the resulting trades, and SettlementContext resolves winning markets. GuessMarketContext.buyShares becomes placeOrder(price, quantity), and TradingMechanism.buy now returns matched trades directly instead of a single TradeExecution.
Diffstat (limited to 'ui-console/src')
-rw-r--r--ui-console/src/main/java/market/guess/ui/console/App.java33
-rw-r--r--ui-console/src/main/java/market/guess/ui/console/command/BuyMenuCommand.java5
-rw-r--r--ui-console/src/main/java/market/guess/ui/console/command/EventDetailsMenuCommand.java1
-rw-r--r--ui-console/src/main/java/market/guess/ui/console/command/LoadFileMenuCommand.java7
-rw-r--r--ui-console/src/main/java/market/guess/ui/console/command/SettleEventMenuCommand.java46
-rw-r--r--ui-console/src/main/java/market/guess/ui/console/io/InputProcessor.java77
6 files changed, 130 insertions, 39 deletions
diff --git a/ui-console/src/main/java/market/guess/ui/console/App.java b/ui-console/src/main/java/market/guess/ui/console/App.java
index a628a36..5ee5aaf 100644
--- a/ui-console/src/main/java/market/guess/ui/console/App.java
+++ b/ui-console/src/main/java/market/guess/ui/console/App.java
@@ -1,17 +1,27 @@
1package market.guess.ui.console; 1package market.guess.ui.console;
2 2
3import java.time.Clock;
3import market.guess.api.CatalogContext; 4import market.guess.api.CatalogContext;
4import market.guess.api.GuessMarketContext; 5import market.guess.api.GuessMarketContext;
5import market.guess.service.LocalGuessMarketContext; 6import market.guess.service.LocalGuessMarketContext;
6import market.guess.service.catalog.LocalCatalogContext; 7import market.guess.service.catalog.LocalCatalogContext;
7import market.guess.service.infrastructure.MarketContext; 8import market.guess.service.catalog.infrastructure.MarketContext;
8import market.guess.service.infrastructure.mapper.v1.EventMapperV1; 9import market.guess.service.catalog.infrastructure.mapper.v1.EventMapperV1;
9import market.guess.service.infrastructure.provider.Loader; 10import market.guess.service.catalog.infrastructure.provider.Loader;
10import market.guess.service.infrastructure.provider.v1.XMLLoaderV1; 11import market.guess.service.catalog.infrastructure.provider.v1.XMLLoaderV1;
11import market.guess.service.infrastructure.repository.EventRepository; 12import market.guess.service.catalog.infrastructure.repository.EventRepository;
12import market.guess.service.infrastructure.repository.InMemoryEventRepository; 13import market.guess.service.catalog.infrastructure.repository.InMemoryEventRepository;
13import market.guess.service.infrastructure.repository.InMemoryUserRepository; 14import market.guess.service.catalog.infrastructure.repository.InMemoryUserRepository;
14import market.guess.service.infrastructure.repository.UserRepository; 15import market.guess.service.catalog.infrastructure.repository.UserRepository;
16import market.guess.service.fulfillment.FulfillmentContext;
17import market.guess.service.fulfillment.LocalFulfillmentContext;
18import market.guess.service.ledger.LedgerContext;
19import market.guess.service.matching.LocalMatchingEngine;
20import market.guess.service.matching.MatchingEngine;
21import market.guess.service.risk.LocalRiskEngine;
22import market.guess.service.risk.RiskEngine;
23import market.guess.service.settlement.LocalSettlementContext;
24import market.guess.service.settlement.SettlementContext;
15import market.guess.ui.console.command.BuyMenuCommand; 25import market.guess.ui.console.command.BuyMenuCommand;
16import market.guess.ui.console.command.EventDetailsMenuCommand; 26import market.guess.ui.console.command.EventDetailsMenuCommand;
17import market.guess.ui.console.command.ExitMenuCommand; 27import market.guess.ui.console.command.ExitMenuCommand;
@@ -35,6 +45,13 @@ public class App {
35 pico.addComponent(Loader.class, XMLLoaderV1.class); 45 pico.addComponent(Loader.class, XMLLoaderV1.class);
36 pico.addComponent(GuessMarketContext.class, LocalGuessMarketContext.class); 46 pico.addComponent(GuessMarketContext.class, LocalGuessMarketContext.class);
37 pico.addComponent(CatalogContext.class, LocalCatalogContext.class); 47 pico.addComponent(CatalogContext.class, LocalCatalogContext.class);
48 pico.addComponent(LedgerContext.class);
49 pico.addComponent(RiskEngine.class, LocalRiskEngine.class);
50 pico.addComponent(MatchingEngine.class, LocalMatchingEngine.class);
51 pico.addComponent(FulfillmentContext.class, LocalFulfillmentContext.class);
52 pico.addComponent(SettlementContext.class, LocalSettlementContext.class);
53
54 pico.addComponent(Clock.class, Clock.systemUTC());
38 55
39 pico.addComponent(LoadFileMenuCommand.class); 56 pico.addComponent(LoadFileMenuCommand.class);
40 pico.addComponent(ListEventsMenuCommand.class); 57 pico.addComponent(ListEventsMenuCommand.class);
diff --git a/ui-console/src/main/java/market/guess/ui/console/command/BuyMenuCommand.java b/ui-console/src/main/java/market/guess/ui/console/command/BuyMenuCommand.java
index 87da340..bee2351 100644
--- a/ui-console/src/main/java/market/guess/ui/console/command/BuyMenuCommand.java
+++ b/ui-console/src/main/java/market/guess/ui/console/command/BuyMenuCommand.java
@@ -1,5 +1,6 @@
1package market.guess.ui.console.command; 1package market.guess.ui.console.command;
2 2
3import java.math.BigDecimal;
3import market.guess.api.CatalogContext; 4import market.guess.api.CatalogContext;
4import market.guess.api.GuessMarketContext; 5import market.guess.api.GuessMarketContext;
5import market.guess.model.event.EventStatus; 6import market.guess.model.event.EventStatus;
@@ -63,7 +64,9 @@ public final class BuyMenuCommand implements MenuCommand {
63 64
64 var amount = io.readInt("How many shares? ", 1, Integer.MAX_VALUE); 65 var amount = io.readInt("How many shares? ", 1, Integer.MAX_VALUE);
65 66
66 var buyResult = context.buyShares("Tester", event.summary().key(), market.key(), amount); 67 var buyResult =
68 context.placeOrder(
69 "Tester", event.summary().key(), market.key(), new BigDecimal(market.price()), amount);
67 if (buyResult.isSuccess()) { 70 if (buyResult.isSuccess()) {
68 io.printReceipt(buyResult.getData()); 71 io.printReceipt(buyResult.getData());
69 return; 72 return;
diff --git a/ui-console/src/main/java/market/guess/ui/console/command/EventDetailsMenuCommand.java b/ui-console/src/main/java/market/guess/ui/console/command/EventDetailsMenuCommand.java
index 881acfa..c9e7585 100644
--- a/ui-console/src/main/java/market/guess/ui/console/command/EventDetailsMenuCommand.java
+++ b/ui-console/src/main/java/market/guess/ui/console/command/EventDetailsMenuCommand.java
@@ -51,5 +51,6 @@ public final class EventDetailsMenuCommand implements MenuCommand {
51 } 51 }
52 52
53 io.printState(event.state()); 53 io.printState(event.state());
54 io.printHistory(event.history());
54 } 55 }
55} 56}
diff --git a/ui-console/src/main/java/market/guess/ui/console/command/LoadFileMenuCommand.java b/ui-console/src/main/java/market/guess/ui/console/command/LoadFileMenuCommand.java
index 46312e4..024f1f7 100644
--- a/ui-console/src/main/java/market/guess/ui/console/command/LoadFileMenuCommand.java
+++ b/ui-console/src/main/java/market/guess/ui/console/command/LoadFileMenuCommand.java
@@ -25,11 +25,8 @@ public final class LoadFileMenuCommand implements MenuCommand {
25 25
26 @Override 26 @Override
27 public void execute() { 27 public void execute() {
28 // var path = io.readPath("Pleae enter the full file path to the XML file: "); 28 var path = io.readPath("Pleae enter the full file path to the XML file: ");
29 // var result = context.loadEvents(Path.of(path)); 29 var result = catalog.loadEvents(Path.of(path));
30 var result =
31 catalog.loadEvents(
32 Path.of("/home/hex-dev/projects/guess-market/test-data/ex-1/multiple.xml"));
33 30
34 if (result.isSuccess()) { 31 if (result.isSuccess()) {
35 io.println( 32 io.println(
diff --git a/ui-console/src/main/java/market/guess/ui/console/command/SettleEventMenuCommand.java b/ui-console/src/main/java/market/guess/ui/console/command/SettleEventMenuCommand.java
index b7344e3..8cc7b02 100644
--- a/ui-console/src/main/java/market/guess/ui/console/command/SettleEventMenuCommand.java
+++ b/ui-console/src/main/java/market/guess/ui/console/command/SettleEventMenuCommand.java
@@ -1,14 +1,19 @@
1package market.guess.ui.console.command; 1package market.guess.ui.console.command;
2 2
3import market.guess.api.CatalogContext;
3import market.guess.api.GuessMarketContext; 4import market.guess.api.GuessMarketContext;
5import market.guess.model.event.EventStatus;
4import market.guess.ui.console.io.InputProcessor; 6import market.guess.ui.console.io.InputProcessor;
5 7
6public final class SettleEventMenuCommand implements MenuCommand { 8public final class SettleEventMenuCommand implements MenuCommand {
7 private final GuessMarketContext context; 9 private final GuessMarketContext context;
10 private final CatalogContext catalog;
8 private final InputProcessor io; 11 private final InputProcessor io;
9 12
10 public SettleEventMenuCommand(GuessMarketContext context, InputProcessor io) { 13 public SettleEventMenuCommand(
14 GuessMarketContext context, CatalogContext catalog, InputProcessor io) {
11 this.context = context; 15 this.context = context;
16 this.catalog = catalog;
12 this.io = io; 17 this.io = io;
13 } 18 }
14 19
@@ -24,7 +29,42 @@ public final class SettleEventMenuCommand implements MenuCommand {
24 29
25 @Override 30 @Override
26 public void execute() { 31 public void execute() {
27 // TODO Auto-generated method stub 32 var result = catalog.getAllEvents();
28 throw new UnsupportedOperationException("Unimplemented method 'execute'"); 33
34 if (!result.isSuccess()) {
35 io.println("Failed to list events");
36 return;
37 }
38
39 var events = result.getData();
40 var active = events.stream().filter(event -> event.status() == EventStatus.ACTIVE).toList();
41
42 if (active.isEmpty()) {
43 io.println("There are currently no active events to settle.");
44 return;
45 }
46
47 var pick = io.readSelect("Pick an event: ", active, event -> "%s".formatted(event.name()));
48
49 var result2 = catalog.getEvent(pick.key());
50 var event = result2.getData();
51 if (event == null) {
52 io.println("That event is no longer available.");
53 return;
54 }
55
56 io.printState(event.state());
57
58 var winner =
59 io.readSelect("Which option won? ", event.state().markets(), o -> "%s".formatted(o.name()));
60
61 var settleResult = context.settleEvent("Tester", event.summary().key(), winner.key());
62 if (settleResult.isSuccess()) {
63 io.newLine();
64 io.println("Settled. Winning option: %s".formatted(settleResult.getData().winningOption()));
65 return;
66 }
67 io.newLine();
68 io.println("Settlement failed: %s".formatted(settleResult.getMessage()));
29 } 69 }
30} 70}
diff --git a/ui-console/src/main/java/market/guess/ui/console/io/InputProcessor.java b/ui-console/src/main/java/market/guess/ui/console/io/InputProcessor.java
index d644a9b..7587768 100644
--- a/ui-console/src/main/java/market/guess/ui/console/io/InputProcessor.java
+++ b/ui-console/src/main/java/market/guess/ui/console/io/InputProcessor.java
@@ -5,8 +5,16 @@ import java.util.Scanner;
5import java.util.function.Function; 5import java.util.function.Function;
6import market.guess.api.MarketStateDTO; 6import market.guess.api.MarketStateDTO;
7import market.guess.api.PurchaseReceiptDTO; 7import market.guess.api.PurchaseReceiptDTO;
8import market.guess.api.TradeRowDTO;
8 9
9public final class InputProcessor { 10public final class InputProcessor {
11 private static final int OPTION_WIDTH = 18;
12 private static final int VALUE_WIDTH = 10;
13 private static final int SHARES_WIDTH = 8;
14 private static final int QTY_WIDTH = 6;
15 private static final int PAID_WIDTH = 8;
16 private static final int AT_WIDTH = 20;
17
10 private final Scanner scanner = new Scanner(System.in); 18 private final Scanner scanner = new Scanner(System.in);
11 19
12 public void print(Object object) { 20 public void print(Object object) {
@@ -28,34 +36,24 @@ public final class InputProcessor {
28 36
29 public void printState(MarketStateDTO state) { 37 public void printState(MarketStateDTO state) {
30 newLine(); 38 newLine();
31 println( 39 println(marketRow("Option", "Value", "Shares"));
32 "%s|%s%s|%s" 40 println("-".repeat(OPTION_WIDTH + VALUE_WIDTH + SHARES_WIDTH + 2));
33 .formatted(
34 padAround("Option", 18), space(10), padLeft("Value", 8), padRight("Shares", 8)));
35
36 println("-".repeat(46));
37 41
38 for (var market : state.markets()) { 42 for (var market : state.markets()) {
39 println( 43 println(marketRow(market.name(), market.price(), market.volume()));
40 "%s|%s%s|%s"
41 .formatted(
42 padAround(market.name(), 18),
43 space(10),
44 padLeft(market.price(), 8),
45 padRight(market.volume(), 8)));
46 } 44 }
47 newLine(); 45 newLine();
48 46
49 println("Event ledger balance: ".formatted(state.accountBalance())); 47 println("Event ledger balance: %s".formatted(state.accountBalance()));
50 println("Commission collected: ".formatted(state.commissionCollected())); 48 println("Commission collected: %s".formatted(state.commissionCollected()));
51 } 49 }
52 50
53 private static String space(int width) { 51 private static String marketRow(String option, String value, String shares) {
54 return " ".repeat(width); 52 return "%s|%s|%s"
55 } 53 .formatted(
56 54 padRight(option, OPTION_WIDTH),
57 private static String padAround(String input, int width) { 55 padLeft(value, VALUE_WIDTH),
58 return input.length() >= width ? input : padRight(padLeft(input, width / 2), width / 2); 56 padLeft(shares, SHARES_WIDTH));
59 } 57 }
60 58
61 private static String padRight(String input, int width) { 59 private static String padRight(String input, int width) {
@@ -141,6 +139,41 @@ public final class InputProcessor {
141 } 139 }
142 140
143 public void printReceipt(PurchaseReceiptDTO receipt) { 141 public void printReceipt(PurchaseReceiptDTO receipt) {
144 println(receipt); 142 newLine();
143 println("Purchase confirmed:");
144 println(tradeRow("Option", "Qty", "Paid", "At"));
145 println("-".repeat(OPTION_WIDTH + QTY_WIDTH + PAID_WIDTH + AT_WIDTH + 3));
146
147 for (var trade : receipt.trades()) {
148 println(tradeRow(trade.optionName(), trade.quantity(), trade.pricePaid(), trade.at()));
149 }
150
151 printState(receipt.stateAfter());
152 }
153
154 public void printHistory(List<TradeRowDTO> history) {
155 newLine();
156 println("Trade history (newest first):");
157
158 if (history.isEmpty()) {
159 println("No trades yet.");
160 return;
161 }
162
163 println(tradeRow("Option", "Qty", "Paid", "At"));
164 println("-".repeat(OPTION_WIDTH + QTY_WIDTH + PAID_WIDTH + AT_WIDTH + 3));
165
166 for (var trade : history) {
167 println(tradeRow(trade.optionName(), trade.quantity(), trade.pricePaid(), trade.at()));
168 }
169 }
170
171 private static String tradeRow(String option, String qty, String paid, String at) {
172 return "%s|%s|%s|%s"
173 .formatted(
174 padRight(option, OPTION_WIDTH),
175 padLeft(qty, QTY_WIDTH),
176 padLeft(paid, PAID_WIDTH),
177 padRight(at, AT_WIDTH));
145 } 178 }
146} 179}