mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-23 06:32:26 +01:00
Implemented second-order forecasts for stocks
This commit is contained in:
parent
7035154454
commit
6effda29a9
@ -221,6 +221,10 @@ export let CONSTANTS: IMap<any> = {
|
||||
|
||||
LatestUpdate:
|
||||
`
|
||||
v0.47.0
|
||||
*
|
||||
* Scripts now start/stop instantly
|
||||
|
||||
v0.47.0
|
||||
* Stock Market changes:
|
||||
** Implemented spread. Stock's now have bid and ask prices at which transactions occur
|
||||
|
@ -100,6 +100,12 @@ export class Stock {
|
||||
*/
|
||||
otlkMag: number;
|
||||
|
||||
/**
|
||||
* Forecast of outlook magnitude. Essentially a second-order forecast.
|
||||
* Unlike 'otlkMag', this number is on an absolute scale from 0-100 (rather than 0-50)
|
||||
*/
|
||||
otlkMagForecast: number;
|
||||
|
||||
/**
|
||||
* Average price of stocks that the player owns in the LONG position
|
||||
*/
|
||||
@ -173,6 +179,7 @@ export class Stock {
|
||||
this.mv = toNumber(p.mv);
|
||||
this.b = p.b;
|
||||
this.otlkMag = p.otlkMag;
|
||||
this.otlkMagForecast = this.getAbsoluteForecast();
|
||||
this.cap = getRandomInt(this.price * 1e3, this.price * 25e3);
|
||||
this.spreadPerc = toNumber(p.spreadPerc);
|
||||
this.priceMovementPerc = this.spreadPerc / (getRandomInt(10, 30) / 10);
|
||||
@ -189,11 +196,62 @@ export class Stock {
|
||||
this.maxShares = Math.round((this.totalShares * outstandingSharePercentage) / 1e5) * 1e5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the stock to a new price. Also updates the stock's previous price tracker
|
||||
*/
|
||||
changePrice(newPrice: number): void {
|
||||
this.lastPrice = this.price;
|
||||
this.price = newPrice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the stock's forecast during a stock market 'tick'.
|
||||
* The way a stock's forecast changes depends on various internal properties,
|
||||
* but is ultimately determined by RNG
|
||||
*/
|
||||
cycleForecast(changeAmt: number=0.1): void {
|
||||
const increaseChance = this.getForecastIncreaseChance();
|
||||
|
||||
if (Math.random() < increaseChance) {
|
||||
// Forecast increases
|
||||
if (this.b) {
|
||||
this.otlkMag += changeAmt;
|
||||
} else {
|
||||
this.otlkMag -= changeAmt;
|
||||
}
|
||||
} else {
|
||||
// Forecast decreases
|
||||
if (this.b) {
|
||||
this.otlkMag -= changeAmt;
|
||||
} else {
|
||||
this.otlkMag += changeAmt;
|
||||
}
|
||||
}
|
||||
|
||||
this.otlkMag = Math.min(this.otlkMag, 50);
|
||||
if (this.otlkMag < 0) {
|
||||
this.otlkMag *= -1;
|
||||
this.b = !this.b;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* "Flip" the stock's second-order forecast. This can occur during a
|
||||
* stock market "cycle" (determined by RNG). It is used to simulate
|
||||
* RL stock market cycles and introduce volatility
|
||||
*/
|
||||
flipForecastForecast(): void {
|
||||
const diff = this.otlkMagForecast - 50;
|
||||
this.otlkMagForecast = 50 + (-1 * diff);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the stock's absolute forecast, which is a number between 0-100
|
||||
*/
|
||||
getAbsoluteForecast(): number {
|
||||
return this.b ? 50 + this.otlkMag : 50 - this.otlkMag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the price at which YOUR stock is bought (market ask price). Accounts for spread
|
||||
*/
|
||||
@ -208,6 +266,15 @@ export class Stock {
|
||||
return this.price * (1 - (this.spreadPerc / 100));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the chance (0-1 decimal) that a stock has of having its forecast increase
|
||||
*/
|
||||
getForecastIncreaseChance(): number {
|
||||
const diff = this.otlkMagForecast - this.getAbsoluteForecast();
|
||||
|
||||
return (50 + Math.min(Math.max(diff, -45), 45)) / 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the Stock to a JSON save state.
|
||||
*/
|
||||
|
@ -200,9 +200,15 @@ export function stockMarketCycle() {
|
||||
if (!(stock instanceof Stock)) { continue; }
|
||||
let thresh = 0.6;
|
||||
if (stock.b) { thresh = 0.4; }
|
||||
if (Math.random() < thresh) {
|
||||
stock.b = !stock.b;
|
||||
if (stock.otlkMag < 5) { stock.otlkMag += 0.1; }
|
||||
const roll = Math.random();
|
||||
if (roll < 0.4) {
|
||||
stock.flipForecastForecast();
|
||||
} else if (roll < 0.6) {
|
||||
stock.otlkMagForecast += 0.5;
|
||||
stock.otlkMagForecast = Math.min(stock.otlkMagForecast * 1.02, 50);
|
||||
} else if (roll < 0.8) {
|
||||
stock.otlkMagForecast -= 0.5;
|
||||
stock.otlkMagForecast = otlkMagForecast * (1 / 1.02);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -264,23 +270,14 @@ export function processStockPrices(numCycles=1) {
|
||||
}
|
||||
|
||||
let otlkMagChange = stock.otlkMag * av;
|
||||
if (stock.otlkMag <= 0.1) {
|
||||
if (stock.otlkMag < 1) {
|
||||
otlkMagChange = 1;
|
||||
}
|
||||
if (c < 0.5) {
|
||||
stock.otlkMag += otlkMagChange;
|
||||
} else {
|
||||
stock.otlkMag -= otlkMagChange;
|
||||
}
|
||||
if (stock.otlkMag > 50) { stock.otlkMag = 50; } // Cap so the "forecast" is between 0 and 100
|
||||
if (stock.otlkMag < 0) {
|
||||
stock.otlkMag *= -1;
|
||||
stock.b = !stock.b;
|
||||
}
|
||||
stock.cycleForecast(otlkMagChange);
|
||||
|
||||
// Shares required for price movement gradually approaches max over time
|
||||
stock.shareTxUntilMovement = Math.min(stock.shareTxUntilMovementUp + 5, stock.shareTxForMovement);
|
||||
stock.shareTxUntilMovement = Math.min(stock.shareTxUntilMovementDown + 5, stock.shareTxForMovement);
|
||||
stock.shareTxUntilMovementUp = Math.min(stock.shareTxUntilMovementUp + 5, stock.shareTxForMovement);
|
||||
stock.shareTxUntilMovementDown = Math.min(stock.shareTxUntilMovementDown + 5, stock.shareTxForMovement);
|
||||
}
|
||||
|
||||
displayStockMarketContent();
|
||||
|
@ -27,6 +27,7 @@ export function StockTickerHeaderText(props: IProps): React.ReactElement {
|
||||
let plusOrMinus = stock.b; // True for "+", false for "-"
|
||||
if (stock.otlkMag < 0) { plusOrMinus = !plusOrMinus }
|
||||
hdrText += (plusOrMinus ? "+" : "-").repeat(Math.floor(Math.abs(stock.otlkMag) / 10) + 1);
|
||||
hdrText += ` - ${stock.getAbsoluteForecast()} / ${stock.otlkMagForecast}`;
|
||||
}
|
||||
|
||||
let styleMarkup = {
|
||||
|
Loading…
Reference in New Issue
Block a user