diff options
| author | Kostya <mail@sartin.in> | 2026-08-14 09:13:01 +0000 |
|---|---|---|
| committer | Kostya <mail@sartin.in> | 2026-08-14 09:13:01 +0000 |
| commit | db4317bbe0ff5709fa54ade5bec112186ca9aca3 (patch) | |
| tree | 3ac2f4f478af4d1af201f044c2ba43897e599b61 /service/src/main/java | |
| parent | 02deb7774db807f05eba4fe483de53ddd44588b2 (diff) | |
| download | guess-market-db4317bbe0ff5709fa54ade5bec112186ca9aca3.tar.gz guess-market-db4317bbe0ff5709fa54ade5bec112186ca9aca3.tar.xz guess-market-db4317bbe0ff5709fa54ade5bec112186ca9aca3.zip | |
Add ledgered credit/debit to Account and trade recording to Event
Drops AccountType (accounts no longer need to self-identify),
gives Account credit()/debit() that append LedgerEntry rows, adds
Event.recordTrade()/tradeVolumeOf()/getCommission(), and implements
LocalGuessMarketContext.listEvents() via the repository.
Diffstat (limited to 'service/src/main/java')
6 files changed, 67 insertions, 26 deletions
diff --git a/service/src/main/java/market/guess/service/LocalGuessMarketContext.java b/service/src/main/java/market/guess/service/LocalGuessMarketContext.java index a01d6d7..67e263f 100644 --- a/service/src/main/java/market/guess/service/LocalGuessMarketContext.java +++ b/service/src/main/java/market/guess/service/LocalGuessMarketContext.java | |||
| @@ -9,6 +9,7 @@ import market.guess.api.GuessMarketContext; | |||
| 9 | import market.guess.api.LoadResultDTO; | 9 | import market.guess.api.LoadResultDTO; |
| 10 | import market.guess.api.PurchaseReceiptDTO; | 10 | import market.guess.api.PurchaseReceiptDTO; |
| 11 | import market.guess.exception.GuessMarketException; | 11 | import market.guess.exception.GuessMarketException; |
| 12 | import market.guess.service.domain.Event; | ||
| 12 | import market.guess.service.repository.EventRepository; | 13 | import market.guess.service.repository.EventRepository; |
| 13 | 14 | ||
| 14 | public final class LocalGuessMarketContext implements GuessMarketContext { | 15 | public final class LocalGuessMarketContext implements GuessMarketContext { |
| @@ -26,7 +27,7 @@ public final class LocalGuessMarketContext implements GuessMarketContext { | |||
| 26 | 27 | ||
| 27 | @Override | 28 | @Override |
| 28 | public List<EventSummaryDTO> listEvents() { | 29 | public List<EventSummaryDTO> listEvents() { |
| 29 | throw new UnsupportedOperationException("Unimplemented method 'listEvents'"); | 30 | return eventRepository.getAllEvents().stream().map(Event::toEventSummary).toList(); |
| 30 | } | 31 | } |
| 31 | 32 | ||
| 32 | @Override | 33 | @Override |
diff --git a/service/src/main/java/market/guess/service/domain/Account.java b/service/src/main/java/market/guess/service/domain/Account.java index 274260e..7e24cdb 100644 --- a/service/src/main/java/market/guess/service/domain/Account.java +++ b/service/src/main/java/market/guess/service/domain/Account.java | |||
| @@ -4,28 +4,47 @@ import java.math.BigDecimal; | |||
| 4 | import java.time.Clock; | 4 | import java.time.Clock; |
| 5 | import java.util.ArrayList; | 5 | import java.util.ArrayList; |
| 6 | import java.util.List; | 6 | import java.util.List; |
| 7 | import java.util.concurrent.atomic.AtomicInteger; | ||
| 8 | import market.guess.api.LedgerType; | ||
| 7 | 9 | ||
| 8 | public final class Account { | 10 | public final class Account { |
| 9 | |||
| 10 | private final AccountType accountType; | ||
| 11 | private final String owner; | 11 | private final String owner; |
| 12 | private final Clock clock; | 12 | private final Clock clock; |
| 13 | private final List<LedgerEntry> entries = new ArrayList<>(); | 13 | private final List<LedgerEntry> entries = new ArrayList<>(); |
| 14 | private BigDecimal balance; | 14 | private BigDecimal balance; |
| 15 | private AtomicInteger runningId = new AtomicInteger(); | ||
| 15 | 16 | ||
| 16 | public Account(AccountType accountType, String owner, BigDecimal initialBalance) { | 17 | public Account(String owner, BigDecimal initialBalance) { |
| 17 | this(accountType, owner, initialBalance, Clock.systemUTC()); | 18 | this(owner, initialBalance, Clock.systemUTC()); |
| 18 | } | 19 | } |
| 19 | 20 | ||
| 20 | public Account(AccountType accountType, String owner, BigDecimal initialBalance, Clock clock) { | 21 | public Account(String owner, BigDecimal initialBalance, Clock clock) { |
| 21 | super(); | 22 | super(); |
| 22 | this.accountType = accountType; | ||
| 23 | this.owner = owner; | 23 | this.owner = owner; |
| 24 | this.balance = BigDecimalOptions.toMoney(initialBalance); | 24 | this.balance = BigDecimalOptions.toMoney(initialBalance); |
| 25 | this.clock = clock; | 25 | this.clock = clock; |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | public String getOwner() { | ||
| 29 | return owner; | ||
| 30 | } | ||
| 31 | |||
| 28 | public BigDecimal getBalance() { | 32 | public BigDecimal getBalance() { |
| 29 | return balance; | 33 | return balance; |
| 30 | } | 34 | } |
| 35 | |||
| 36 | public boolean credit(LedgerType type, BigDecimal amount, String note) { | ||
| 37 | record(type, amount, note); | ||
| 38 | return true; | ||
| 39 | } | ||
| 40 | |||
| 41 | public boolean debit(LedgerType type, BigDecimal amount, String note) { | ||
| 42 | record(type, amount.negate(), note); | ||
| 43 | return true; | ||
| 44 | } | ||
| 45 | |||
| 46 | private void record(LedgerType type, BigDecimal amount, String note) { | ||
| 47 | entries.add( | ||
| 48 | new LedgerEntry(runningId.incrementAndGet(), clock.instant(), type, amount, balance, note)); | ||
| 49 | } | ||
| 31 | } | 50 | } |
diff --git a/service/src/main/java/market/guess/service/domain/AccountType.java b/service/src/main/java/market/guess/service/domain/AccountType.java deleted file mode 100644 index 293ff4e..0000000 --- a/service/src/main/java/market/guess/service/domain/AccountType.java +++ /dev/null | |||
| @@ -1,6 +0,0 @@ | |||
| 1 | package market.guess.service.domain; | ||
| 2 | |||
| 3 | public enum AccountType { | ||
| 4 | EVENT, | ||
| 5 | USER | ||
| 6 | } | ||
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 383126f..2d7ac39 100644 --- a/service/src/main/java/market/guess/service/domain/Event.java +++ b/service/src/main/java/market/guess/service/domain/Event.java | |||
| @@ -1,8 +1,10 @@ | |||
| 1 | package market.guess.service.domain; | 1 | package market.guess.service.domain; |
| 2 | 2 | ||
| 3 | import java.math.BigDecimal; | 3 | import java.math.BigDecimal; |
| 4 | import java.time.Instant; | ||
| 4 | import java.util.ArrayList; | 5 | import java.util.ArrayList; |
| 5 | import java.util.List; | 6 | import java.util.List; |
| 7 | import java.util.concurrent.atomic.AtomicInteger; | ||
| 6 | import market.guess.api.CommissionTiming; | 8 | import market.guess.api.CommissionTiming; |
| 7 | import market.guess.api.EventStatus; | 9 | import market.guess.api.EventStatus; |
| 8 | import market.guess.api.EventSummaryDTO; | 10 | import market.guess.api.EventSummaryDTO; |
| @@ -20,10 +22,10 @@ public final class Event { | |||
| 20 | private final Account account; | 22 | private final Account account; |
| 21 | private final String marketMaker; | 23 | private final String marketMaker; |
| 22 | 24 | ||
| 25 | private final AtomicInteger runningId = new AtomicInteger(); | ||
| 23 | private final List<Trade> trades = new ArrayList<>(); | 26 | private final List<Trade> trades = new ArrayList<>(); |
| 24 | private EventStatus status; | 27 | private EventStatus status; |
| 25 | private String winningOptionKey; | 28 | private String winningOptionKey; |
| 26 | private long nextTradeSeq = 1; | ||
| 27 | private BigDecimal settlementCommission = BigDecimalOptions.ZERO_MONEY; | 29 | private BigDecimal settlementCommission = BigDecimalOptions.ZERO_MONEY; |
| 28 | 30 | ||
| 29 | public Event( | 31 | public Event( |
| @@ -46,7 +48,7 @@ public final class Event { | |||
| 46 | this.mechanism = mechanism; | 48 | this.mechanism = mechanism; |
| 47 | this.options = List.copyOf(options); | 49 | this.options = List.copyOf(options); |
| 48 | this.marketMaker = marketMaker; | 50 | this.marketMaker = marketMaker; |
| 49 | this.account = new Account(AccountType.EVENT, eventKey, BigDecimalOptions.ZERO_MONEY); | 51 | this.account = new Account(eventKey, BigDecimalOptions.ZERO_MONEY); |
| 50 | this.status = status; | 52 | this.status = status; |
| 51 | } | 53 | } |
| 52 | 54 | ||
| @@ -114,20 +116,40 @@ public final class Event { | |||
| 114 | this.winningOptionKey = winningOptionKey; | 116 | this.winningOptionKey = winningOptionKey; |
| 115 | } | 117 | } |
| 116 | 118 | ||
| 117 | public long getNextTradeSeq() { | 119 | public Trade recordTrade( |
| 118 | return nextTradeSeq; | 120 | Instant time, |
| 119 | } | 121 | String user, |
| 122 | Option option, | ||
| 123 | int quantity, | ||
| 124 | BigDecimal cost, | ||
| 125 | BigDecimal commission) { | ||
| 126 | var trade = | ||
| 127 | new Trade( | ||
| 128 | runningId.incrementAndGet(), | ||
| 129 | time, | ||
| 130 | user, | ||
| 131 | option.getKey(), | ||
| 132 | option.getName(), | ||
| 133 | quantity, | ||
| 134 | cost, | ||
| 135 | commission); | ||
| 136 | trades.add(trade); | ||
| 120 | 137 | ||
| 121 | public void setNextTradeSeq(long nextTradeSeq) { | 138 | return trade; |
| 122 | this.nextTradeSeq = nextTradeSeq; | ||
| 123 | } | 139 | } |
| 124 | 140 | ||
| 125 | public BigDecimal getSettlementCommission() { | 141 | public BigDecimal tradeVolumeOf(String option) { |
| 126 | return settlementCommission; | 142 | return trades.stream() |
| 143 | .filter(trade -> trade.optionKey().equalsIgnoreCase(option)) | ||
| 144 | .map(Trade::sharesCost) | ||
| 145 | .reduce(BigDecimalOptions.ZERO_MONEY, BigDecimal::add); | ||
| 127 | } | 146 | } |
| 128 | 147 | ||
| 129 | public void setSettlementCommission(BigDecimal settlementCommission) { | 148 | public BigDecimal getCommission() { |
| 130 | this.settlementCommission = settlementCommission; | 149 | return trades.stream() |
| 150 | .map(Trade::commission) | ||
| 151 | .reduce(BigDecimalOptions.ZERO_MONEY, BigDecimal::add) | ||
| 152 | .add(settlementCommission); | ||
| 131 | } | 153 | } |
| 132 | 154 | ||
| 133 | public EventSummaryDTO toEventSummary() { | 155 | public EventSummaryDTO toEventSummary() { |
diff --git a/service/src/main/java/market/guess/service/domain/LedgerEntry.java b/service/src/main/java/market/guess/service/domain/LedgerEntry.java index b9173c8..4ca4b92 100644 --- a/service/src/main/java/market/guess/service/domain/LedgerEntry.java +++ b/service/src/main/java/market/guess/service/domain/LedgerEntry.java | |||
| @@ -5,4 +5,9 @@ import java.time.Instant; | |||
| 5 | import market.guess.api.LedgerType; | 5 | import market.guess.api.LedgerType; |
| 6 | 6 | ||
| 7 | public record LedgerEntry( | 7 | public record LedgerEntry( |
| 8 | Instant time, LedgerType type, BigDecimal amount, BigDecimal balanceAfter, String note) {} | 8 | int id, |
| 9 | Instant time, | ||
| 10 | LedgerType type, | ||
| 11 | BigDecimal amount, | ||
| 12 | BigDecimal balanceAfter, | ||
| 13 | String note) {} | ||
diff --git a/service/src/main/java/market/guess/service/domain/User.java b/service/src/main/java/market/guess/service/domain/User.java index 3ae1d0f..af9ac78 100644 --- a/service/src/main/java/market/guess/service/domain/User.java +++ b/service/src/main/java/market/guess/service/domain/User.java | |||
| @@ -8,7 +8,7 @@ public final class User { | |||
| 8 | 8 | ||
| 9 | public User(String name, BigDecimal initialBalance) { | 9 | public User(String name, BigDecimal initialBalance) { |
| 10 | this.name = name; | 10 | this.name = name; |
| 11 | this.account = new Account(AccountType.USER, name, initialBalance); | 11 | this.account = new Account(name, initialBalance); |
| 12 | } | 12 | } |
| 13 | 13 | ||
| 14 | public String getName() { | 14 | public String getName() { |