Skip to content

Commit

Permalink
Feat: Refactored Exchange, testing
Browse files Browse the repository at this point in the history
Refactored Exchange, fixed semantic bugs, centralized logic, and made integerproperty read only for safety and encapsulation
  • Loading branch information
tommyah committed May 25, 2026
1 parent 3488978 commit 3ee9296
Show file tree
Hide file tree
Showing 2 changed files with 221 additions and 96 deletions.
102 changes: 61 additions & 41 deletions src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/Exchange.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
import edu.ntnu.idi.idatt2003.g40.mappe.model.Share;
import edu.ntnu.idi.idatt2003.g40.mappe.model.Stock;
import edu.ntnu.idi.idatt2003.g40.mappe.model.Transaction;
import edu.ntnu.idi.idatt2003.g40.mappe.service.*;
import edu.ntnu.idi.idatt2003.g40.mappe.service.PurchaseCalculator;
import edu.ntnu.idi.idatt2003.g40.mappe.service.SaleCalculator;
import edu.ntnu.idi.idatt2003.g40.mappe.service.TransactionCalculator;
import edu.ntnu.idi.idatt2003.g40.mappe.service.TransactionFactory;
import edu.ntnu.idi.idatt2003.g40.mappe.service.TransactionType;
import edu.ntnu.idi.idatt2003.g40.mappe.utils.Validator;
import java.math.BigDecimal;
import java.util.ArrayList;
Expand All @@ -13,7 +17,7 @@
import java.util.Map;
import java.util.Random;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.ReadOnlyIntegerWrapper;

/**
* Represents a stock exchange where stocks can be traded.
Expand All @@ -40,7 +44,7 @@ public final class Exchange {
/**
* Current week (set to 1 in constructor).
* */
private final IntegerProperty week = new SimpleIntegerProperty(1);
private final ReadOnlyIntegerWrapper week = new ReadOnlyIntegerWrapper(1);

