Skip to content

Commit

Permalink
best/worst preformers of the week
Browse files Browse the repository at this point in the history
  • Loading branch information
EspenTinius committed May 27, 2026
1 parent 0b7352e commit 98cc03c
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public enum MarketActions {
SORT_PRICE,
/** Cycle the change-filter between winners-only and losers-only. */
SORT_CHANGE,
/** Cycle the week-change filter between best-of-week and worst-of-week. */
SORT_WEEK_CHANGE,
/** Show only stocks the player currently owns. */
SORT_OWNED;
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,13 @@ protected void initInteractions() {
? MarketSort.LOSERS : MarketSort.WINNERS;
getViewElement().setSort(next);
});
getViewElement().setOnAction(MarketActions.SORT_WEEK_CHANGE,
() -> {
MarketSort next =
(getViewElement().getCurrentSort() == MarketSort.BEST_WEEK)
? MarketSort.WORST_WEEK : MarketSort.BEST_WEEK;
getViewElement().setSort(next);
});
getViewElement().setOnAction(MarketActions.SORT_OWNED,
() -> getViewElement().setSort(MarketSort.OWNED));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
*
* <p>{@link #WINNERS} and {@link #LOSERS} are mutually exclusive views
* of the "change" filter button: pressing the button cycles between
* the two. {@link #OWNED} acts as a "show only owned" filter rather
* than a pure sort.</p>
* the two. {@link #BEST_WEEK} and {@link #WORST_WEEK} behave the same
* way on the "week" filter button, but only consider the change from
* the previous week to the current one. {@link #OWNED} acts as a
* "show only owned" filter rather than a pure sort.</p>
* */
public enum MarketSort {
/** Alphabetical sort by ticker symbol. */
Expand All @@ -17,6 +19,10 @@ public enum MarketSort {
WINNERS,
/** Show only stocks with negative change, sorted ascending. */
LOSERS,
/** Show only stocks that went up this week, sorted best to worst. */
BEST_WEEK,
/** Show only stocks that went down this week, sorted worst to best. */
WORST_WEEK,
/** Show only stocks the player currently owns, sorted by quantity. */
OWNED;
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public class MarketView extends ViewElement<VBox, MarketActions> {
private Button sortTickerButton;
private Button sortPriceButton;
private Button sortChangeButton;
private Button sortWeekChangeButton;
private Button sortOwnedButton;

/** Maps every sort mode to its filter-bar button. Used for active state. */
Expand Down Expand Up @@ -113,6 +114,7 @@ private HBox buildFilterBar() {
sortTickerButton = new Button("A-Z");
sortPriceButton = new Button("price");
sortChangeButton = new Button("change");
sortWeekChangeButton = new Button("week");
sortOwnedButton = new Button("owned");

sortButtonMap.put(MarketSort.TICKER, sortTickerButton);
Expand All @@ -121,16 +123,20 @@ private HBox buildFilterBar() {
// between them on each press.
sortButtonMap.put(MarketSort.WINNERS, sortChangeButton);
sortButtonMap.put(MarketSort.LOSERS, sortChangeButton);
// BEST_WEEK and WORST_WEEK share the week button in the same way.
sortButtonMap.put(MarketSort.BEST_WEEK, sortWeekChangeButton);
sortButtonMap.put(MarketSort.WORST_WEEK, sortWeekChangeButton);
sortButtonMap.put(MarketSort.OWNED, sortOwnedButton);

registerButton(MarketActions.SORT_TICKER, sortTickerButton);
registerButton(MarketActions.SORT_PRICE, sortPriceButton);
registerButton(MarketActions.SORT_CHANGE, sortChangeButton);
registerButton(MarketActions.SORT_WEEK_CHANGE, sortWeekChangeButton);
registerButton(MarketActions.SORT_OWNED, sortOwnedButton);

HBox filterBar = new HBox(8,
searchField, sortTickerButton, sortPriceButton,
sortChangeButton, sortOwnedButton);
sortChangeButton, sortWeekChangeButton, sortOwnedButton);
filterBar.setAlignment(Pos.CENTER_LEFT);
filterBar.setPadding(Insets.EMPTY);

Expand Down Expand Up @@ -163,7 +169,8 @@ protected void initStyling() {
getRootPane().getStyleClass().add("market-container");

searchField.getStyleClass().add("market-search");
Stream.of(sortTickerButton, sortPriceButton, sortChangeButton, sortOwnedButton)
Stream.of(sortTickerButton, sortPriceButton, sortChangeButton,
sortWeekChangeButton, sortOwnedButton)
.forEach(b -> b.getStyleClass().add("market-filter-btn"));

stocksScroll.getStyleClass().add("market-scroll");
Expand Down Expand Up @@ -223,7 +230,8 @@ public MarketSort getCurrentSort() {
*
* <p>The change button doubles as a winners/losers cycle - its label
* updates to reflect the active mode so the user can tell which one
* they're currently looking at.</p>
* they're currently looking at. The week button does the same for
* best-of-week and worst-of-week.</p>
*
* @param sort the new sort mode.
* */
Expand All @@ -237,6 +245,7 @@ public void setSort(final MarketSort sort) {
}
}
refreshChangeButtonLabel();
refreshWeekChangeButtonLabel();
renderStocks();
}

Expand All @@ -256,11 +265,28 @@ private void refreshChangeButtonLabel() {
});
}

/**
* Updates the label on the week-change button the same way as
* {@link #refreshChangeButtonLabel()}, but for the best/worst-of-week
* cycle.
* */
private void refreshWeekChangeButtonLabel() {
if (sortWeekChangeButton == null) {
return;
}
sortWeekChangeButton.setText(switch (currentSort) {
case BEST_WEEK -> "best of week";
case WORST_WEEK -> "worst of week";
default -> "week";
});
}

/**
* Rebuilds the stock card grid based on the current search term and
* sort mode.
*
* <p>{@link MarketSort#WINNERS}, {@link MarketSort#LOSERS} and
* <p>{@link MarketSort#WINNERS}, {@link MarketSort#LOSERS},
* {@link MarketSort#BEST_WEEK}, {@link MarketSort#WORST_WEEK} and
* {@link MarketSort#OWNED} act as filters: in those modes the grid
* only shows the matching subset of stocks. The other modes show
* every stock that matches the search term.</p>
Expand Down Expand Up @@ -301,6 +327,8 @@ private boolean matchesSortFilter(final Stock stock) {
return switch (currentSort) {
case WINNERS -> changePercent(stock) > 0;
case LOSERS -> changePercent(stock) < 0;
case BEST_WEEK -> weekChangePercent(stock) > 0;
case WORST_WEEK -> weekChangePercent(stock) < 0;
case OWNED -> ownedLookup.applyAsInt(stock.getSymbol()) > 0;
default -> true;
};
Expand All @@ -315,6 +343,8 @@ private String emptyMessageFor(final MarketSort sort) {
return switch (sort) {
case WINNERS -> "no winners right now";
case LOSERS -> "no losers right now";
case BEST_WEEK -> "no stocks went up this week";
case WORST_WEEK -> "no stocks went down this week";
case OWNED -> "you don't own any stocks yet";
default -> "no stocks match your search";
};
Expand All @@ -331,6 +361,10 @@ private Comparator<Stock> comparatorFor(final MarketSort sort) {
(Stock s) -> changePercent(s)).reversed();
case LOSERS -> Comparator.comparingDouble(
(Stock s) -> changePercent(s));
case BEST_WEEK -> Comparator.comparingDouble(
(Stock s) -> weekChangePercent(s)).reversed();
case WORST_WEEK -> Comparator.comparingDouble(
(Stock s) -> weekChangePercent(s));
case OWNED -> Comparator.comparingInt(
(Stock s) -> ownedLookup.applyAsInt(s.getSymbol())).reversed();
};
Expand All @@ -353,6 +387,24 @@ private double changePercent(final Stock stock) {
return ((end - start) / start) * 100.0;
}

/**
* Calculates the change percentage between the previous week's price
* and the current week's price. Each entry in the price history
* represents one week, so this is just the last two prices.
* */
private double weekChangePercent(final Stock stock) {
List<BigDecimal> prices = stock.getHistoricalPrices();
if (prices.size() < 2) {
return 0.0;
}
double previous = prices.get(prices.size() - 2).doubleValue();
double current = prices.getLast().doubleValue();
if (previous == 0.0) {
return 0.0;
}
return ((current - previous) / previous) * 100.0;
}

/**
* Builds a single stock card. The card is clickable and forwards the
* selection to the registered {@code onStockSelected} consumer.
Expand Down

0 comments on commit 98cc03c

Please sign in to comment.