From dd7cf029bd1d1a63bff0f7bfaa1c0f2b94e5263a Mon Sep 17 00:00:00 2001
From: =
Date: Tue, 26 May 2026 20:06:10 +0200
Subject: [PATCH 1/5] Fix: Fixed save warning
---
.../view/playgame/PlayGameController.java | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/playgame/PlayGameController.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/playgame/PlayGameController.java
index b5001f3..7fbe560 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/playgame/PlayGameController.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/playgame/PlayGameController.java
@@ -175,13 +175,27 @@ private void handleUploadSave() {
if (uploadedSave == null) {
showAlert(AlertType.WARNING,
"No saves found",
- "Den valgte filen inneholdt ingen gyldig save.\n\n"
- + "Forventet JSON-format:\n"
+ "The chosen file includes an unsupported format!\n\n"
+ + "Expected JSON format:\n"
+ " {\n"
+ " \"name\": \"MySave\",\n"
+ " \"balance\": 10000.00,\n"
+ " \"startingCapital\": 10000.00,\n"
+ " \"stockDataPath\": null\n"
+ + " \"week\": 1\n"
+ + " \"ownedShares\": [\n"
+ + " { \"symbol\": \"AAPL\", \"quantity\": 5, \"purchasePrice\": 150.00 }\n"
+ + " ]\n"
+ + " \"transactions\": [\n"
+ + " { \"type\": \"PURCHASE\", \"symbol\": \"AAPL\", \"quantity\": 5, \"price\": 150.00, \"week\": 1}\n"
+ + " ]\n"
+ + " \"stocks\": [\n"
+ + " { \"symbol\": \"AAPL\", \"companyName\": \"Apple inc.\", \"salesPrice\": 155.25}\n"
+ + " ]\n"
+ + " \"netWorthHistory\": [\n"
+ + " 1001.10,\n"
+ + " 1025.24,\n"
+ + " ]\n"
+ " }");
return;
}
From f7ef95b90cc7776e63b9225e4f68851528674134 Mon Sep 17 00:00:00 2001
From: =
Date: Tue, 26 May 2026 23:23:51 +0200
Subject: [PATCH 2/5] Feat: Fix portfolio and summary controller
Fixed portfolio (revert to arraylist of shares), and fixed visual bug in financial summary graph
---
.../idatt2003/g40/mappe/engine/Exchange.java | 3 +-
.../idatt2003/g40/mappe/model/Portfolio.java | 107 +++++++++---------
.../financialsummary/SummaryController.java | 1 -
3 files changed, 54 insertions(+), 57 deletions(-)
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/Exchange.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/Exchange.java
index bfeaad4..5e7bf07 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/Exchange.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/Exchange.java
@@ -297,13 +297,14 @@ public Transaction sell(BigDecimal amount,
Share ownedPosition = matchingShares.getFirst();
BigDecimal totalOwned = ownedPosition.getQuantity();
+ BigDecimal ownedPurchasePrice = ownedPosition.getPurchasePrice();
if (amount.compareTo(totalOwned) > 0) {
amount = totalOwned;
}
Stock stock = ownedPosition.getStock();
- Share shareToSell = new Share(stock, amount, stock.getSalesPrice());
+ Share shareToSell = new Share(stock, amount, ownedPurchasePrice);
Transaction sale = TransactionFactory.createTransaction(
TransactionType.SALE, shareToSell, getWeek()
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/Portfolio.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/Portfolio.java
index a2a73a3..3b08af9 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/Portfolio.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/model/Portfolio.java
@@ -2,6 +2,7 @@
import edu.ntnu.idi.idatt2003.g40.mappe.utils.Validator;
import java.math.BigDecimal;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -17,9 +18,9 @@
public final class Portfolio {
/**
- * Map used to handle internal shares.
+ * List of shares in portfolio.
* */
- private final Map shares = new HashMap<>();
+ private final List shares = new ArrayList<>();
/**
* Creates an empty portfolio.
@@ -39,30 +40,13 @@ public void addShare(final Share share) throws IllegalArgumentException {
if (share == null) {
throw new IllegalArgumentException("Invalid share!");
}
- String symbol = share.getStock().getSymbol().toUpperCase();
-
- if (shares.containsKey(symbol)) {
- Share existingShare = shares.get(symbol);
- BigDecimal totalQuantity =
- existingShare.getQuantity().add(share.getQuantity());
-
- shares.put(symbol,
- new Share(
- share.getStock(),
- totalQuantity, existingShare.getPurchasePrice()
- )
- );
- } else {
- shares.put(symbol, share);
- }
+ shares.add(share);
}
/**
* Removes a share from the portfolio.
*
- * Uses the quantity value to deduct share amount from the map.
- * If quantity to remove is equal to amount held, removes share entirely.
- * If not, splits the share.
+ * Removes based on FIFO (First In First Out)
*
* @param share the share to remove
*
@@ -72,35 +56,42 @@ public void addShare(final Share share) throws IllegalArgumentException {
* @throws IllegalArgumentException if share is null.
*
*/
- public boolean removeShare(final Share share)
- throws IllegalArgumentException {
+ public boolean removeShare(final Share share) throws IllegalArgumentException {
if (share == null) {
throw new IllegalArgumentException("Invalid share!");
}
- String symbol = share.getStock().getSymbol().toUpperCase();
- if (!shares.containsKey(symbol)) {
- return false;
- }
- Share ownedShare = shares.get(symbol);
- int comparison = ownedShare.getQuantity().compareTo(share.getQuantity());
+ String symbol = share.getStock().getSymbol();
- if (comparison < 0) {
- throw new IllegalArgumentException(
- "Cannot remove more shares than are currently owned!");
- } else if (comparison == 0) {
-
- shares.remove(symbol);
- } else {
- BigDecimal remainingQuantity =
- ownedShare.getQuantity().subtract(share.getQuantity());
- shares.put(symbol,
- new Share(
- share.getStock(),
- remainingQuantity,
- ownedShare.getPurchasePrice()
- )
- );
+ List matchingShares = shares.stream()
+ .filter(s -> s.getStock().getSymbol().equalsIgnoreCase(symbol))
+ .toList();
+
+ BigDecimal quantityToRemove = share.getQuantity();
+
+ BigDecimal totalOwned = matchingShares.stream()
+ .map(Share::getQuantity)
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
+
+ if (quantityToRemove.compareTo(totalOwned) > 0) {
+ throw new IllegalArgumentException("Cannot remove more shares than are currently owned!");
+ }
+
+ for (Share s : matchingShares) {
+ if (quantityToRemove.signum() <= 0) {
+ break;
+ }
+
+ int comparison = s.getQuantity().compareTo(quantityToRemove);
+ shares.remove(s);
+
+ if (comparison > 0) {
+ BigDecimal remainingQuantity = s.getQuantity().subtract(quantityToRemove);
+ shares.add(new Share(s.getStock(), remainingQuantity, s.getPurchasePrice()));
+ quantityToRemove = BigDecimal.ZERO;
+ } else {
+ quantityToRemove = quantityToRemove.subtract(s.getQuantity());
+ }
}
return true;
}
@@ -111,7 +102,7 @@ public boolean removeShare(final Share share)
* @return a list of shares
*/
public List getShares() {
- return List.copyOf(shares.values());
+ return shares;
}
/**
@@ -141,8 +132,9 @@ public List getShares(final String symbol)
throw new IllegalArgumentException(
Validator.VALID_STOCK_SYMBOL.getErrorMessage());
}
- Share share = shares.get(symbol.toUpperCase());
- return share != null ? List.of(share) : List.of();
+ return shares.stream()
+ .filter(s -> s.getStock().getSymbol().equalsIgnoreCase(symbol))
+ .toList();
}
/**
@@ -160,10 +152,13 @@ public boolean contains(final Share share)
if (share == null) {
throw new IllegalArgumentException("Invalid share!");
}
- String symbol = share.getStock().getSymbol().toUpperCase();
- Share owned = shares.get(symbol);
- return owned != null
- && owned.getQuantity().compareTo(share.getQuantity()) >= 0;
+
+ BigDecimal totalOwned = shares.stream()
+ .filter(s -> s.getStock().getSymbol().equalsIgnoreCase(share.getStock().getSymbol()))
+ .map(Share::getQuantity)
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
+
+ return totalOwned.compareTo(share.getQuantity()) >= 0;
}
/**
@@ -174,7 +169,7 @@ public boolean contains(final Share share)
* */
public BigDecimal getNetWorth() {
BigDecimal netWorth = BigDecimal.ZERO;
- for (Share s : shares.values()) {
+ for (Share s : shares) {
netWorth = netWorth.add(
s.getQuantity().multiply(s.getStock().getSalesPrice())
);
@@ -194,7 +189,9 @@ public BigDecimal getTotalShareQuantityBySymbol(final String symbol) {
if (symbol == null) {
return BigDecimal.ZERO;
}
- Share share = shares.get(symbol.toUpperCase());
- return share != null ? share.getQuantity() : BigDecimal.ZERO;
+ return shares.stream()
+ .filter(s -> s.getStock().getSymbol().equalsIgnoreCase(symbol))
+ .map(Share::getQuantity)
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
}
}
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/financialsummary/SummaryController.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/financialsummary/SummaryController.java
index 9b5aa5c..4fd2020 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/financialsummary/SummaryController.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/widgets/financialsummary/SummaryController.java
@@ -102,7 +102,6 @@ public void handleContextUpdate(final Exchange criticalExchange, final Player ac
this.playerNetWorthHistory.addAll(this.player.getNetWorthHistory());
} else {
this.playerNetWorthHistory.add(this.player.getStartingMoney());
- this.playerNetWorthHistory.add(this.player.getNetWorth());
}
getViewElement().setWeek(this.exchange.getWeek());
From 12bb7bc40afeb81d20a998ffa26203cc32c3859b Mon Sep 17 00:00:00 2001
From: =
Date: Tue, 26 May 2026 23:35:39 +0200
Subject: [PATCH 3/5] Feat: Updated exchange and test classes
---
.../idi/idatt2003/g40/mappe/engine/Exchange.java | 13 ++++++++-----
.../idatt2003/g40/mappe/engine/ExchangeTest.java | 13 +++++++------
.../idi/idatt2003/g40/mappe/model/PlayerTest.java | 9 ++++++---
3 files changed, 21 insertions(+), 14 deletions(-)
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/Exchange.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/Exchange.java
index 5e7bf07..632f657 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/Exchange.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/Exchange.java
@@ -295,16 +295,19 @@ public Transaction sell(BigDecimal amount,
throw new IllegalArgumentException("Player does not own any shares of this stock!");
}
- Share ownedPosition = matchingShares.getFirst();
- BigDecimal totalOwned = ownedPosition.getQuantity();
- BigDecimal ownedPurchasePrice = ownedPosition.getPurchasePrice();
+ BigDecimal totalOwned = matchingShares.stream()
+ .map(Share::getQuantity)
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
if (amount.compareTo(totalOwned) > 0) {
amount = totalOwned;
}
- Stock stock = ownedPosition.getStock();
- Share shareToSell = new Share(stock, amount, ownedPurchasePrice);
+ Stock stock = matchingShares.getFirst().getStock();
+
+ BigDecimal oldestPurchasePrice = matchingShares.getFirst().getPurchasePrice();
+
+ Share shareToSell = new Share(stock, amount, oldestPurchasePrice);
Transaction sale = TransactionFactory.createTransaction(
TransactionType.SALE, shareToSell, getWeek()
diff --git a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/ExchangeTest.java b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/ExchangeTest.java
index 66ce0c5..65337f7 100644
--- a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/ExchangeTest.java
+++ b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/engine/ExchangeTest.java
@@ -156,19 +156,20 @@ void buyThrowsExceptionOnIllegalArguments() {
@Test
void sellReturnsSaleAndAddsMoneyToPlayer() {
- Share share =
- new Share(appleStock, new BigDecimal("2"),
- appleStock.getSalesPrice());
-
- Transaction transaction = testExchange.sell(share, testPlayer);
+ BigDecimal sellAmount = new BigDecimal("2");
+ Share share = new Share(appleStock, sellAmount, appleStock.getSalesPrice());
+ testPlayer.getPortfolio().addShare(share);
+ Transaction transaction = testExchange.sell(sellAmount, appleStock.getSymbol(), testPlayer);
TransactionCalculator calculator = new SaleCalculator(share);
BigDecimal expectedPlayerMoney =
testPlayer.getStartingMoney().add(calculator.calculateTotal());
+
assertInstanceOf(Sale.class, transaction);
assertEquals(1, transaction.getWeek());
- assertSame(share, transaction.getShare());
+ assertEquals(share.getQuantity(), transaction.getShare().getQuantity());
+ assertEquals(share.getPurchasePrice(), transaction.getShare().getPurchasePrice());
assertEquals(expectedPlayerMoney, testPlayer.getMoney());
}
diff --git a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/PlayerTest.java b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/PlayerTest.java
index 9ac2d64..3837214 100644
--- a/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/PlayerTest.java
+++ b/src/test/java/edu/ntnu/idi/idatt2003/g40/mappe/model/PlayerTest.java
@@ -130,13 +130,16 @@ void getNetWorthCalculatesCorrectlyForSales() {
Stock stock = new Stock("AAPL", "Apple inc.,", new BigDecimal("100.00"));
Share share = new Share(stock, new BigDecimal("1"), new BigDecimal("100.00"));
- BigDecimal expectedNetWorth = testPlayer.getNetWorth();
- Transaction transaction = TransactionFactory.createTransaction(TransactionType.SALE, share, 1);
+ testPlayer.getPortfolio().addShare(share);
+ BigDecimal baselineNetWorth = testPlayer.getNetWorth();
+ Transaction transaction = TransactionFactory.createTransaction(TransactionType.SALE, share, 1);
testPlayer.handleTransaction(transaction);
BigDecimal actualNetWorth = testPlayer.getNetWorth();
- expectedNetWorth = expectedNetWorth.add(transaction.getCalculator().calculateTotal());
+
+ BigDecimal commissionFee = transaction.getCalculator().calculateCommission();
+ BigDecimal expectedNetWorth = baselineNetWorth.subtract(commissionFee);
assertEquals(expectedNetWorth, actualNetWorth);
assertEquals(actualNetWorth.floatValue(),
From 477cbba12d5bd5047af164d855c4ed999f442a20 Mon Sep 17 00:00:00 2001
From: =
Date: Tue, 26 May 2026 23:37:59 +0200
Subject: [PATCH 4/5] Fix: Removed unused method
---
.../view/playgame/PlayGameController.java | 33 ++++---------------
1 file changed, 6 insertions(+), 27 deletions(-)
diff --git a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/playgame/PlayGameController.java b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/playgame/PlayGameController.java
index 7fbe560..c2e788b 100644
--- a/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/playgame/PlayGameController.java
+++ b/src/main/java/edu/ntnu/idi/idatt2003/g40/mappe/view/playgame/PlayGameController.java
@@ -7,10 +7,8 @@
import edu.ntnu.idi.idatt2003.g40.mappe.service.event.EventType;
import edu.ntnu.idi.idatt2003.g40.mappe.view.ViewController;
import edu.ntnu.idi.idatt2003.g40.mappe.view.ViewEnum;
-
import java.io.File;
import java.util.List;
-
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
@@ -22,12 +20,10 @@
*
* Extends {@link ViewController}
*
- *
- * Handles four user actions: navigating to the create-game screen,
+ *
Handles four user actions: navigating to the create-game screen,
* going back to the main menu, uploading a custom save file from
* disk, and opening a save when one of the displayed rows is
- * clicked.
- *
+ * clicked.
*/
public class PlayGameController extends ViewController {
@@ -38,19 +34,6 @@ public class PlayGameController extends ViewController {
* */
private final SaveGameService saveGameService;
- /**
- * Constructor with a default {@link SaveGameService} pointing at
- * the bundled saves directory.
- *
- * @param view the {@link PlayGameView} this controller is
- * attached to.
- * @param eventManager the active {@link EventManager}.
- */
- public PlayGameController(final PlayGameView view,
- final EventManager eventManager) {
- this(view, eventManager, new SaveGameService());
- }
-
/**
* Constructor accepting an explicit {@link SaveGameService} for
* tests or custom save locations.
@@ -122,12 +105,10 @@ protected void initInteractions() {
* Re-reads every save from disk and returns the one whose name
* matches, or {@code null} if no matching save is found.
*
- *
- * Used to defeat the staleness of the {@link SaveGame} instance
+ *
Used to defeat the staleness of the {@link SaveGame} instance
* bound to the clicked row in {@link PlayGameView}: between when
* {@link #refresh()} last ran and the click, the auto-save flow may
- * have written newer content to disk.
- *
+ * have written newer content to disk.
*
* @param name the save display name to look up.
* @return the freshly parsed {@link SaveGame}, or {@code null}.
@@ -149,10 +130,8 @@ private SaveGame findFreshSaveByName(final String name) {
* disk. Parses it using {@link SaveGameService#loadSaveFromFile} and
* appends the resulting save to the view's current list.
*
- *
- * Shows an alert if the user picks a file that contains no valid
- * save entry.
- *
+ * Shows an alert if the user picks a file that contains no valid
+ * save entry.
*/
private void handleUploadSave() {
FileChooser fileChooser = new FileChooser();
From 2f3b227779afbdc778c8b7547333eec78d7266f3 Mon Sep 17 00:00:00 2001
From: =
Date: Tue, 26 May 2026 23:47:51 +0200
Subject: [PATCH 5/5] Fix: Removed default save files.
---
src/main/resources/saves/Halleluja.json | 13 --
src/main/resources/saves/Newbie.json | 183 ------------------------
2 files changed, 196 deletions(-)
delete mode 100644 src/main/resources/saves/Halleluja.json
delete mode 100644 src/main/resources/saves/Newbie.json
diff --git a/src/main/resources/saves/Halleluja.json b/src/main/resources/saves/Halleluja.json
deleted file mode 100644
index 7737bf7..0000000
--- a/src/main/resources/saves/Halleluja.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "name": "Halleluja",
- "balance": 1000650901.43,
- "startingCapital": 10000.0,
- "stockDataPath": null,
- "week": 1,
- "ownedShares": [],
- "transactions": [],
- "stocks": [
- { "symbol": "TOTA", "name": "Total badass1", "price": 136.80 },
- { "symbol": "LIBR", "name": "Libra avenger of worlds", "price": 19134.23 }
- ]
-}
diff --git a/src/main/resources/saves/Newbie.json b/src/main/resources/saves/Newbie.json
deleted file mode 100644
index 9ce1a99..0000000
--- a/src/main/resources/saves/Newbie.json
+++ /dev/null
@@ -1,183 +0,0 @@
-{
- "name": "Newbie",
- "balance": 11.2045,
- "startingCapital": 10000.0,
- "stockDataPath": null,
- "week": 5,
- "ownedShares": [
- { "symbol": "NVDA", "quantity": 50.0, "purchasePrice": 191.27 },
- { "symbol": "CARR", "quantity": 5.0, "purchasePrice": 75.12 }
- ],
- "transactions": [
- { "type": "PURCHASE", "symbol": "NVDA", "quantity": 5.0, "price": 191.27, "week": 1 },
- { "type": "PURCHASE", "symbol": "NVDA", "quantity": 5.0, "price": 191.27, "week": 1 },
- { "type": "PURCHASE", "symbol": "NVDA", "quantity": 5.0, "price": 191.27, "week": 1 },
- { "type": "PURCHASE", "symbol": "NVDA", "quantity": 5.0, "price": 191.27, "week": 1 },
- { "type": "PURCHASE", "symbol": "NVDA", "quantity": 5.0, "price": 191.27, "week": 1 },
- { "type": "PURCHASE", "symbol": "NVDA", "quantity": 5.0, "price": 191.27, "week": 1 },
- { "type": "PURCHASE", "symbol": "NVDA", "quantity": 5.0, "price": 191.27, "week": 1 },
- { "type": "PURCHASE", "symbol": "NVDA", "quantity": 5.0, "price": 191.27, "week": 1 },
- { "type": "PURCHASE", "symbol": "NVDA", "quantity": 5.0, "price": 191.27, "week": 1 },
- { "type": "PURCHASE", "symbol": "NVDA", "quantity": 5.0, "price": 191.27, "week": 1 },
- { "type": "PURCHASE", "symbol": "CARR", "quantity": 1.0, "price": 75.12, "week": 5 },
- { "type": "PURCHASE", "symbol": "CARR", "quantity": 4.0, "price": 75.12, "week": 5 }
- ],
- "stocks": [
- { "symbol": "CARR", "name": "Carrier Global", "price": 75.12 },
- { "symbol": "AAPL", "name": "Apple Inc.", "price": 232.42 },
- { "symbol": "SNDK", "name": "Sandisk Corporation", "price": 861.26 },
- { "symbol": "WYNN", "name": "Wynn Resorts", "price": 97.54 },
- { "symbol": "TSCO", "name": "Tractor Supply", "price": 53.32 },
- { "symbol": "AMGN", "name": "Amgen", "price": 404.61 },
- { "symbol": "TSLA", "name": "Tesla Inc.", "price": 360.17 },
- { "symbol": "GDDY", "name": "GoDaddy", "price": 85.21 },
- { "symbol": "SBUX", "name": "Starbucks", "price": 110.59 },
- { "symbol": "KVUE", "name": "Kenvue", "price": 15.57 },
- { "symbol": "META", "name": "Meta Platforms", "price": 707.00 },
- { "symbol": "DLTR", "name": "Dollar Tree", "price": 175.45 },
- { "symbol": "ABBV", "name": "AbbVie", "price": 258.94 },
- { "symbol": "HUBB", "name": "Hubbell Incorporated", "price": 495.97 },
- { "symbol": "JKHY", "name": "Jack Henry & Associates", "price": 169.66 },
- { "symbol": "FSLR", "name": "First Solar", "price": 229.93 },
- { "symbol": "FTNT", "name": "Fortinet", "price": 95.95 },
- { "symbol": "EPAM", "name": "EPAM Systems", "price": 178.35 },
- { "symbol": "POOL", "name": "Pool Corporation", "price": 318.43 },
- { "symbol": "MCHP", "name": "Microchip Technology", "price": 65.59 },
- { "symbol": "VRSK", "name": "Verisk Analytics", "price": 178.39 },
- { "symbol": "MRNA", "name": "Moderna", "price": 31.43 },
- { "symbol": "HOOD", "name": "Robinhood Markets Inc.", "price": 54.38 },
- { "symbol": "VRSN", "name": "Verisign", "price": 233.77 },
- { "symbol": "AMAT", "name": "Applied Materials", "price": 377.25 },
- { "symbol": "MDLZ", "name": "Mondelez International", "price": 65.31 },
- { "symbol": "PCAR", "name": "Paccar", "price": 115.13 },
- { "symbol": "NDAQ", "name": "Nasdaq Inc.", "price": 83.12 },
- { "symbol": "INTU", "name": "Intuit", "price": 440.63 },
- { "symbol": "HSIC", "name": "Henry Schein", "price": 91.95 },
- { "symbol": "FISV", "name": "Fiserv Inc.", "price": 59.88 },
- { "symbol": "CSGP", "name": "CoStar Group", "price": 42.70 },
- { "symbol": "DDOG", "name": "Datadog", "price": 146.05 },
- { "symbol": "BLDR", "name": "Builders FirstSource", "price": 128.46 },
- { "symbol": "AXON", "name": "Axon Enterprise", "price": 504.02 },
- { "symbol": "ALGN", "name": "Align Technology", "price": 183.45 },
- { "symbol": "FICO", "name": "Fair Isaac", "price": 1520.04 },
- { "symbol": "FITB", "name": "Fifth Third Bancorp", "price": 51.73 },
- { "symbol": "NTAP", "name": "NetApp", "price": 120.48 },
- { "symbol": "FOXA", "name": "Fox Corporation (Class A)", "price": 55.61 },
- { "symbol": "QCOM", "name": "Qualcomm", "price": 141.80 },
- { "symbol": "VTRS", "name": "Viatris", "price": 17.43 },
- { "symbol": "LRCX", "name": "Lam Research", "price": 248.71 },
- { "symbol": "PLTR", "name": "Palantir Technologies", "price": 131.78 },
- { "symbol": "PANW", "name": "Palo Alto Networks", "price": 184.16 },
- { "symbol": "NFLX", "name": "Netflix", "price": 65.49 },
- { "symbol": "KLAC", "name": "KLA Corporation", "price": 1443.99 },
- { "symbol": "NVDA", "name": "Nvidia", "price": 179.79 },
- { "symbol": "IBKR", "name": "Interactive Brokers Group", "price": 71.52 },
- { "symbol": "CRWD", "name": "CrowdStrike", "price": 411.42 },
- { "symbol": "ULTA", "name": "Ulta Beauty", "price": 575.53 },
- { "symbol": "JBHT", "name": "J.B. Hunt", "price": 232.28 },
- { "symbol": "SMCI", "name": "Supermicro", "price": 33.83 },
- { "symbol": "NXPI", "name": "NXP Semiconductors", "price": 219.91 },
- { "symbol": "VRTX", "name": "Vertex Pharmaceuticals", "price": 528.78 },
- { "symbol": "MTCH", "name": "Match Group", "price": 32.55 },
- { "symbol": "CBOE", "name": "Cboe Global Markets", "price": 252.98 },
- { "symbol": "CPRT", "name": "Copart", "price": 42.35 },
- { "symbol": "VICI", "name": "Vici Properties", "price": 30.53 },
- { "symbol": "CHRW", "name": "C.H. Robinson", "price": 194.63 },
- { "symbol": "INTC", "name": "Intel", "price": 39.70 },
- { "symbol": "ROST", "name": "Ross Stores", "price": 204.91 },
- { "symbol": "GEHC", "name": "GE HealthCare", "price": 85.26 },
- { "symbol": "SCHW", "name": "Charles Schwab Corporation", "price": 80.90 },
- { "symbol": "CVNA", "name": "Carvana Co.", "price": 356.72 },
- { "symbol": "IDXX", "name": "Idexx Laboratories", "price": 864.02 },
- { "symbol": "INCY", "name": "Incyte", "price": 116.33 },
- { "symbol": "GNRC", "name": "Generac", "price": 218.82 },
- { "symbol": "CPAY", "name": "Corpay", "price": 416.82 },
- { "symbol": "REGN", "name": "Regeneron Pharmaceuticals", "price": 628.99 },
- { "symbol": "CTAS", "name": "Cintas", "price": 213.98 },
- { "symbol": "FAST", "name": "Fastenal", "price": 39.25 },
- { "symbol": "AMZN", "name": "Amazon", "price": 221.21 },
- { "symbol": "ZBRA", "name": "Zebra Technologies", "price": 289.08 },
- { "symbol": "ODFL", "name": "Old Dominion", "price": 180.38 },
- { "symbol": "TTWO", "name": "Take-Two Interactive", "price": 158.67 },
- { "symbol": "CTRA", "name": "Coterra", "price": 32.93 },
- { "symbol": "LDOS", "name": "Leidos", "price": 166.54 },
- { "symbol": "ARES", "name": "Ares Management Corporation", "price": 120.77 },
- { "symbol": "CINF", "name": "Cincinnati Financial", "price": 154.99 },
- { "symbol": "ANET", "name": "Arista Networks", "price": 139.90 },
- { "symbol": "AMCR", "name": "Amcor", "price": 42.97 },
- { "symbol": "HBAN", "name": "Huntington Bancshares", "price": 21.94 },
- { "symbol": "EVRG", "name": "Evergy", "price": 85.83 },
- { "symbol": "ABNB", "name": "Airbnb", "price": 109.68 },
- { "symbol": "DASH", "name": "DoorDash", "price": 149.25 },
- { "symbol": "COIN", "name": "Coinbase Global", "price": 145.35 },
- { "symbol": "CIEN", "name": "Ciena Corporation", "price": 235.39 },
- { "symbol": "FANG", "name": "Diamondback Energy", "price": 150.66 },
- { "symbol": "PSKY", "name": "Paramount Skydance Corp", "price": 9.44 },
- { "symbol": "ORLY", "name": "O'Reilly Auto Parts", "price": 92.14 },
- { "symbol": "SBAC", "name": "SBA Communications", "price": 220.40 },
- { "symbol": "ACGL", "name": "Arch Capital Group", "price": 87.91 },
- { "symbol": "CTSH", "name": "Cognizant", "price": 60.15 },
- { "symbol": "VLTO", "name": "Veralto", "price": 110.53 },
- { "symbol": "MPWR", "name": "Monolithic Power Systems", "price": 1094.81 },
- { "symbol": "PODD", "name": "Insulet Corporation", "price": 261.32 },
- { "symbol": "MRSH", "name": "Marsh & McLennan Companies Inc.", "price": 156.59 },
- { "symbol": "PAYC", "name": "Paycom", "price": 133.30 },
- { "symbol": "NTRS", "name": "Northern Trust", "price": 140.60 },
- { "symbol": "ADSK", "name": "Autodesk", "price": 229.35 },
- { "symbol": "MSCI", "name": "MSCI", "price": 383.57 },
- { "symbol": "ORCL", "name": "Oracle Corporation", "price": 175.34 },
- { "symbol": "ERIE", "name": "Erie Indemnity", "price": 329.21 },
- { "symbol": "TECH", "name": "Bio-Techne", "price": 51.66 },
- { "symbol": "TRMB", "name": "Trimble Inc.", "price": 71.90 },
- { "symbol": "EBAY", "name": "eBay", "price": 78.21 },
- { "symbol": "INVH", "name": "Invitation Homes", "price": 25.62 },
- { "symbol": "NDSN", "name": "Nordson Corporation", "price": 310.61 },
- { "symbol": "DECK", "name": "Deckers Brands", "price": 108.29 },
- { "symbol": "ADBE", "name": "Adobe Inc.", "price": 275.09 },
- { "symbol": "SNPS", "name": "Synopsys", "price": 424.09 },
- { "symbol": "CHTR", "name": "Charter Communications", "price": 311.42 },
- { "symbol": "STLD", "name": "Steel Dynamics", "price": 180.50 },
- { "symbol": "BIIB", "name": "Biogen", "price": 210.90 },
- { "symbol": "TRGP", "name": "Targa Resources", "price": 243.04 },
- { "symbol": "SOLV", "name": "Solventum", "price": 57.18 },
- { "symbol": "TROW", "name": "T. Rowe Price", "price": 103.71 },
- { "symbol": "AVGO", "name": "Broadcom", "price": 361.04 },
- { "symbol": "CSCO", "name": "Cisco", "price": 78.72 },
- { "symbol": "CTVA", "name": "Corteva", "price": 75.55 },
- { "symbol": "EXPD", "name": "Expeditors International", "price": 164.36 },
- { "symbol": "EXPE", "name": "Expedia Group", "price": 320.19 },
- { "symbol": "AKAM", "name": "Akamai Technologies", "price": 98.72 },
- { "symbol": "CBRE", "name": "CBRE Group", "price": 123.63 },
- { "symbol": "FFIV", "name": "F5 Inc.", "price": 290.99 },
- { "symbol": "MSFT", "name": "Microsoft", "price": 392.30 },
- { "symbol": "COST", "name": "Costco", "price": 904.79 },
- { "symbol": "NCLH", "name": "Norwegian Cruise Line Holdings", "price": 23.90 },
- { "symbol": "KEYS", "name": "Keysight Technologies", "price": 195.34 },
- { "symbol": "SPGI", "name": "S&P Global", "price": 467.26 },
- { "symbol": "UBER", "name": "Uber", "price": 74.73 },
- { "symbol": "BKNG", "name": "Booking Holdings", "price": 4604.23 },
- { "symbol": "DELL", "name": "Dell Technologies", "price": 137.42 },
- { "symbol": "DXCM", "name": "Dexcom", "price": 59.60 },
- { "symbol": "PYPL", "name": "PayPal", "price": 29.98 },
- { "symbol": "GOOG", "name": "Alphabet Inc. (Class C)", "price": 247.04 },
- { "symbol": "BALL", "name": "Ball Corporation", "price": 79.65 },
- { "symbol": "WELL", "name": "Welltower", "price": 205.91 },
- { "symbol": "MNST", "name": "Monster Beverage", "price": 80.26 },
- { "symbol": "OTIS", "name": "Otis Worldwide", "price": 84.99 },
- { "symbol": "SWKS", "name": "Skyworks Solutions", "price": 65.09 },
- { "symbol": "GRMN", "name": "Garmin", "price": 201.12 },
- { "symbol": "WDAY", "name": "Workday Inc.", "price": 99.40 },
- { "symbol": "APTV", "name": "Aptiv", "price": 79.90 },
- { "symbol": "RVTY", "name": "Revvity", "price": 88.91 },
- { "symbol": "TMUS", "name": "T-Mobile US", "price": 188.82 },
- { "symbol": "LULU", "name": "Lululemon Athletica", "price": 150.36 },
- { "symbol": "HOLX", "name": "Hologic", "price": 60.47 },
- { "symbol": "NWSA", "name": "News Corp (Class A)", "price": 19.11 },
- { "symbol": "PAYX", "name": "Paychex", "price": 88.93 },
- { "symbol": "CDNS", "name": "Cadence Design Systems", "price": 269.75 },
- { "symbol": "ALLE", "name": "Allegion", "price": 187.22 },
- { "symbol": "GILD", "name": "Gilead Sciences", "price": 152.35 },
- { "symbol": "EQIX", "name": "Equinix", "price": 898.66 },
- { "symbol": "ISRG", "name": "Intuitive Surgical", "price": 563.46 }
- ]
-}