aboutsummaryrefslogtreecommitdiffstats
path: root/service/src/main/java/market/guess
diff options
context:
space:
mode:
Diffstat (limited to 'service/src/main/java/market/guess')
-rw-r--r--service/src/main/java/market/guess/service/LocalGuessMarketContext.java68
-rw-r--r--service/src/main/java/market/guess/service/TradingOperation.java13
-rw-r--r--service/src/main/java/market/guess/service/TradingOperationImpl.java22
-rw-r--r--service/src/main/java/market/guess/service/catalog/LocalCatalogContext.java (renamed from service/src/main/java/market/guess/service/LocalCatalogContext.java)29
-rw-r--r--service/src/main/java/market/guess/service/domain/Event.java22
-rw-r--r--service/src/main/java/market/guess/service/domain/MarketAction.java6
-rw-r--r--service/src/main/java/market/guess/service/infrastructure/MarketContext.java18
-rw-r--r--service/src/main/java/market/guess/service/infrastructure/mapper/v1/EventMapperV1.java2
-rw-r--r--service/src/main/java/market/guess/service/infrastructure/mapper/v2/EventMapperV2.java2
-rw-r--r--service/src/main/java/market/guess/service/infrastructure/provider/LoadValidator.java22
-rw-r--r--service/src/main/java/market/guess/service/infrastructure/provider/Loader.java3
-rw-r--r--service/src/main/java/market/guess/service/infrastructure/provider/v1/XMLLoaderV1.java14
-rw-r--r--service/src/main/java/market/guess/service/ledger/LedgerContext.java2
-rw-r--r--service/src/main/java/market/guess/service/ledger/LedgerEntry.java2
-rw-r--r--service/src/main/java/market/guess/service/matching/OrderMatchingContext.java13
-rw-r--r--service/src/main/java/market/guess/service/mechanism/OrderBookTradingMechanism.java10
-rw-r--r--service/src/main/java/market/guess/service/mechanism/TradeExecution.java5
-rw-r--r--service/src/main/java/market/guess/service/mechanism/TradingMechanism.java3
18 files changed, 118 insertions, 138 deletions
diff --git a/service/src/main/java/market/guess/service/LocalGuessMarketContext.java b/service/src/main/java/market/guess/service/LocalGuessMarketContext.java
index 8f97432..3feef6a 100644
--- a/service/src/main/java/market/guess/service/LocalGuessMarketContext.java
+++ b/service/src/main/java/market/guess/service/LocalGuessMarketContext.java
@@ -1,25 +1,21 @@
1package market.guess.service; 1package market.guess.service;
2 2
3import java.nio.file.Path;
4import java.util.List;
5import market.guess.api.AccountDTO;
6import market.guess.api.EventDetailDTO;
7import market.guess.api.GuessMarketContext; 3import market.guess.api.GuessMarketContext;
8import market.guess.api.LoadResultDTO;
9import market.guess.api.ProblemDTO;
10import market.guess.api.PurchaseReceiptDTO; 4import market.guess.api.PurchaseReceiptDTO;
11import market.guess.api.Result; 5import market.guess.api.Result;
6import market.guess.api.TradeRowDTO;
7import market.guess.model.event.EventDetailDTO;
12import market.guess.service.infrastructure.MarketContext; 8import market.guess.service.infrastructure.MarketContext;
9import market.guess.service.matching.OrderMatchingContext;
13 10
14public final class LocalGuessMarketContext implements GuessMarketContext { 11public final class LocalGuessMarketContext implements GuessMarketContext {
15 12 private final OrderMatchingContext orderMatching;
16 private final TradingOperation tradingOperation;
17 private final MarketContext context; 13 private final MarketContext context;
18 14
19 public LocalGuessMarketContext(MarketContext context, TradingOperation tradingOperation) { 15 public LocalGuessMarketContext(MarketContext context, OrderMatchingContext orderMatching) {
20 super(); 16 super();
21 this.context = context; 17 this.context = context;
22 this.tradingOperation = tradingOperation; 18 this.orderMatching = orderMatching;
23 } 19 }
24 20
25 @Override 21 @Override
@@ -28,42 +24,46 @@ public final class LocalGuessMarketContext implements GuessMarketContext {
28 var optional = context.getEvents().get(eventKey); 24 var optional = context.getEvents().get(eventKey);
29 25
30 var userOptional = context.getUsers().get(userName); 26 var userOptional = context.getUsers().get(userName);
31 if (optional.isEmpty() || userOptional.isEmpty()) Result.error("Unable to find user/event."); 27 if (optional.isEmpty() || userOptional.isEmpty())
28 return Result.error("Unable to find user/event.");
32 29
33 var event = optional.get(); 30 var event = optional.get();
34 var user = userOptional.get(); 31 var user = userOptional.get();
35 var option = event.getOption(optionKey); 32 var option = event.getOption(optionKey);
36 var trade = tradingOperation.buy(event, user, option, quantity); 33
37 return Result.ok(new PurchaseReceiptDTO()); 34 var trades = orderMatching.submitOrder(user, event, option, quantity);
35 return Result.ok(
36 new PurchaseReceiptDTO(
37 trades.stream()
38 .map(
39 t ->
40 new TradeRowDTO(
41 t.at().toString(),
42 t.userName(),
43 t.optionName(),
44 String.valueOf(t.quantity()),
45 t.totalPaid().toPlainString()))
46 .toList(),
47 event.toMarketState()));
38 } 48 }
39 49
40 @Override 50 @Override
41 public Result<EventDetailDTO> closeEvent( 51 public Result<EventDetailDTO> settleEvent(
42 String userName, String eventKey, String winningOptionKey) { 52 String userName, String eventKey, String winningOptionKey) {
43 throw new UnsupportedOperationException("Unimplemented method 'account'"); 53 var optional = context.getEvents().get(eventKey);
44 }
45 54
46 @Override 55 if (optional.isEmpty()) return Result.error("Unknown event.");
47 public Result<AccountDTO> account(String userName) {
48 throw new UnsupportedOperationException("Unimplemented method 'account'");
49 }
50 56
51 @Override 57 var event = optional.get();
52 public Result<Void> saveState(Path path) { 58 if (event.canSettle(userName)) {
53 context.save();
54 return Result.ok();
55 }
56 59
57 @Override 60 var payout = event.getPayout();
58 public Result<LoadResultDTO> restoreState(Path path) {
59 61
60 try { 62 return Result.ok(
61 context.load(); 63 new EventDetailDTO(
62 } catch (Exception e) { 64 event.toEventSummary(), event.toMarketState(), event.getHistory(), ""));
63 return Result.error(
64 "Failed to load.",
65 LoadResultDTO.failure(path.toString(), List.of(new ProblemDTO("", e.getMessage(), ""))));
66 } 65 }
67 return Result.ok(LoadResultDTO.success(path.toString(), context.getEvents().getAll().size())); 66
67 return Result.error("Couldn't settle event.");
68 } 68 }
69} 69}
diff --git a/service/src/main/java/market/guess/service/TradingOperation.java b/service/src/main/java/market/guess/service/TradingOperation.java
deleted file mode 100644
index 63572c0..0000000
--- a/service/src/main/java/market/guess/service/TradingOperation.java
+++ /dev/null
@@ -1,13 +0,0 @@
1package market.guess.service;
2
3import market.guess.service.domain.Event;
4import market.guess.service.domain.Market;
5import market.guess.service.domain.Settlement;
6import market.guess.service.domain.Trade;
7import market.guess.service.domain.User;
8
9public interface TradingOperation {
10 Trade buy(Event event, User user, Market Option, int quantity);
11
12 Settlement settle(Event event, String winningOption);
13}
diff --git a/service/src/main/java/market/guess/service/TradingOperationImpl.java b/service/src/main/java/market/guess/service/TradingOperationImpl.java
deleted file mode 100644
index 641a3a9..0000000
--- a/service/src/main/java/market/guess/service/TradingOperationImpl.java
+++ /dev/null
@@ -1,22 +0,0 @@
1package market.guess.service;
2
3import market.guess.service.domain.Event;
4import market.guess.service.domain.Market;
5import market.guess.service.domain.Settlement;
6import market.guess.service.domain.Trade;
7import market.guess.service.domain.User;
8
9public final class TradingOperationImpl implements TradingOperation {
10
11 @Override
12 public Trade buy(Event event, User user, Market Option, int quantity) {
13 // TODO Auto-generated method stub
14 throw new UnsupportedOperationException("Unimplemented method 'buy'");
15 }
16
17 @Override
18 public Settlement settle(Event event, String winningOption) {
19 // TODO Auto-generated method stub
20 throw new UnsupportedOperationException("Unimplemented method 'settle'");
21 }
22}
diff --git a/service/src/main/java/market/guess/service/LocalCatalogContext.java b/service/src/main/java/market/guess/service/catalog/LocalCatalogContext.java
index 15a5890..6035ce7 100644
--- a/service/src/main/java/market/guess/service/LocalCatalogContext.java
+++ b/service/src/main/java/market/guess/service/catalog/LocalCatalogContext.java
@@ -1,18 +1,17 @@
1package market.guess.service; 1package market.guess.service.catalog;
2 2
3import java.nio.file.Path; 3import java.nio.file.Path;
4import java.util.List; 4import java.util.List;
5import market.guess.api.CatalogContext; 5import market.guess.api.CatalogContext;
6import market.guess.api.EventDetailDTO;
7import market.guess.api.EventSummaryDTO;
8import market.guess.api.LoadResultDTO; 6import market.guess.api.LoadResultDTO;
9import market.guess.api.Result; 7import market.guess.api.Result;
8import market.guess.model.event.EventDetailDTO;
9import market.guess.model.event.EventSummaryDTO;
10import market.guess.service.domain.Event; 10import market.guess.service.domain.Event;
11import market.guess.service.infrastructure.MarketContext; 11import market.guess.service.infrastructure.MarketContext;
12import market.guess.service.infrastructure.provider.Loader; 12import market.guess.service.infrastructure.provider.Loader;
13 13
14public final class LocalCatalogContext implements CatalogContext { 14public final class LocalCatalogContext implements CatalogContext {
15
16 private final MarketContext context; 15 private final MarketContext context;
17 private final Loader provider; 16 private final Loader provider;
18 17
@@ -24,7 +23,7 @@ public final class LocalCatalogContext implements CatalogContext {
24 23
25 @Override 24 @Override
26 public Result<LoadResultDTO> loadEvents(Path path) { 25 public Result<LoadResultDTO> loadEvents(Path path) {
27 return Result.ok(provider.load(path)); 26 return provider.load(path);
28 } 27 }
29 28
30 @Override 29 @Override
@@ -42,4 +41,24 @@ public final class LocalCatalogContext implements CatalogContext {
42 return Result.ok( 41 return Result.ok(
43 new EventDetailDTO(event.toEventSummary(), event.toMarketState(), event.getHistory(), "")); 42 new EventDetailDTO(event.toEventSummary(), event.toMarketState(), event.getHistory(), ""));
44 } 43 }
44
45 @Override
46 public Result<Void> saveState(Path path) {
47 try {
48 context.save();
49 } catch (Exception e) {
50 return Result.error("Failed to save.", e.getMessage());
51 }
52 return Result.ok();
53 }
54
55 @Override
56 public Result<LoadResultDTO> restoreState(Path path) {
57 try {
58 context.load();
59 } catch (Exception e) {
60 return Result.error("Failed to load.", e.getMessage());
61 }
62 return Result.ok(new LoadResultDTO(path.toString(), context.getEvents().getAll().size()));
63 }
45} 64}
diff --git a/service/src/main/java/market/guess/service/domain/Event.java b/service/src/main/java/market/guess/service/domain/Event.java
index 5c98908..943328a 100644
--- a/service/src/main/java/market/guess/service/domain/Event.java
+++ b/service/src/main/java/market/guess/service/domain/Event.java
@@ -5,10 +5,10 @@ import java.time.Instant;
5import java.util.ArrayList; 5import java.util.ArrayList;
6import java.util.List; 6import java.util.List;
7import market.guess.api.CommissionTiming; 7import market.guess.api.CommissionTiming;
8import market.guess.api.EventStatus;
9import market.guess.api.EventSummaryDTO;
10import market.guess.api.MarketStateDTO; 8import market.guess.api.MarketStateDTO;
11import market.guess.api.TradeRowDTO; 9import market.guess.api.TradeRowDTO;
10import market.guess.model.event.EventStatus;
11import market.guess.model.event.EventSummaryDTO;
12import market.guess.service.ledger.Ledger; 12import market.guess.service.ledger.Ledger;
13import market.guess.service.mechanism.TradingMechanism; 13import market.guess.service.mechanism.TradingMechanism;
14 14
@@ -78,16 +78,12 @@ public final class Event {
78 return commissionTiming; 78 return commissionTiming;
79 } 79 }
80 80
81 public TradingMechanism getMechanism() {
82 return mechanism;
83 }
84
85 public List<Market> getMarkets() { 81 public List<Market> getMarkets() {
86 return markets; 82 return markets;
87 } 83 }
88 84
89 public List<String> getOptionsNames() { 85 public List<String> getOptionsNames() {
90 return markets.stream().map(Market::getKey).toList(); 86 return markets.stream().map(Market::getName).toList();
91 } 87 }
92 88
93 public Ledger getLedger() { 89 public Ledger getLedger() {
@@ -190,4 +186,16 @@ public final class Event {
190 .findFirst() 186 .findFirst()
191 .orElseThrow(() -> new IllegalArgumentException("No such option.")); 187 .orElseThrow(() -> new IllegalArgumentException("No such option."));
192 } 188 }
189
190 public boolean canSettle(String userName) {
191 if (state != EventStatus.ACTIVE) return false;
192 if (marketMaker.equalsIgnoreCase(userName)) return true;
193
194 return false;
195 }
196
197 public BigDecimal getPayout() {
198 // TODO Auto-generated method stub
199 throw new UnsupportedOperationException("Unimplemented method 'getPayout'");
200 }
193} 201}
diff --git a/service/src/main/java/market/guess/service/domain/MarketAction.java b/service/src/main/java/market/guess/service/domain/MarketAction.java
index c0aabd1..d1a0ad5 100644
--- a/service/src/main/java/market/guess/service/domain/MarketAction.java
+++ b/service/src/main/java/market/guess/service/domain/MarketAction.java
@@ -5,7 +5,7 @@ public record MarketAction(
5 5
6 public static final String BUY = "BUY"; 6 public static final String BUY = "BUY";
7 public static final String SELL = "SELL"; 7 public static final String SELL = "SELL";
8 public static final String CLOSE = "CLOSE"; 8 public static final String SETTLE = "SETTLE";
9 9
10 public static MarketAction buy( 10 public static MarketAction buy(
11 String at, String userName, String eventKey, String optionKey, long quantity) { 11 String at, String userName, String eventKey, String optionKey, long quantity) {
@@ -17,8 +17,8 @@ public record MarketAction(
17 return new MarketAction(SELL, at, userName, eventKey, optionKey, quantity); 17 return new MarketAction(SELL, at, userName, eventKey, optionKey, quantity);
18 } 18 }
19 19
20 public static MarketAction close( 20 public static MarketAction settle(
21 String at, String userName, String eventKey, String winningOptionKey) { 21 String at, String userName, String eventKey, String winningOptionKey) {
22 return new MarketAction(CLOSE, at, userName, eventKey, winningOptionKey, 0); 22 return new MarketAction(SETTLE, at, userName, eventKey, winningOptionKey, 0);
23 } 23 }
24} 24}
diff --git a/service/src/main/java/market/guess/service/infrastructure/MarketContext.java b/service/src/main/java/market/guess/service/infrastructure/MarketContext.java
index d9fe3f8..7113803 100644
--- a/service/src/main/java/market/guess/service/infrastructure/MarketContext.java
+++ b/service/src/main/java/market/guess/service/infrastructure/MarketContext.java
@@ -10,6 +10,7 @@ import java.nio.file.Path;
10import java.nio.file.Paths; 10import java.nio.file.Paths;
11import java.time.Instant; 11import java.time.Instant;
12import java.util.List; 12import java.util.List;
13import market.guess.exception.GuessMarketException;
13import market.guess.service.domain.Event; 14import market.guess.service.domain.Event;
14import market.guess.service.domain.User; 15import market.guess.service.domain.User;
15import market.guess.service.infrastructure.adapter.InstantTypeAdapter; 16import market.guess.service.infrastructure.adapter.InstantTypeAdapter;
@@ -41,21 +42,19 @@ public final class MarketContext {
41 return users; 42 return users;
42 } 43 }
43 44
44 public void save() { 45 public void save() throws GuessMarketException {
45 var path = Paths.get("test-file.json"); 46 var path = Paths.get("test-file.json");
46 47
47 try { 48 try {
48 if (path.getParent() != null) Files.createDirectories(path.getParent()); 49 if (path.getParent() != null) Files.createDirectories(path.getParent());
49 Files.writeString( 50 Files.writeString(
50 path, 51 path, GSON.toJson(new Wrapper(events.getAll(), users.getAll())), StandardCharsets.UTF_8);
51 GSON.toJson(new Wrapper(events.getAll(), users.getAll())),
52 StandardCharsets.UTF_8);
53 } catch (IOException e) { 52 } catch (IOException e) {
54 53 throw new GuessMarketException("Failed to sove the file.");
55 } 54 }
56 } 55 }
57 56
58 public void load() { 57 public void load() throws GuessMarketException {
59 try { 58 try {
60 var wrapper = 59 var wrapper =
61 GSON.fromJson( 60 GSON.fromJson(
@@ -71,9 +70,10 @@ public final class MarketContext {
71 users.add(user); 70 users.add(user);
72 } 71 }
73 72
74 } catch (JsonSyntaxException | IOException e) { 73 } catch (JsonSyntaxException e) {
75 // TODO Auto-generated catch block 74 throw new GuessMarketException("The JSON file is malformed.");
76 e.printStackTrace(); 75 } catch (IOException e) {
76 throw new GuessMarketException("Unable the file.");
77 } 77 }
78 } 78 }
79} 79}
diff --git a/service/src/main/java/market/guess/service/infrastructure/mapper/v1/EventMapperV1.java b/service/src/main/java/market/guess/service/infrastructure/mapper/v1/EventMapperV1.java
index 7984c41..f049a0b 100644
--- a/service/src/main/java/market/guess/service/infrastructure/mapper/v1/EventMapperV1.java
+++ b/service/src/main/java/market/guess/service/infrastructure/mapper/v1/EventMapperV1.java
@@ -3,7 +3,7 @@ package market.guess.service.infrastructure.mapper.v1;
3import java.util.ArrayList; 3import java.util.ArrayList;
4import java.util.List; 4import java.util.List;
5import market.guess.api.CommissionTiming; 5import market.guess.api.CommissionTiming;
6import market.guess.api.EventStatus; 6import market.guess.model.event.EventStatus;
7import market.guess.service.domain.Event; 7import market.guess.service.domain.Event;
8import market.guess.service.domain.Market; 8import market.guess.service.domain.Market;
9import market.guess.service.infrastructure.mapper.Mapper; 9import market.guess.service.infrastructure.mapper.Mapper;
diff --git a/service/src/main/java/market/guess/service/infrastructure/mapper/v2/EventMapperV2.java b/service/src/main/java/market/guess/service/infrastructure/mapper/v2/EventMapperV2.java
index 0dd8e43..7037c02 100644
--- a/service/src/main/java/market/guess/service/infrastructure/mapper/v2/EventMapperV2.java
+++ b/service/src/main/java/market/guess/service/infrastructure/mapper/v2/EventMapperV2.java
@@ -3,7 +3,7 @@ package market.guess.service.infrastructure.mapper.v2;
3import java.util.ArrayList; 3import java.util.ArrayList;
4import java.util.List; 4import java.util.List;
5import market.guess.api.CommissionTiming; 5import market.guess.api.CommissionTiming;
6import market.guess.api.EventStatus; 6import market.guess.model.event.EventStatus;
7import market.guess.service.domain.Event; 7import market.guess.service.domain.Event;
8import market.guess.service.domain.Market; 8import market.guess.service.domain.Market;
9import market.guess.service.infrastructure.mapper.Mapper; 9import market.guess.service.infrastructure.mapper.Mapper;
diff --git a/service/src/main/java/market/guess/service/infrastructure/provider/LoadValidator.java b/service/src/main/java/market/guess/service/infrastructure/provider/LoadValidator.java
deleted file mode 100644
index c1c3895..0000000
--- a/service/src/main/java/market/guess/service/infrastructure/provider/LoadValidator.java
+++ /dev/null
@@ -1,22 +0,0 @@
1package market.guess.service.infrastructure.provider;
2
3import java.util.ArrayList;
4import java.util.Collections;
5import java.util.List;
6import market.guess.api.ProblemDTO;
7
8public final class LoadValidator {
9 private final List<ProblemDTO> problems = new ArrayList<>();
10
11 public void append(String code, String humanizedMessage, String location) {
12 problems.add(new ProblemDTO(code, humanizedMessage, location));
13 }
14
15 public boolean isValid() {
16 return problems.isEmpty();
17 }
18
19 public List<ProblemDTO> getIssues() {
20 return Collections.unmodifiableList(problems);
21 }
22}
diff --git a/service/src/main/java/market/guess/service/infrastructure/provider/Loader.java b/service/src/main/java/market/guess/service/infrastructure/provider/Loader.java
index 7588855..fcca92f 100644
--- a/service/src/main/java/market/guess/service/infrastructure/provider/Loader.java
+++ b/service/src/main/java/market/guess/service/infrastructure/provider/Loader.java
@@ -2,7 +2,8 @@ package market.guess.service.infrastructure.provider;
2 2
3import java.nio.file.Path; 3import java.nio.file.Path;
4import market.guess.api.LoadResultDTO; 4import market.guess.api.LoadResultDTO;
5import market.guess.api.Result;
5 6
6public interface Loader { 7public interface Loader {
7 LoadResultDTO load(Path path); 8 Result<LoadResultDTO> load(Path path);
8} 9}
diff --git a/service/src/main/java/market/guess/service/infrastructure/provider/v1/XMLLoaderV1.java b/service/src/main/java/market/guess/service/infrastructure/provider/v1/XMLLoaderV1.java
index 0d5bb9d..412a5d2 100644
--- a/service/src/main/java/market/guess/service/infrastructure/provider/v1/XMLLoaderV1.java
+++ b/service/src/main/java/market/guess/service/infrastructure/provider/v1/XMLLoaderV1.java
@@ -5,9 +5,9 @@ import jakarta.xml.bind.JAXBException;
5import java.nio.file.Files; 5import java.nio.file.Files;
6import java.nio.file.Path; 6import java.nio.file.Path;
7import market.guess.api.LoadResultDTO; 7import market.guess.api.LoadResultDTO;
8import market.guess.api.Result;
8import market.guess.service.domain.User; 9import market.guess.service.domain.User;
9import market.guess.service.infrastructure.mapper.v1.EventMapperV1; 10import market.guess.service.infrastructure.mapper.v1.EventMapperV1;
10import market.guess.service.infrastructure.provider.LoadValidator;
11import market.guess.service.infrastructure.provider.Loader; 11import market.guess.service.infrastructure.provider.Loader;
12import market.guess.service.infrastructure.repository.EventRepository; 12import market.guess.service.infrastructure.repository.EventRepository;
13import market.guess.service.infrastructure.repository.UserRepository; 13import market.guess.service.infrastructure.repository.UserRepository;
@@ -34,11 +34,11 @@ public final class XMLLoaderV1 implements Loader {
34 } 34 }
35 35
36 @Override 36 @Override
37 public LoadResultDTO load(Path path) { 37 public Result<LoadResultDTO> load(Path path) {
38 var validator = new LoadValidator(); 38
39 if (path.getFileName() == null 39 if (path.getFileName() == null
40 || !path.getFileName().toString().toLowerCase().endsWith(".xml")) { 40 || !path.getFileName().toString().toLowerCase().endsWith(".xml")) {
41 validator.append("NOT_XML", "The file must be an XML.", path.toString()); 41 return Result.error("NOT_XML", "The file must be an XML.");
42 } 42 }
43 43
44 try (var stream = Files.newInputStream(path)) { 44 try (var stream = Files.newInputStream(path)) {
@@ -51,11 +51,9 @@ public final class XMLLoaderV1 implements Loader {
51 51
52 userRepository.add(new User("Tester", 9_999_999)); 52 userRepository.add(new User("Tester", 9_999_999));
53 53
54 return LoadResultDTO.success(path.toString(), eventRepository.getAll().size()); 54 return Result.ok(new LoadResultDTO(path.toString(), eventRepository.getAll().size()));
55 } catch (Exception e) { 55 } catch (Exception e) {
56 validator.append("UNREADABLE", "The file could not be read.", path.toString()); 56 return Result.error("UNREADABLE", "The file could not be read.");
57 } 57 }
58
59 return LoadResultDTO.failure(path.toString(), validator.getIssues());
60 } 58 }
61} 59}
diff --git a/service/src/main/java/market/guess/service/ledger/LedgerContext.java b/service/src/main/java/market/guess/service/ledger/LedgerContext.java
index caecf55..12ec614 100644
--- a/service/src/main/java/market/guess/service/ledger/LedgerContext.java
+++ b/service/src/main/java/market/guess/service/ledger/LedgerContext.java
@@ -2,7 +2,7 @@ package market.guess.service.ledger;
2 2
3import java.math.BigDecimal; 3import java.math.BigDecimal;
4import java.time.Clock; 4import java.time.Clock;
5import market.guess.api.LedgerType; 5import market.guess.model.ledger.LedgerType;
6import market.guess.service.infrastructure.repository.UserRepository; 6import market.guess.service.infrastructure.repository.UserRepository;
7 7
8public final class LedgerContext { 8public final class LedgerContext {
diff --git a/service/src/main/java/market/guess/service/ledger/LedgerEntry.java b/service/src/main/java/market/guess/service/ledger/LedgerEntry.java
index 8008f20..79f12bf 100644
--- a/service/src/main/java/market/guess/service/ledger/LedgerEntry.java
+++ b/service/src/main/java/market/guess/service/ledger/LedgerEntry.java
@@ -2,7 +2,7 @@ package market.guess.service.ledger;
2 2
3import java.math.BigDecimal; 3import java.math.BigDecimal;
4import java.time.Instant; 4import java.time.Instant;
5import market.guess.api.LedgerType; 5import market.guess.model.ledger.LedgerType;
6 6
7public final record LedgerEntry( 7public final record LedgerEntry(
8 int id, 8 int id,
diff --git a/service/src/main/java/market/guess/service/matching/OrderMatchingContext.java b/service/src/main/java/market/guess/service/matching/OrderMatchingContext.java
new file mode 100644
index 0000000..8882432
--- /dev/null
+++ b/service/src/main/java/market/guess/service/matching/OrderMatchingContext.java
@@ -0,0 +1,13 @@
1package market.guess.service.matching;
2
3import java.util.List;
4import market.guess.service.domain.Event;
5import market.guess.service.domain.Market;
6import market.guess.service.domain.Trade;
7import market.guess.service.domain.User;
8
9public interface OrderMatchingContext {
10 List<Trade> submitOrder(User user, Event event, Market market, int quantity);
11
12 boolean cancelOrder();
13}
diff --git a/service/src/main/java/market/guess/service/mechanism/OrderBookTradingMechanism.java b/service/src/main/java/market/guess/service/mechanism/OrderBookTradingMechanism.java
index 91d5110..43f4843 100644
--- a/service/src/main/java/market/guess/service/mechanism/OrderBookTradingMechanism.java
+++ b/service/src/main/java/market/guess/service/mechanism/OrderBookTradingMechanism.java
@@ -6,6 +6,7 @@ import java.util.ArrayList;
6import java.util.List; 6import java.util.List;
7import market.guess.api.MechanismType; 7import market.guess.api.MechanismType;
8import market.guess.service.domain.BigDecimalOptions; 8import market.guess.service.domain.BigDecimalOptions;
9import market.guess.service.domain.Trade;
9 10
10public final class OrderBookTradingMechanism implements TradingMechanism { 11public final class OrderBookTradingMechanism implements TradingMechanism {
11 public enum Side { 12 public enum Side {
@@ -58,12 +59,12 @@ public final class OrderBookTradingMechanism implements TradingMechanism {
58 } 59 }
59 60
60 @Override 61 @Override
61 public TradeExecution buy(int optionIndex, int quantity) { 62 public Trade buy(int optionIndex, int quantity) {
62 return match(optionIndex, Side.BUY, quantity, null, false); 63 return match(optionIndex, Side.BUY, quantity, null, false);
63 } 64 }
64 65
65 /** Priced limit order — not on the shared interface, no engine call site yet. */ 66 /** Priced limit order — not on the shared interface, no engine call site yet. */
66 public TradeExecution placeOrder(int optionIndex, Side side, int quantity, BigDecimal price) { 67 public Trade placeOrder(int optionIndex, Side side, int quantity, BigDecimal price) {
67 if (price.signum() < 0 || price.compareTo(BigDecimal.valueOf(d)) > 0) { 68 if (price.signum() < 0 || price.compareTo(BigDecimal.valueOf(d)) > 0) {
68 throw new IllegalArgumentException("Price must be between 0 and " + d); 69 throw new IllegalArgumentException("Price must be between 0 and " + d);
69 } 70 }
@@ -97,7 +98,7 @@ public final class OrderBookTradingMechanism implements TradingMechanism {
97 return book.isEmpty() ? null : book.get(0).price; 98 return book.isEmpty() ? null : book.get(0).price;
98 } 99 }
99 100
100 private TradeExecution match( 101 private Trade match(
101 int optionIndex, Side side, int quantity, BigDecimal limitPrice, boolean restRemainder) { 102 int optionIndex, Side side, int quantity, BigDecimal limitPrice, boolean restRemainder) {
102 var opposing = side == Side.BUY ? asks.get(optionIndex) : bids.get(optionIndex); 103 var opposing = side == Side.BUY ? asks.get(optionIndex) : bids.get(optionIndex);
103 104
@@ -127,7 +128,8 @@ public final class OrderBookTradingMechanism implements TradingMechanism {
127 new RestingOrder(limitPrice, quantity - filled)); 128 new RestingOrder(limitPrice, quantity - filled));
128 } 129 }
129 130
130 return new TradeExecution(filled, BigDecimalOptions.toMoney(proceeds)); 131 return new Trade(
132 filled, null, null, null, null, filled, BigDecimalOptions.toMoney(proceeds), proceeds);
131 } 133 }
132 134
133 private static boolean crosses(Side side, BigDecimal restingPrice, BigDecimal limitPrice) { 135 private static boolean crosses(Side side, BigDecimal restingPrice, BigDecimal limitPrice) {
diff --git a/service/src/main/java/market/guess/service/mechanism/TradeExecution.java b/service/src/main/java/market/guess/service/mechanism/TradeExecution.java
deleted file mode 100644
index 3d8df1d..0000000
--- a/service/src/main/java/market/guess/service/mechanism/TradeExecution.java
+++ /dev/null
@@ -1,5 +0,0 @@
1package market.guess.service.mechanism;
2
3import java.math.BigDecimal;
4
5public record TradeExecution(int filledQuantity, BigDecimal cost) {}
diff --git a/service/src/main/java/market/guess/service/mechanism/TradingMechanism.java b/service/src/main/java/market/guess/service/mechanism/TradingMechanism.java
index fc915fb..204a170 100644
--- a/service/src/main/java/market/guess/service/mechanism/TradingMechanism.java
+++ b/service/src/main/java/market/guess/service/mechanism/TradingMechanism.java
@@ -2,6 +2,7 @@ package market.guess.service.mechanism;
2 2
3import java.math.BigDecimal; 3import java.math.BigDecimal;
4import market.guess.api.MechanismType; 4import market.guess.api.MechanismType;
5import market.guess.service.domain.Trade;
5 6
6public sealed interface TradingMechanism permits LmsrTradingMechanism, OrderBookTradingMechanism { 7public sealed interface TradingMechanism permits LmsrTradingMechanism, OrderBookTradingMechanism {
7 8
@@ -9,7 +10,7 @@ public sealed interface TradingMechanism permits LmsrTradingMechanism, OrderBook
9 10
10 BigDecimal openingCost(); 11 BigDecimal openingCost();
11 12
12 TradeExecution buy(int optionIndex, int quantity); 13 Trade buy(int optionIndex, int quantity);
13 14
14 BigDecimal[] prices(); 15 BigDecimal[] prices();
15} 16}