Skip to content

Commit

Permalink
Feat: Added sell functionality
Browse files Browse the repository at this point in the history
Made it possible to sell shares based on a stock. This works by getting total amount of shares owned on the stock, and splitting shares if the required amount to sell does not divide the total amount owned.
  • Loading branch information
tommyah committed May 15, 2026
1 parent 9e557d6 commit b705e59
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@
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.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.*;
import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventData;
import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventManager;
import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventPublisher;
Expand Down Expand Up @@ -199,6 +197,53 @@ public Transaction sell(final Share share, final Player player)
return sale;
}

/**
* Method called when a player sells share.
*
* @param amount the amount of "shares" to sell.
* @param stockSymbol the stock to sell shares in.
* @param player the player buying stock.
*
* @return Transaction representing the transaction.
*
* @throws IllegalArgumentException if any parameter is null, or if player does not have enough shares.
* */
public List<Transaction> sell(final BigDecimal amount, final String stockSymbol, final Player player)
throws IllegalArgumentException {
if (amount == null || player == null || !Validator.NOT_EMPTY.isValid(stockSymbol)) {
throw new IllegalArgumentException("Invalid sell!");
} else {

List<Share> sharesOfStock = player.getPortfolio().getShares().stream()
.filter(s -> s.getStock().getSymbol().equals(stockSymbol))
.toList();

BigDecimal totalOwned = player.getPortfolio().getTotalSharesBySymbol(stockSymbol);

if (amount.compareTo(totalOwned) > 0) {
throw new IllegalArgumentException("Not enough shares!");
}
ArrayList<Transaction> transactions = new ArrayList<>();
BigDecimal remainingToSell = amount;

for (Share share : sharesOfStock) {
if (remainingToSell.compareTo(BigDecimal.ZERO) <= 0) break;

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));
}
}
return transactions;
}
}

/**
* Method for advancing time, increasing the amount of weeks.
* */
Expand Down
17 changes: 16 additions & 1 deletion src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/Player.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ public final class Player {
* */
private final FloatProperty networthAsFloatProp = new SimpleFloatProperty(0);

/**
* Current money of player as a listenable {@link FloatProperty} object.
* */
private final FloatProperty moneyAsFloatProp = new SimpleFloatProperty(0);

/**
* The players' portfolio, holding their shares.
* */
Expand Down Expand Up @@ -68,6 +73,7 @@ public Player(final String name, final BigDecimal startingMoney) throws IllegalA
this.startingMoney = startingMoney;
this.money = this.startingMoney;
this.networthAsFloatProp.setValue(this.startingMoney);
this.moneyAsFloatProp.setValue(this.startingMoney);
this.portfolio = new Portfolio();
this.transactionArchive = new TransactionArchive();
}
Expand Down Expand Up @@ -115,7 +121,6 @@ public void addMoney(final BigDecimal amount) {
*/
public void withdrawMoney(final BigDecimal amount) {
money = money.subtract(amount);

}

/**
Expand Down Expand Up @@ -155,6 +160,15 @@ public FloatProperty getNetWorthAsFloatProperty() {
return networthAsFloatProp;
}

/**
* Get money as a {@link FloatProperty} object, allowing listening for changes.
*
* @return FloatProperty.
* */
public FloatProperty getMoneyAsFloatProperty() {
return moneyAsFloatProp;
}

/**
* Getter method for players' current status.
*
Expand Down Expand Up @@ -182,6 +196,7 @@ public void handleTransaction(final Transaction transaction) {
portfolio.removeShare(sale.getShare());
}
networthAsFloatProp.setValue(getNetWorth().floatValue());
moneyAsFloatProp.setValue(money);
transaction.commit(this);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,41 @@ public BigDecimal getNetWorth() {
}
return netWorth;
}

/**
* Helper method to get total amount of shares owned in a specific stock.
*
* @param symbol the symbol of the stock to check for shares.
* */
public BigDecimal getTotalSharesBySymbol(final String symbol) {
return shares.stream()
.filter(s -> s.getStock().getSymbol().equals(symbol))
.map(Share::getQuantity)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}

/**
* "Splits" a share in two pieces based on an amount.
*
* @param share the share to split.
* @param splitAmount the amount to split by.
*
* @return the split share from the original to the split amount.
*
* @throws IllegalArgumentException if share or split amount is invalid.
* */
public Share splitShare(final Share share, final BigDecimal splitAmount)
throws IllegalArgumentException {
if (!contains(share) || splitAmount.compareTo(share.getQuantity()) > 0) {
throw new IllegalArgumentException("Cannot split share!");
}
BigDecimal remainingAmount = share.getQuantity().subtract(splitAmount);

Share newShare1 = new Share(share.getStock(), splitAmount, share.getPurchasePrice());
Share newShare2 = new Share(share.getStock(), remainingAmount, share.getPurchasePrice());
removeShare(share);
addShare(newShare1);
addShare(newShare2);
return newShare1;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;

public class DashBoardController extends ViewController<DashBoardView> {

Expand Down Expand Up @@ -44,11 +45,8 @@ private void populateStockList() {


getViewElement().setOnStockAction(stockBtn, s, (stock)-> {
BigDecimal amountOfSharesOwnedThisStock = player.getPortfolio().getShares().stream()
.filter(share -> share.getStock().equals(s))
.map(Share::getQuantity)
.reduce(BigDecimal.ZERO, BigDecimal::add);
handleStockSelection(stock, amountOfSharesOwnedThisStock.floatValue());
BigDecimal amountOfSharesOwned = player.getPortfolio().getTotalSharesBySymbol(s.getSymbol());
handleStockSelection(stock, amountOfSharesOwned.floatValue());
});
}
}
Expand All @@ -72,6 +70,18 @@ protected void initInteractions() {
}
});

getViewElement().setOnAction(DashBoardActions.SELL_SHARES, () -> {
if (Validator.NOT_EMPTY.isValid(getViewElement().getQuantityInputField().getText())) {
List<Transaction> transactions = exchange.sell(new BigDecimal(getViewElement().getQuantityInputField().getText()), getViewElement().getCurrentStock().getSymbol(), player);

for (Transaction t : transactions) {
if(t.isCommited()) {
getViewElement().addOwnedShares(-t.getShare().getQuantity().floatValue());
}
}
}
});

exchange.weekProperty().addListener((observable,o,n) -> {
getViewElement().updateGraph();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,9 @@ protected void initInteractions() {
player.getNetWorthAsFloatProperty().addListener((observable, o, n) -> {
getViewElement().setBalance(player.getMoney().floatValue(), player.getNetWorth().floatValue());
});

player.getMoneyAsFloatProperty().addListener((observable, o, n) -> {
getViewElement().setBalance(player.getMoney().floatValue(), player.getNetWorth().floatValue());
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ protected void initStyling() {
}

public void setBalance(final float money, final float netWorth) {
balanceLabel.setText(Math.round(money*100f)/100f + "$ / " + Math.round(netWorth*100f)/100f + "$");
balanceLabel.setText(Math.round(money*100f)/100f + "NOK / " + Math.round(netWorth*100f)/100f + "NOK");
}

public void setWeek(int week) {
Expand Down

0 comments on commit b705e59

Please sign in to comment.