mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-21 05:35:45 +01:00
Finished implementing player influencing on stock 2nd-order forecasts. Balanced recent stock market changes
This commit is contained in:
parent
8398fd47f0
commit
35f8a5115a
@ -224,7 +224,9 @@ export let CONSTANTS: IMap<any> = {
|
||||
v0.47.0
|
||||
* Stock Market changes:
|
||||
** Transactions no longer influence stock prices (but they still influence forecast)
|
||||
** Changed the way stock's behave, particularly with regard to how the stock forecast occasionally "flips"
|
||||
** Changed the way stocks behave, particularly with regard to how the stock forecast occasionally "flips"
|
||||
** Hacking & growing a server can potentially affect the way the corresponding stock's forecast changes
|
||||
** Working for a company positively affects the way the corresponding stock's forecast changes
|
||||
|
||||
* Scripts now start/stop instantly
|
||||
* Improved performance when starting up many copies of a new script (by Ornedan)
|
||||
|
@ -90,6 +90,10 @@ import {
|
||||
shortStock,
|
||||
sellShort,
|
||||
} from "./StockMarket/BuyingAndSelling";
|
||||
import {
|
||||
influenceStockThroughServerHack,
|
||||
influenceStockThroughServerGrow,
|
||||
} from "./StockMarket/PlayerInfluencing";
|
||||
import { Stock } from "./StockMarket/Stock";
|
||||
import {
|
||||
StockMarket,
|
||||
@ -439,7 +443,7 @@ function NetscriptFunctions(workerScript) {
|
||||
}
|
||||
return out;
|
||||
},
|
||||
hack : function(ip, { threads: requestedThreads } = {}){
|
||||
hack : function(ip, { threads: requestedThreads, stock } = {}){
|
||||
updateDynamicRam("hack", getRamCost("hack"));
|
||||
if (ip === undefined) {
|
||||
throw makeRuntimeRejectMsg(workerScript, "Hack() call has incorrect number of arguments. Takes 1 argument");
|
||||
@ -501,6 +505,9 @@ function NetscriptFunctions(workerScript) {
|
||||
workerScript.scriptRef.log("Script SUCCESSFULLY hacked " + server.hostname + " for $" + formatNumber(moneyGained, 2) + " and " + formatNumber(expGainedOnSuccess, 4) + " exp (t=" + threads + ")");
|
||||
}
|
||||
server.fortify(CONSTANTS.ServerFortifyAmount * Math.min(threads, maxThreadNeeded));
|
||||
if (stock) {
|
||||
influenceStockThroughServerHack(server, moneyGained);
|
||||
}
|
||||
return Promise.resolve(moneyGained);
|
||||
} else {
|
||||
// Player only gains 25% exp for failure?
|
||||
@ -555,7 +562,7 @@ function NetscriptFunctions(workerScript) {
|
||||
return Promise.resolve(true);
|
||||
});
|
||||
},
|
||||
grow : function(ip, { threads: requestedThreads } = {}){
|
||||
grow : function(ip, { threads: requestedThreads, stock } = {}){
|
||||
updateDynamicRam("grow", getRamCost("grow"));
|
||||
const threads = resolveNetscriptRequestedThreads(workerScript, "grow", requestedThreads);
|
||||
if (ip === undefined) {
|
||||
@ -596,6 +603,9 @@ function NetscriptFunctions(workerScript) {
|
||||
}
|
||||
workerScript.scriptRef.onlineExpGained += expGain;
|
||||
Player.gainHackingExp(expGain);
|
||||
if (stock) {
|
||||
influenceStockThroughServerGrow(server, moneyAfter - moneyBefore);
|
||||
}
|
||||
return Promise.resolve(moneyAfter/moneyBefore);
|
||||
});
|
||||
},
|
||||
|
@ -39,6 +39,7 @@ import { SpecialServerIps, SpecialServerNames } from "../../Server/SpecialServer
|
||||
import { applySourceFile } from "../../SourceFile/applySourceFile";
|
||||
import { SourceFiles } from "../../SourceFile/SourceFiles";
|
||||
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
|
||||
import { influenceStockThroughCompanyWork } from "../../StockMarket/PlayerInfluencing";
|
||||
|
||||
import Decimal from "decimal.js";
|
||||
|
||||
@ -580,8 +581,8 @@ export function startWork(companyName) {
|
||||
}
|
||||
|
||||
export function work(numCycles) {
|
||||
//Cap the number of cycles being processed to whatever would put you at
|
||||
//the work time limit (8 hours)
|
||||
// Cap the number of cycles being processed to whatever would put you at
|
||||
// the work time limit (8 hours)
|
||||
var overMax = false;
|
||||
if (this.timeWorked + (Engine._idleSpeed * numCycles) >= CONSTANTS.MillisecondsPer8Hours) {
|
||||
overMax = true;
|
||||
@ -589,21 +590,24 @@ export function work(numCycles) {
|
||||
}
|
||||
this.timeWorked += Engine._idleSpeed * numCycles;
|
||||
|
||||
this.workRepGainRate = this.getWorkRepGain();
|
||||
this.workRepGainRate = this.getWorkRepGain();
|
||||
this.processWorkEarnings(numCycles);
|
||||
|
||||
//If timeWorked == 8 hours, then finish. You can only gain 8 hours worth of exp and money
|
||||
// If timeWorked == 8 hours, then finish. You can only gain 8 hours worth of exp and money
|
||||
if (overMax || this.timeWorked >= CONSTANTS.MillisecondsPer8Hours) {
|
||||
return this.finishWork(false);
|
||||
}
|
||||
|
||||
var comp = Companies[this.companyName], companyRep = "0";
|
||||
const comp = Companies[this.companyName];
|
||||
let companyRep = "0";
|
||||
if (comp == null || !(comp instanceof Company)) {
|
||||
console.error(`Could not find Company: ${this.companyName}`);
|
||||
} else {
|
||||
companyRep = comp.playerReputation;
|
||||
}
|
||||
|
||||
influenceStockThroughCompanyWork(comp, this.workRepGainRate, numCycles);
|
||||
|
||||
const position = this.jobs[this.companyName];
|
||||
|
||||
var txt = document.getElementById("work-in-progress-text");
|
||||
|
@ -5,6 +5,7 @@ export type IStockMarket = {
|
||||
[key: string]: Stock;
|
||||
} & {
|
||||
lastUpdate: number;
|
||||
storedCycles: number;
|
||||
Orders: IOrderBook;
|
||||
}
|
||||
storedCycles: number;
|
||||
ticksUntilCycle: number;
|
||||
};
|
||||
|
@ -2,5 +2,79 @@
|
||||
* Implementation of the mechanisms that allow the player to affect the
|
||||
* Stock Market
|
||||
*/
|
||||
import { Server } from "../Server/Server";
|
||||
import { Stock } from "./Stock";
|
||||
import { StockMarket } from "./StockMarket";
|
||||
|
||||
import { Company } from "../Company/Company";
|
||||
import { Server } from "../Server/Server";
|
||||
|
||||
// Change in second-order forecast due to hacks/grows
|
||||
const forecastForecastChangeFromHack = 0.1;
|
||||
|
||||
// Change in second-order forecast due to company work
|
||||
const forecastForecastChangeFromCompanyWork = 0.001;
|
||||
|
||||
/**
|
||||
* Potentially decreases a stock's second-order forecast when its corresponding
|
||||
* server is hacked. The chance of the hack decreasing the stock's second-order
|
||||
* forecast is dependent on what percentage of the server's money is hacked
|
||||
* @param {Server} server - Server being hack()ed
|
||||
* @param {number} moneyHacked - Amount of money stolen from the server
|
||||
*/
|
||||
export function influenceStockThroughServerHack(server: Server, moneyHacked: number): void {
|
||||
const orgName = server.organizationName;
|
||||
let stock: Stock | null = null;
|
||||
if (typeof orgName === "string" && orgName !== "") {
|
||||
stock = StockMarket[orgName];
|
||||
}
|
||||
if (!(stock instanceof Stock)) { return; }
|
||||
|
||||
const percTotalMoneyHacked = moneyHacked / server.moneyMax;
|
||||
if (Math.random() < percTotalMoneyHacked) {
|
||||
console.log(`Influencing stock ${stock.name}`);
|
||||
stock.changeForecastForecast(stock.otlkMagForecast - forecastForecastChangeFromHack);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Potentially increases a stock's second-order forecast when its corresponding
|
||||
* server is grown (grow()). The chance of the grow() to increase the stock's
|
||||
* second-order forecast is dependent on how much money is added to the server
|
||||
* @param {Server} server - Server being grow()n
|
||||
* @param {number} moneyHacked - Amount of money added to the server
|
||||
*/
|
||||
export function influenceStockThroughServerGrow(server: Server, moneyGrown: number): void {
|
||||
const orgName = server.organizationName;
|
||||
let stock: Stock | null = null;
|
||||
if (typeof orgName === "string" && orgName !== "") {
|
||||
stock = StockMarket[orgName];
|
||||
}
|
||||
if (!(stock instanceof Stock)) { return; }
|
||||
|
||||
const percTotalMoneyGrown = moneyGrown / server.moneyMax;
|
||||
if (Math.random() < percTotalMoneyGrown) {
|
||||
console.log(`Influencing stock ${stock.name}`);
|
||||
stock.changeForecastForecast(stock.otlkMagForecast + forecastForecastChangeFromHack);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Potentially increases a stock's second-order forecast when the player works for
|
||||
* its corresponding company.
|
||||
* @param {Company} company - Company being worked for
|
||||
* @param {number} performanceMult - Effectiveness of player's work. Affects influence
|
||||
* @param {number} cyclesOfWork - # game cycles of work being processed
|
||||
*/
|
||||
export function influenceStockThroughCompanyWork(company: Company, performanceMult: number, cyclesOfWork: number): void {
|
||||
const compName = company.name;
|
||||
let stock: Stock | null = null;
|
||||
if (typeof compName === "string" && compName !== "") {
|
||||
stock = StockMarket[compName];
|
||||
}
|
||||
if (!(stock instanceof Stock)) { return; }
|
||||
|
||||
if (Math.random() < 0.001 * cyclesOfWork) {
|
||||
const change = forecastForecastChangeFromCompanyWork * performanceMult;
|
||||
stock.changeForecastForecast(stock.otlkMagForecast + change);
|
||||
}
|
||||
}
|
||||
|
@ -187,6 +187,18 @@ export class Stock {
|
||||
this.maxShares = Math.round((this.totalShares * outstandingSharePercentage) / 1e5) * 1e5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely set the stock's second-order forecast to a new value
|
||||
*/
|
||||
changeForecastForecast(newff: number): void {
|
||||
this.otlkMagForecast = newff;
|
||||
if (this.otlkMagForecast > 100) {
|
||||
this.otlkMagForecast = 100;
|
||||
} else if (this.otlkMagForecast < 0) {
|
||||
this.otlkMagForecast = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the stock to a new price. Also updates the stock's previous price tracker
|
||||
*/
|
||||
@ -232,9 +244,9 @@ export class Stock {
|
||||
*/
|
||||
cycleForecastForecast(changeAmt: number=0.1): void {
|
||||
if (Math.random() < 0.5) {
|
||||
this.otlkMagForecast = Math.min(this.otlkMagForecast + changeAmt, 100);
|
||||
this.changeForecastForecast(this.otlkMagForecast + changeAmt);
|
||||
} else {
|
||||
this.otlkMagForecast = Math.max(this.otlkMagForecast - changeAmt, 0);
|
||||
this.changeForecastForecast(this.otlkMagForecast - changeAmt);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,14 +4,12 @@ import {
|
||||
shortStock,
|
||||
sellShort,
|
||||
} from "./BuyingAndSelling";
|
||||
import { IOrderBook } from "./IOrderBook";
|
||||
import { IStockMarket } from "./IStockMarket";
|
||||
import { Order } from "./Order";
|
||||
import { processOrders } from "./OrderProcessing";
|
||||
import { Stock } from "./Stock";
|
||||
import { TicksPerCycle } from "./StockMarketConstants";
|
||||
import {
|
||||
getStockMarket4SDataCost,
|
||||
getStockMarket4STixApiCost
|
||||
} from "./StockMarketCosts";
|
||||
import { InitStockMetadata } from "./data/InitStockMetadata";
|
||||
import { OrderTypes } from "./data/OrderTypes";
|
||||
import { PositionTypes } from "./data/PositionTypes";
|
||||
@ -21,6 +19,7 @@ import { StockMarketRoot } from "./ui/Root";
|
||||
import { CONSTANTS } from "../Constants";
|
||||
import { WorkerScript } from "../Netscript/WorkerScript";
|
||||
import { Player } from "../Player";
|
||||
import { IMap } from "../types";
|
||||
|
||||
import { Page, routing } from ".././ui/navigationTracking";
|
||||
import { numeralWrapper } from ".././ui/numeralFormat";
|
||||
@ -28,17 +27,17 @@ import { numeralWrapper } from ".././ui/numeralFormat";
|
||||
import { dialogBoxCreate } from "../../utils/DialogBox";
|
||||
import { Reviver } from "../../utils/JSONReviver";
|
||||
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
|
||||
export let StockMarket = {}; // Maps full stock name -> Stock object
|
||||
export let SymbolToStockMap = {}; // Maps symbol -> Stock object
|
||||
export let StockMarket: IStockMarket | IMap<any> = {}; // Maps full stock name -> Stock object
|
||||
export let SymbolToStockMap: IMap<Stock> = {}; // Maps symbol -> Stock object
|
||||
|
||||
export function placeOrder(stock, shares, price, type, position, workerScript=null) {
|
||||
export function placeOrder(stock: Stock, shares: number, price: number, type: OrderTypes, position: PositionTypes, workerScript: WorkerScript | null=null) {
|
||||
const tixApi = (workerScript instanceof WorkerScript);
|
||||
if (!(stock instanceof Stock)) {
|
||||
if (tixApi) {
|
||||
workerScript.log(`ERROR: Invalid stock passed to placeOrder() function`);
|
||||
workerScript!.log(`ERROR: Invalid stock passed to placeOrder() function`);
|
||||
} else {
|
||||
dialogBoxCreate(`ERROR: Invalid stock passed to placeOrder() function`);
|
||||
}
|
||||
@ -46,7 +45,7 @@ export function placeOrder(stock, shares, price, type, position, workerScript=nu
|
||||
}
|
||||
if (typeof shares !== "number" || typeof price !== "number") {
|
||||
if (tixApi) {
|
||||
workerScript.log("ERROR: Invalid numeric value provided for either 'shares' or 'price' argument");
|
||||
workerScript!.log("ERROR: Invalid numeric value provided for either 'shares' or 'price' argument");
|
||||
} else {
|
||||
dialogBoxCreate("ERROR: Invalid numeric value provided for either 'shares' or 'price' argument");
|
||||
}
|
||||
@ -55,7 +54,7 @@ export function placeOrder(stock, shares, price, type, position, workerScript=nu
|
||||
|
||||
const order = new Order(stock.symbol, shares, price, type, position);
|
||||
if (StockMarket["Orders"] == null) {
|
||||
const orders = {};
|
||||
const orders: IOrderBook = {};
|
||||
for (const name in StockMarket) {
|
||||
const stk = StockMarket[name];
|
||||
if (!(stk instanceof Stock)) { continue; }
|
||||
@ -68,7 +67,7 @@ export function placeOrder(stock, shares, price, type, position, workerScript=nu
|
||||
// Process to see if it should be executed immediately
|
||||
const processOrderRefs = {
|
||||
rerenderFn: displayStockMarketContent,
|
||||
stockMarket: StockMarket,
|
||||
stockMarket: StockMarket as IStockMarket,
|
||||
symbolToStockMap: SymbolToStockMap,
|
||||
}
|
||||
processOrders(stock, order.type, order.pos, processOrderRefs);
|
||||
@ -78,7 +77,15 @@ export function placeOrder(stock, shares, price, type, position, workerScript=nu
|
||||
}
|
||||
|
||||
// Returns true if successfully cancels an order, false otherwise
|
||||
export function cancelOrder(params, workerScript=null) {
|
||||
interface ICancelOrderParams {
|
||||
order?: Order;
|
||||
pos?: PositionTypes;
|
||||
price?: number;
|
||||
shares?: number;
|
||||
stock?: Stock;
|
||||
type?: OrderTypes;
|
||||
}
|
||||
export function cancelOrder(params: ICancelOrderParams, workerScript: WorkerScript | null=null) {
|
||||
var tixApi = (workerScript instanceof WorkerScript);
|
||||
if (StockMarket["Orders"] == null) {return false;}
|
||||
if (params.order && params.order instanceof Order) {
|
||||
@ -108,42 +115,24 @@ export function cancelOrder(params, workerScript=null) {
|
||||
stockOrders.splice(i, 1);
|
||||
displayStockMarketContent();
|
||||
if (tixApi) {
|
||||
workerScript.scriptRef.log("Successfully cancelled order: " + orderTxt);
|
||||
workerScript!.scriptRef.log("Successfully cancelled order: " + orderTxt);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (tixApi) {
|
||||
workerScript.scriptRef.log("Failed to cancel order: " + orderTxt);
|
||||
workerScript!.scriptRef.log("Failed to cancel order: " + orderTxt);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function loadStockMarket(saveString) {
|
||||
export function loadStockMarket(saveString: string) {
|
||||
if (saveString === "") {
|
||||
StockMarket = {};
|
||||
} else {
|
||||
StockMarket = JSON.parse(saveString, Reviver);
|
||||
|
||||
// Backwards compatibility for v0.47.0
|
||||
const orderBook = StockMarket["Orders"];
|
||||
if (orderBook != null) {
|
||||
// For each order, set its 'stockSymbol' property equal to the
|
||||
// symbol of its 'stock' property
|
||||
for (const stockSymbol in orderBook) {
|
||||
const ordersForStock = orderBook[stockSymbol];
|
||||
if (Array.isArray(ordersForStock)) {
|
||||
for (const order of ordersForStock) {
|
||||
if (order instanceof Order && order.stock instanceof Stock) {
|
||||
order.stockSymbol = order.stock.symbol;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(`Converted Stock Market order book to v0.47.0 format`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -163,7 +152,7 @@ export function initStockMarket() {
|
||||
StockMarket[name] = new Stock(metadata);
|
||||
}
|
||||
|
||||
const orders = {};
|
||||
const orders: IOrderBook = {};
|
||||
for (const name in StockMarket) {
|
||||
const stock = StockMarket[name];
|
||||
if (!(stock instanceof Stock)) { continue; }
|
||||
@ -196,11 +185,13 @@ export function stockMarketCycle() {
|
||||
if (!(stock instanceof Stock)) { continue; }
|
||||
|
||||
const roll = Math.random();
|
||||
if (roll < 0.4) {
|
||||
if (roll < 0.2) {
|
||||
stock.flipForecastForecast();
|
||||
} else if (roll < 0.55) {
|
||||
StockMarket.ticksUntilCycle = 4 * TicksPerCycle;
|
||||
} else if (roll < 0.65) {
|
||||
stock.b = !stock.b;
|
||||
stock.flipForecastForecast();
|
||||
StockMarket.ticksUntilCycle = TicksPerCycle;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -226,7 +217,6 @@ export function processStockPrices(numCycles=1) {
|
||||
--StockMarket.ticksUntilCycle;
|
||||
if (StockMarket.ticksUntilCycle <= 0) {
|
||||
stockMarketCycle();
|
||||
StockMarket.ticksUntilCycle = TicksPerCycle;
|
||||
}
|
||||
|
||||
var v = Math.random();
|
||||
@ -251,7 +241,7 @@ export function processStockPrices(numCycles=1) {
|
||||
const c = Math.random();
|
||||
const processOrderRefs = {
|
||||
rerenderFn: displayStockMarketContent,
|
||||
stockMarket: StockMarket,
|
||||
stockMarket: StockMarket as IStockMarket,
|
||||
symbolToStockMap: SymbolToStockMap,
|
||||
}
|
||||
if (c < chc) {
|
||||
@ -282,7 +272,7 @@ export function processStockPrices(numCycles=1) {
|
||||
displayStockMarketContent();
|
||||
}
|
||||
|
||||
let stockMarketContainer = null;
|
||||
let stockMarketContainer: HTMLElement | null = null;
|
||||
function setStockMarketContainer() {
|
||||
stockMarketContainer = document.getElementById("stock-market-container");
|
||||
document.removeEventListener("DOMContentLoaded", setStockMarketContainer);
|
||||
@ -301,6 +291,7 @@ export function displayStockMarketContent() {
|
||||
}
|
||||
|
||||
if (stockMarketContainer instanceof HTMLElement) {
|
||||
const castedStockMarket = StockMarket as IStockMarket;
|
||||
ReactDOM.render(
|
||||
<StockMarketRoot
|
||||
buyStockLong={buyStock}
|
||||
@ -311,7 +302,7 @@ export function displayStockMarketContent() {
|
||||
placeOrder={placeOrder}
|
||||
sellStockLong={sellStock}
|
||||
sellStockShort={sellShort}
|
||||
stockMarket={StockMarket}
|
||||
stockMarket={castedStockMarket}
|
||||
/>,
|
||||
stockMarketContainer
|
||||
)
|
@ -2,4 +2,4 @@
|
||||
* How many stock market 'ticks' before a 'cycle' is triggered.
|
||||
* A 'tick' is whenver stock prices update
|
||||
*/
|
||||
export const TicksPerCycle = 100;
|
||||
export const TicksPerCycle = 80;
|
||||
|
@ -6,7 +6,7 @@ import { PositionTypes } from "./data/PositionTypes";
|
||||
import { CONSTANTS } from "../Constants";
|
||||
|
||||
// Amount by which a stock's forecast changes during each price movement
|
||||
export const forecastChangePerPriceMovement = 0.005;
|
||||
export const forecastChangePerPriceMovement = 0.01;
|
||||
|
||||
/**
|
||||
* Calculate the total cost of a "buy" transaction. This accounts for spread and commission.
|
||||
@ -97,10 +97,8 @@ export function processTransactionForecastMovement(stock: Stock, shares: number)
|
||||
|
||||
|
||||
// Forecast always decreases in magnitude
|
||||
const forecastChange = Math.min(5, forecastChangePerPriceMovement * (numIterations - 1));
|
||||
if (stock.otlkMag > 10) {
|
||||
stock.otlkMag -= forecastChange;
|
||||
}
|
||||
const forecastChange = forecastChangePerPriceMovement * (numIterations - 1);
|
||||
stock.otlkMag = Math.max(6, stock.otlkMag - forecastChange);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -128,6 +128,22 @@ describe("Stock Market Tests", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("#changeForecastForecast()", function() {
|
||||
it("should get the stock's second-order forecast property", function() {
|
||||
stock.changeForecastForecast(99);
|
||||
expect(stock.otlkMagForecast).to.equal(99);
|
||||
stock.changeForecastForecast(1);
|
||||
expect(stock.otlkMagForecast).to.equal(1);
|
||||
});
|
||||
|
||||
it("should prevent values outside of 0-100", function() {
|
||||
stock.changeForecastForecast(101);
|
||||
expect(stock.otlkMagForecast).to.equal(100);
|
||||
stock.changeForecastForecast(-1);
|
||||
expect(stock.otlkMagForecast).to.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#changePrice()", function() {
|
||||
it("should set both the last price and current price properties", function() {
|
||||
const newPrice = 20e3;
|
||||
@ -139,7 +155,28 @@ describe("Stock Market Tests", function() {
|
||||
|
||||
describe("#cycleForecast()", function() {
|
||||
it("should appropriately change the otlkMag by the given amount when b=true", function() {
|
||||
stock.getForecastIncreaseChance = () => { return 1; }
|
||||
stock.cycleForecast(5);
|
||||
expect(stock.otlkMag).to.equal(ctorParams.otlkMag + 5);
|
||||
|
||||
stock.getForecastIncreaseChance = () => { return 0; }
|
||||
stock.cycleForecast(10);
|
||||
expect(stock.otlkMag).to.equal(ctorParams.otlkMag - 5);
|
||||
});
|
||||
|
||||
it("should NOT(!) the stock's 'b' property if it causes 'otlkMag' to go below 0", function() {
|
||||
stock.getForecastIncreaseChance = () => { return 0; }
|
||||
stock.cycleForecast(25);
|
||||
expect(stock.otlkMag).to.equal(5);
|
||||
expect(stock.b).to.equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#cycleForecastForecast()", function() {
|
||||
it("should increase the stock's second-order forecast by a given amount", function() {
|
||||
const expected = [65, 75];
|
||||
stock.cycleForecastForecast(5);
|
||||
expect(stock.otlkMagForecast).to.be.oneOf(expected);
|
||||
});
|
||||
});
|
||||
|
||||
@ -266,6 +303,10 @@ describe("Stock Market Tests", function() {
|
||||
stock.otlkMagForecast = 50;
|
||||
stock.otlkMag = 0;
|
||||
expect(stock.getForecastIncreaseChance()).to.equal(0.5);
|
||||
|
||||
stock.otlkMagForecast = 25;
|
||||
stock.otlkMag = 5; // Asolute forecast of 45
|
||||
expect(stock.getForecastIncreaseChance()).to.equal(0.3);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -343,12 +384,14 @@ describe("Stock Market Tests", function() {
|
||||
const stock = StockMarket[stockName];
|
||||
if (!(stock instanceof Stock)) { continue; }
|
||||
initialValues[stock.symbol] = {
|
||||
price: stock.price,
|
||||
b: stock.b,
|
||||
otlkMag: stock.otlkMag,
|
||||
price: stock.price,
|
||||
}
|
||||
}
|
||||
|
||||
// Don't know or care how many exact cycles are required
|
||||
StockMarket.lastUpdate = new Date().getTime() - 5e3;
|
||||
processStockPrices(1e9);
|
||||
|
||||
// Both price and 'otlkMag' should be different
|
||||
@ -356,7 +399,14 @@ describe("Stock Market Tests", function() {
|
||||
const stock = StockMarket[stockName];
|
||||
if (!(stock instanceof Stock)) { continue; }
|
||||
expect(initialValues[stock.symbol].price).to.not.equal(stock.price);
|
||||
expect(initialValues[stock.symbol].otlkMag).to.not.equal(stock.otlkMag);
|
||||
// expect(initialValues[stock.symbol].otlkMag).to.not.equal(stock.otlkMag);
|
||||
expect(initialValues[stock.symbol]).to.satisfy(function(initValue) {
|
||||
if ((initValue.otlkMag !== stock.otlkMag) || (initValue.b !== stock.b)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -882,6 +932,10 @@ describe("Stock Market Tests", function() {
|
||||
});
|
||||
|
||||
describe("Order Processing", function() {
|
||||
// TODO
|
||||
});
|
||||
|
||||
describe("Player Influencing", function() {
|
||||
// TODO
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user