diff options
Diffstat (limited to 'ui-console/src/main/java/market/guess')
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 @@ | |||
| 1 | package market.guess.ui.console; | 1 | package market.guess.ui.console; |
| 2 | 2 | ||
| 3 | import java.time.Clock; | ||
| 3 | import market.guess.api.CatalogContext; | 4 | import market.guess.api.CatalogContext; |
| 4 | import market.guess.api.GuessMarketContext; | 5 | import market.guess.api.GuessMarketContext; |
| 5 | import market.guess.service.LocalGuessMarketContext; | 6 | import market.guess.service.LocalGuessMarketContext; |
| 6 | import market.guess.service.catalog.LocalCatalogContext; | 7 | import market.guess.service.catalog.LocalCatalogContext; |
| 7 | import market.guess.service.infrastructure.MarketContext; | 8 | import market.guess.service.catalog.infrastructure.MarketContext; |
| 8 | import market.guess.service.infrastructure.mapper.v1.EventMapperV1; | 9 | import market.guess.service.catalog.infrastructure.mapper.v1.EventMapperV1; |
| 9 | import market.guess.service.infrastructure.provider.Loader; | 10 | import market.guess.service.catalog.infrastructure.provider.Loader; |
| 10 | import market.guess.service.infrastructure.provider.v1.XMLLoaderV1; | 11 | import market.guess.service.catalog.infrastructure.provider.v1.XMLLoaderV1; |
| 11 | import market.guess.service.infrastructure.repository.EventRepository; | 12 | import market.guess.service.catalog.infrastructure.repository.EventRepository; |
| 12 | import market.guess.service.infrastructure.repository.InMemoryEventRepository; | 13 | import market.guess.service.catalog.infrastructure.repository.InMemoryEventRepository; |
| 13 | import market.guess.service.infrastructure.repository.InMemoryUserRepository; | 14 | import market.guess.service.catalog.infrastructure.repository.InMemoryUserRepository; |
| 14 | import market.guess.service.infrastructure.repository.UserRepository; | 15 | import market.guess.service.catalog.infrastructure.repository.UserRepository; |
| 16 | import market.guess.service.fulfillment.FulfillmentContext; | ||
| 17 | import market.guess.service.fulfillment.LocalFulfillmentContext; | ||
| 18 | import market.guess.service.ledger.LedgerContext; | ||
| 19 | import market.guess.service.matching.LocalMatchingEngine; | ||
| 20 | import market.guess.service.matching.MatchingEngine; | ||
| 21 | import market.guess.service.risk.LocalRiskEngine; | ||
| 22 | import market.guess.service.risk.RiskEngine; | ||
| 23 | import market.guess.service.settlement.LocalSettlementContext; | ||
| 24 | import market.guess.service.settlement.SettlementContext; | ||
| 15 | import market.guess.ui.console.command.BuyMenuCommand; | 25 | import market.guess.ui.console.command.BuyMenuCommand; |
| 16 | import market.guess.ui.console.command.EventDetailsMenuCommand; | 26 | import market.guess.ui.console.command.EventDetailsMenuCommand; |
| 17 | import market.guess.ui.console.command.ExitMenuCommand; | 27 | import 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 @@ | |||
| 1 | package market.guess.ui.console.command; | 1 | package market.guess.ui.console.command; |
| 2 | 2 | ||
| 3 | import java.math.BigDecimal; | ||
| 3 | import market.guess.api.CatalogContext; | 4 | import market.guess.api.CatalogContext; |
| 4 | import market.guess.api.GuessMarketContext; | 5 | import market.guess.api.GuessMarketContext; |
| 5 | import market.guess.model.event.EventStatus; | 6 | import 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 @@ | |||
| 1 | package market.guess.ui.console.command; | 1 | package market.guess.ui.console.command; |
| 2 | 2 | ||
| 3 | import market.guess.api.CatalogContext; | ||
| 3 | import market.guess.api.GuessMarketContext; | 4 | import market.guess.api.GuessMarketContext; |
| 5 | import market.guess.model.event.EventStatus; | ||
| 4 | import market.guess.ui.console.io.InputProcessor; | 6 | import market.guess.ui.console.io.InputProcessor; |
| 5 | 7 | ||
| 6 | public final class SettleEventMenuCommand implements MenuCommand { | 8 | public 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; | |||
| 5 | import java.util.function.Function; | 5 | import java.util.function.Function; |
| 6 | import market.guess.api.MarketStateDTO; | 6 | import market.guess.api.MarketStateDTO; |
| 7 | import market.guess.api.PurchaseReceiptDTO; | 7 | import market.guess.api.PurchaseReceiptDTO; |
| 8 | import market.guess.api.TradeRowDTO; | ||
| 8 | 9 | ||
| 9 | public final class InputProcessor { | 10 | public 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 | } |