Skip to content

Commit

Permalink
Merge branch 'dev' into test-development
Browse files Browse the repository at this point in the history
  • Loading branch information
nolydvo authored May 25, 2026
2 parents 18a74af + 69f7abe commit 2ef6cb7
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 42 deletions.
3 changes: 2 additions & 1 deletion src/main/java/millions/controller/GameController.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package millions.controller;

import java.io.FileNotFoundException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.file.Path;
Expand Down Expand Up @@ -38,7 +39,7 @@ public void startGame(

player = new Player(name, startingMoney);

} catch (UncheckedFileNotFoundException e) {
} catch (FileNotFoundException e) {
throw new UncheckedFileNotFoundException(e.getMessage());
} catch (InvalidFormatException e) {
throw new InvalidFormatException(e.getMessage());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import millions.model.Stock;


import java.io.FileNotFoundException;
import java.nio.file.Path;
import java.util.List;

Expand All @@ -28,7 +29,7 @@ public CSVFileHandler() {
* @throws InvalidFormatException Throws an InvalidFormatException received from parser
* @throws UncheckedFileNotFoundException Upon Receiving a FilenotFoundException
*/
public List<Stock> getStocksFromFile(Path filePath) {
public List<Stock> getStocksFromFile(Path filePath) throws FileNotFoundException {
try {
StockFileReader reader = new StockFileReader();
List<String> lines = reader.readFile(filePath);
Expand All @@ -38,8 +39,8 @@ public List<Stock> getStocksFromFile(Path filePath) {

} catch (InvalidFormatException e) {
throw new InvalidFormatException(e.getMessage());
} catch (UncheckedFileNotFoundException e) {
throw new UncheckedFileNotFoundException(e.getMessage());
} catch (FileNotFoundException e) {
throw new FileNotFoundException(e.getMessage());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ public CSVStockFileParser() {}
public boolean verifyCSV(List<String> lines) {
return lines.stream()
.filter(l -> !(l.startsWith("#") || l.isBlank()))
.noneMatch(l -> l.split(",").length != 3);
.noneMatch(l -> l.split(",").length != 4 || l.split(",")[3].split(";").length != 6);
}

/**
* Parses the supplied lines if they satisfy the correct format expectations
*
* @param lines <p>lines to be parsed. <br>
* Each line must contain three data fields: String,String,BigDecimal <br>
* Each line must contain four data fields: String,String,BigDecimal,String <br>
* (Fields cannot be blank) <br>
* blank lines or lines beginning with '#' are ignored
* </p>
Expand All @@ -49,9 +49,14 @@ public List<Stock> parse(List<String> lines) {
String symbol = split[0];
String company = split[1];
BigDecimal price = new BigDecimal(split[2]);
stocks.add(new Stock(symbol, company, price));
String[] functionValues = split[3].split(";");
List<BigDecimal> convertedFunctionValues = new ArrayList<>();
for (String functionValue : functionValues) {
convertedFunctionValues.add(new BigDecimal(functionValue));
}
stocks.add(new Stock(symbol, company, price, convertedFunctionValues));
} catch (NumberFormatException e) {
throw new InvalidFormatException("Error with number conversion on line: " + l + "\n" + "Last field must be a number");
throw new InvalidFormatException("Error with number conversion on line: " + l + "\n" + "ensure all number fields are actually numbers");
} catch (IllegalArgumentException e) {
throw new InvalidFormatException("Illegal argument on line: " + l + "\n" + e.getMessage());
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/millions/controller/fileIO/StockFileReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public StockFileReader() {}
* @return List of each line in the file as a string
* @throws UncheckedFileNotFoundException Upon encountering a FileNotFoundException
*/
public List<String> readFile(Path path) {
public List<String> readFile(Path path) throws FileNotFoundException {
File file = new File(path.toString());
List<String> lines = new ArrayList<>();
try (Reader reader = new FileReader(file);
Expand All @@ -34,7 +34,7 @@ public List<String> readFile(Path path) {
}
} catch (IOException e) {
if (e instanceof FileNotFoundException) {
throw new UncheckedFileNotFoundException("Couldn't find file at specified path");
throw new FileNotFoundException("Couldn't find file at specified path");
}
else {
logger.log(Level.SEVERE, "Encountered unexpected IOException: ", e.getMessage());
Expand Down
27 changes: 16 additions & 11 deletions src/main/java/millions/model/Exchange.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@
import java.util.Map;
import java.util.Random;
import java.util.stream.Collectors;
import millions.model.calculators.PriceChangeCalculator;
import millions.model.factories.PurchaseFactory;
import millions.model.factories.SaleFactory;
import millions.model.factories.TransactionFactory;

/**
* The stock exchange where players buy and sell shares. Manages stocks and simulates weekly price changes.
* The stock exchange where players buy and sell shares. Manages stocks and simulates weekly price
* changes.
*/
public class Exchange {
private String name;
Expand Down Expand Up @@ -139,19 +141,22 @@ public List<Stock> getLosers(int limit) {
.collect(Collectors.toList());
}

/**
* Advances the current game week by performing new price calculations for all stocks.
*/
/** Advances the current game week by performing new price calculations for all stocks. */
public void advance() {
PriceChangeCalculator priceChangeCalculator = new PriceChangeCalculator();
this.weekNumber++;
for (Stock stock : this.stocks.values()) {
double change = 0.9 + random.nextDouble() * 0.2;
stock.addNewSalesPrice(
stock
.getSalesPrice()
.multiply(BigDecimal.valueOf(change))
.setScale(2, RoundingMode.HALF_UP));
// RoundingMode from AI suggestion
BigDecimal change = priceChangeCalculator.calculateChange(stock);
stock.addNewSalesPrice(stock.getSalesPrice().add(change).setScale(2, RoundingMode.HALF_UP));
// Round to stop crazy values

// double change = 0.9 + random.nextDouble() * 0.2;
// stock.addNewSalesPrice(
// stock
// .getSalesPrice()
// .multiply(BigDecimal.valueOf(change))
// .setScale(2, RoundingMode.HALF_UP));
// // RoundingMode from AI suggestion
}
notifyWeekAdvanced();
}
Expand Down
19 changes: 16 additions & 3 deletions src/main/java/millions/model/Stock.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,27 @@
public class Stock {
String symbol;
String company;
List<BigDecimal> volatility;
List<BigDecimal> prices;

/**
* @param symbol Stock ticker symbol
* @param company company name
* @param prices List of prices
* @param volatilityParameters numbers used for price change calculation functions
* @throws IllegalArgumentException
*/
public Stock(String symbol, String company, List<BigDecimal> prices) {
public Stock(String symbol, String company, List<BigDecimal> prices, List<BigDecimal> volatilityParameters) {
this.symbol = symbol;
this.company = company;
this.prices = new ArrayList<>(prices);

this.volatility = volatilityParameters;

if (volatilityParameters.size() != 6) {
throw new IllegalArgumentException("Invalid volatility function count");
}

if (symbol == null || symbol.isBlank()) {
throw new IllegalArgumentException("Symbol cannot be null or blank");
}
Expand All @@ -30,9 +38,10 @@ public Stock(String symbol, String company, List<BigDecimal> prices) {
}
}


/** Stock() with single price instead of list. */
public Stock(String symbol, String company, BigDecimal initialPrice) {
this(symbol, company, new ArrayList<>(List.of(initialPrice)));
public Stock(String symbol, String company, BigDecimal initialPrice, List<BigDecimal> volatilityFunctions) {
this(symbol, company, new ArrayList<>(List.of(initialPrice)), volatilityFunctions);
}

/**
Expand Down Expand Up @@ -110,6 +119,10 @@ public BigDecimal getLatestPriceChange() {
return currentPrice.subtract(lastPrice);
}

public List<BigDecimal> getVolatilityParameters() {
return this.volatility;
}

@Override
public String toString() {
return "Stock [symbol: " + symbol + ", company: " + company + ", prices: " + prices + "]";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package millions.model.calculators;

import java.math.BigDecimal;
import java.util.List;
import millions.model.Stock;

public class PriceChangeCalculator {

public PriceChangeCalculator() {}

public BigDecimal calculateChange(Stock stock) {
List<BigDecimal> values = stock.getVolatilityParameters();
int week = stock.getHistoricalPrices().size();
BigDecimal change = BigDecimal.ZERO;
change = change.add(drift(values.get(0)));
change = change.add(volatility(values.get(1)));
change = change.add(cycle(values.get(2), values.get(3), week));
change = change.add(explosion(values.get(4), values.get(5)));
return stock.getSalesPrice().multiply(change);
}

/*
* Flat slope change, eg upwards/downwards slope
*/
private BigDecimal drift(BigDecimal input) {
if (input.equals(BigDecimal.ZERO)) {
return BigDecimal.ZERO;
}
return input;
}

/*
* Random noise of size x
*/
private BigDecimal volatility(BigDecimal input) {
if (input.equals(BigDecimal.ZERO)) {
return BigDecimal.ZERO;
}
return input.multiply(BigDecimal.valueOf(Math.random() * 2 - 1));
}

/*
* Sinus curve based on week, times size of the curve
*/
private BigDecimal cycle(BigDecimal speed, BigDecimal size, int week) {
if (speed.equals(BigDecimal.ZERO) || size.equals(BigDecimal.ZERO)) {
return BigDecimal.ZERO;
}
return size.multiply(BigDecimal.valueOf(Math.sin(week * speed.doubleValue())));
}

/*
* probability% change of an explosion of size% positive or negative
*/
private BigDecimal explosion(BigDecimal probability, BigDecimal size) {
if (probability.equals(BigDecimal.ZERO) || size.equals(BigDecimal.ZERO)) {
return BigDecimal.ZERO;
}
if (Math.random() >= probability.doubleValue()) {
return BigDecimal.ZERO;
}
return Math.random() < 0.5 ? size : size.negate();
}
}
Loading

0 comments on commit 2ef6cb7

Please sign in to comment.