/**
* Map of {@link Stock} objects. Key is stock symbol. Value is stock.
Expand Down Expand Up @@ -120,10 +124,12 @@ public boolean hasStock(final String symbol) {
*
* @return {@link Stock} element gotten.
*
* @throws IllegalArgumentException if symbol is invalid.
* @throws IllegalArgumentException if symbol is invalid or not in exchange.
* */
public Stock getStock(final String symbol) throws IllegalArgumentException {
if (!Validator.VALID_STOCK_SYMBOL.isValid(symbol)) {
public Stock getStock(final String symbol)
throws IllegalArgumentException {
if (!Validator.VALID_STOCK_SYMBOL.isValid(symbol)
|| !stockMap.containsKey(symbol)) {
throw new IllegalArgumentException(
Validator.VALID_STOCK_SYMBOL.getErrorMessage());
}
Expand Down Expand Up @@ -164,10 +170,12 @@ public List<Stock> findStocks(final String searchTerm) {
public Transaction buy(final String symbol,
final BigDecimal quantity,
final Player player) throws IllegalArgumentException {
if (!Validator.VALID_STOCK_SYMBOL.isValid(symbol) || player == null) {
if (!Validator.VALID_STOCK_SYMBOL.isValid(symbol)
|| quantity == null
|| player == null) {
throw new IllegalArgumentException("Invalid purchase!");
}
Stock stock = stockMap.get(symbol);
Stock stock = getStock(symbol);
Share share = new Share(stock, quantity, stock.getSalesPrice());
TransactionCalculator calculator = new PurchaseCalculator(share);
Transaction purchase = TransactionFactory.createTransaction(
Expand Down Expand Up @@ -212,10 +220,12 @@ TransactionType.SALE, share, getWeek(), calculator
* @param stockSymbol the stock to sell shares in.
* @param player the player buying stock.
*
* @return List of transactions commited to sell the shares.
* @return List of transactions commited to sell the shares.
*
* @throws IllegalArgumentException if any parameter is null,
* or if player does not have enough shares.
* or if player does not have enough shares,
* or if player does not own any shares
* of the given stock.
* */
public List<Transaction> sell(BigDecimal amount,
final String stockSymbol,
Expand All @@ -225,41 +235,50 @@ public List<Transaction> sell(BigDecimal amount,
|| player == null
|| !Validator.VALID_STOCK_SYMBOL.isValid(stockSymbol)) {
throw new IllegalArgumentException("Invalid sell!");
} else {
}

List<Share> sharesOfStock = player.getPortfolio().getShares().stream()
.filter(s -> s.getStock().getSymbol().equals(stockSymbol))
.toList();
// Get all shares the player owns of the stock given in the parameter.
List<Share> sharesOfStock = player.getPortfolio().getShares().stream()
.filter(s -> s.getStock().getSymbol().equals(stockSymbol))
.toList();

BigDecimal totalOwned = player.getPortfolio()
.getTotalSharesBySymbol(stockSymbol);
// Throws error if player does not own any shares of the given stock.
if (sharesOfStock.isEmpty()) {
throw new IllegalArgumentException("Player does not own"
+ " any shares of this stock!");
}

if (amount.compareTo(totalOwned) > 0) {
amount = totalOwned;
}
ArrayList<Transaction> transactions = new ArrayList<>();
BigDecimal remainingToSell = amount;
// Gets the total quantity amount of shares owned of given stock.
BigDecimal totalOwned = player.getPortfolio()
.getTotalSharesBySymbol(stockSymbol);

// If amount wanted to sell is greater than total owned,
// sells entire collection.
if (amount.compareTo(totalOwned) > 0) {
amount = totalOwned;
}
ArrayList<Transaction> transactions = new ArrayList<>();
BigDecimal remainingToSell = amount;

for (Share share : sharesOfStock) {
if (remainingToSell.compareTo(BigDecimal.ZERO) <= 0) {
break;
}
// For every share owned, sells if the quantity is less than
// or equal to the remaining amount of shares to sell.
// Splits share if the remaining amount to sell is less than share quantity.
for (Share share : sharesOfStock) {

BigDecimal shareQty = share.getQuantity();
BigDecimal shareQty = share.getQuantity();

if (shareQty.compareTo(remainingToSell) <= 0) {
remainingToSell = remainingToSell.subtract(shareQty);
transactions.add(sell(share, player));
} else {
Share newShare = player.getPortfolio().splitShare(
share, remainingToSell
);
remainingToSell = BigDecimal.ZERO;
transactions.add(sell(newShare, player));
}
if (shareQty.compareTo(remainingToSell) <= 0) {
remainingToSell = remainingToSell.subtract(shareQty);
transactions.add(sell(share, player));
} else {
Share newShare = player.getPortfolio().splitShare(
share, remainingToSell
);
transactions.add(sell(newShare, player));
break;
}
return transactions;
}
return transactions;
}

/**
Expand All @@ -275,12 +294,13 @@ public void advance() {
for (Stock stock : stockMap.values()) {
BigDecimal currentPrice = stock.getSalesPrice();

double change = (
(random.nextDouble() * 0.10) - 0.05) + stock.getFortune();
BigDecimal change = BigDecimal.valueOf(random.nextDouble() * 0.10 - 0.05)
.add(BigDecimal.valueOf(stock.getFortune()));
stock.setFortune(0);
BigDecimal factor = BigDecimal.valueOf(1 + change);
BigDecimal factor = BigDecimal.ONE.add(change);

BigDecimal newPrice = currentPrice.multiply(factor);
BigDecimal newPrice = currentPrice.multiply(factor)
.setScale(2, java.math.RoundingMode.HALF_UP);
stock.addNewSalesPrice(newPrice);
}
week.set(week.get() + 1);
Expand Down
Loading

0 comments on commit 3ee9296

Please sign in to comment.