Merge pull request #4007 from Snarling/stockCtx

NETSCRIPT: Normalized Stock API logging
This commit is contained in:
hydroflame 2022-08-23 12:05:29 -03:00 committed by GitHub
commit 9eb6568519
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 94 additions and 223 deletions

@ -163,7 +163,7 @@ export function NetscriptStockMarket(): InternalAPI<TIX> {
const shares = helpers.number(ctx, "shares", _shares); const shares = helpers.number(ctx, "shares", _shares);
checkTixApiAccess(ctx); checkTixApiAccess(ctx);
const stock = getStockFromSymbol(ctx, symbol); const stock = getStockFromSymbol(ctx, symbol);
const res = buyStock(stock, shares, ctx.workerScript, {}); const res = buyStock(stock, shares, ctx, {});
return res ? stock.getAskPrice() : 0; return res ? stock.getAskPrice() : 0;
}, },
sellStock: sellStock:
@ -173,7 +173,7 @@ export function NetscriptStockMarket(): InternalAPI<TIX> {
const shares = helpers.number(ctx, "shares", _shares); const shares = helpers.number(ctx, "shares", _shares);
checkTixApiAccess(ctx); checkTixApiAccess(ctx);
const stock = getStockFromSymbol(ctx, symbol); const stock = getStockFromSymbol(ctx, symbol);
const res = sellStock(stock, shares, ctx.workerScript, {}); const res = sellStock(stock, shares, ctx, {});
return res ? stock.getBidPrice() : 0; return res ? stock.getBidPrice() : 0;
}, },
@ -192,7 +192,7 @@ export function NetscriptStockMarket(): InternalAPI<TIX> {
} }
} }
const stock = getStockFromSymbol(ctx, symbol); const stock = getStockFromSymbol(ctx, symbol);
const res = shortStock(stock, shares, ctx.workerScript, {}); const res = shortStock(stock, shares, ctx, {});
return res ? stock.getBidPrice() : 0; return res ? stock.getBidPrice() : 0;
}, },
@ -211,7 +211,7 @@ export function NetscriptStockMarket(): InternalAPI<TIX> {
} }
} }
const stock = getStockFromSymbol(ctx, symbol); const stock = getStockFromSymbol(ctx, symbol);
const res = sellShort(stock, shares, ctx.workerScript, {}); const res = sellShort(stock, shares, ctx, {});
return res ? stock.getAskPrice() : 0; return res ? stock.getAskPrice() : 0;
}, },
@ -258,7 +258,7 @@ export function NetscriptStockMarket(): InternalAPI<TIX> {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid position type: ${pos}`); throw helpers.makeRuntimeErrorMsg(ctx, `Invalid position type: ${pos}`);
} }
return placeOrder(stock, shares, price, orderType, orderPos, ctx.workerScript); return placeOrder(stock, shares, price, orderType, orderPos, ctx);
}, },
cancelOrder: cancelOrder:
(ctx: NetscriptContext) => (ctx: NetscriptContext) =>
@ -314,7 +314,7 @@ export function NetscriptStockMarket(): InternalAPI<TIX> {
type: orderType, type: orderType,
pos: orderPos, pos: orderPos,
}; };
return cancelOrder(params, ctx.workerScript); return cancelOrder(params, ctx);
}, },
getOrders: (ctx: NetscriptContext) => (): StockOrder => { getOrders: (ctx: NetscriptContext) => (): StockOrder => {
checkTixApiAccess(ctx); checkTixApiAccess(ctx);

@ -12,7 +12,6 @@ import {
import { PositionTypes } from "./data/PositionTypes"; import { PositionTypes } from "./data/PositionTypes";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
import { WorkerScript } from "../Netscript/WorkerScript";
import { Player } from "../Player"; import { Player } from "../Player";
import { numeralWrapper } from "../ui/numeralFormat"; import { numeralWrapper } from "../ui/numeralFormat";
@ -21,6 +20,8 @@ import { Money } from "../ui/React/Money";
import { dialogBoxCreate } from "../ui/React/DialogBox"; import { dialogBoxCreate } from "../ui/React/DialogBox";
import * as React from "react"; import * as React from "react";
import { NetscriptContext } from "../Netscript/APIWrapper";
import { helpers } from "../Netscript/NetscriptHelpers";
/** /**
* Each function takes an optional config object as its last argument * Each function takes an optional config object as its last argument
@ -34,14 +35,14 @@ interface IOptions {
* Attempt to buy a stock in the long position * Attempt to buy a stock in the long position
* @param {Stock} stock - Stock to buy * @param {Stock} stock - Stock to buy
* @param {number} shares - Number of shares to buy * @param {number} shares - Number of shares to buy
* @param {WorkerScript} workerScript - If this is being called through Netscript * @param {NetscriptContext} ctx - If this is being called through Netscript
* @param opts - Optional configuration for this function's behavior. See top of file * @param opts - Optional configuration for this function's behavior. See top of file
* @returns {boolean} - true if successful, false otherwise * @returns {boolean} - true if successful, false otherwise
*/ */
export function buyStock( export function buyStock(
stock: Stock, stock: Stock,
shares: number, shares: number,
workerScript: WorkerScript | null = null, ctx: NetscriptContext | null = null,
opts: IOptions = {}, opts: IOptions = {},
): boolean { ): boolean {
// Validate arguments // Validate arguments
@ -50,8 +51,8 @@ export function buyStock(
return false; return false;
} }
if (stock == null || isNaN(shares)) { if (stock == null || isNaN(shares)) {
if (workerScript) { if (ctx) {
workerScript.log("stock.buyStock", () => `Invalid arguments: stock='${stock}' shares='${shares}'`); helpers.log(ctx, () => `Invalid arguments: stock='${stock}' shares='${shares}'`);
} else if (opts.suppressDialog !== true) { } else if (opts.suppressDialog !== true) {
dialogBoxCreate("Failed to buy stock. This may be a bug, contact developer"); dialogBoxCreate("Failed to buy stock. This may be a bug, contact developer");
} }
@ -65,9 +66,9 @@ export function buyStock(
return false; return false;
} }
if (Player.money < totalPrice) { if (Player.money < totalPrice) {
if (workerScript) { if (ctx) {
workerScript.log( helpers.log(
"stock.buyStock", ctx,
() => () =>
`You do not have enough money to purchase this position. You need ${numeralWrapper.formatMoney(totalPrice)}.`, `You do not have enough money to purchase this position. You need ${numeralWrapper.formatMoney(totalPrice)}.`,
); );
@ -84,9 +85,9 @@ export function buyStock(
// Would this purchase exceed the maximum number of shares? // Would this purchase exceed the maximum number of shares?
if (shares + stock.playerShares + stock.playerShortShares > stock.maxShares) { if (shares + stock.playerShares + stock.playerShortShares > stock.maxShares) {
if (workerScript) { if (ctx) {
workerScript.log( helpers.log(
"stock.buyStock", ctx,
() => () =>
`Purchasing '${shares + stock.playerShares + stock.playerShortShares}' shares would exceed ${ `Purchasing '${shares + stock.playerShares + stock.playerShortShares}' shares would exceed ${
stock.symbol stock.symbol
@ -113,13 +114,13 @@ export function buyStock(
opts.rerenderFn(); opts.rerenderFn();
} }
if (workerScript) { if (ctx) {
const resultTxt = `Bought ${numeralWrapper.formatShares(shares)} shares of ${ const resultTxt = `Bought ${numeralWrapper.formatShares(shares)} shares of ${
stock.symbol stock.symbol
} for ${numeralWrapper.formatMoney(totalPrice)}. Paid ${numeralWrapper.formatMoney( } for ${numeralWrapper.formatMoney(totalPrice)}. Paid ${numeralWrapper.formatMoney(
CONSTANTS.StockMarketCommission, CONSTANTS.StockMarketCommission,
)} in commission fees.`; )} in commission fees.`;
workerScript.log("stock.buyStock", () => resultTxt); helpers.log(ctx, () => resultTxt);
} else if (opts.suppressDialog !== true) { } else if (opts.suppressDialog !== true) {
dialogBoxCreate( dialogBoxCreate(
<> <>
@ -136,20 +137,20 @@ export function buyStock(
* Attempt to sell a stock in the long position * Attempt to sell a stock in the long position
* @param {Stock} stock - Stock to sell * @param {Stock} stock - Stock to sell
* @param {number} shares - Number of shares to sell * @param {number} shares - Number of shares to sell
* @param {WorkerScript} workerScript - If this is being called through Netscript * @param {NetscriptContext} ctx - If this is being called through Netscript
* @param opts - Optional configuration for this function's behavior. See top of file * @param opts - Optional configuration for this function's behavior. See top of file
* returns {boolean} - true if successfully sells given number of shares OR MAX owned, false otherwise * returns {boolean} - true if successfully sells given number of shares OR MAX owned, false otherwise
*/ */
export function sellStock( export function sellStock(
stock: Stock, stock: Stock,
shares: number, shares: number,
workerScript: WorkerScript | null = null, ctx: NetscriptContext | null = null,
opts: IOptions = {}, opts: IOptions = {},
): boolean { ): boolean {
// Sanitize/Validate arguments // Sanitize/Validate arguments
if (stock == null || shares < 0 || isNaN(shares)) { if (stock == null || shares < 0 || isNaN(shares)) {
if (workerScript) { if (ctx) {
workerScript.log("stock.sellStock", () => `Invalid arguments: stock='${stock}' shares='${shares}'`); helpers.log(ctx, () => `Invalid arguments: stock='${stock}' shares='${shares}'`);
} else if (opts.suppressDialog !== true) { } else if (opts.suppressDialog !== true) {
dialogBoxCreate( dialogBoxCreate(
"Failed to sell stock. This is probably due to an invalid quantity. Otherwise, this may be a bug, contact developer", "Failed to sell stock. This is probably due to an invalid quantity. Otherwise, this may be a bug, contact developer",
@ -175,8 +176,8 @@ export function sellStock(
netProfit = 0; netProfit = 0;
} }
Player.gainMoney(gains, "stock"); Player.gainMoney(gains, "stock");
if (workerScript) { if (ctx) {
workerScript.scriptRef.onlineMoneyMade += netProfit; ctx.workerScript.scriptRef.onlineMoneyMade += netProfit;
Player.scriptProdSinceLastAug += netProfit; Player.scriptProdSinceLastAug += netProfit;
} }
@ -191,11 +192,11 @@ export function sellStock(
opts.rerenderFn(); opts.rerenderFn();
} }
if (workerScript) { if (ctx) {
const resultTxt = const resultTxt =
`Sold ${numeralWrapper.formatShares(shares)} shares of ${stock.symbol}. ` + `Sold ${numeralWrapper.formatShares(shares)} shares of ${stock.symbol}. ` +
`After commissions, you gained a total of ${numeralWrapper.formatMoney(gains)}.`; `After commissions, you gained a total of ${numeralWrapper.formatMoney(gains)}.`;
workerScript.log("stock.sellStock", () => resultTxt); helpers.log(ctx, () => resultTxt);
} else if (opts.suppressDialog !== true) { } else if (opts.suppressDialog !== true) {
dialogBoxCreate( dialogBoxCreate(
<> <>
@ -212,14 +213,14 @@ export function sellStock(
* Attempt to buy a stock in the short position * Attempt to buy a stock in the short position
* @param {Stock} stock - Stock to sell * @param {Stock} stock - Stock to sell
* @param {number} shares - Number of shares to short * @param {number} shares - Number of shares to short
* @param {WorkerScript} workerScript - If this is being called through Netscript * @param {NetscriptContext} ctx - If this is being called through Netscript
* @param opts - Optional configuration for this function's behavior. See top of file * @param opts - Optional configuration for this function's behavior. See top of file
* @returns {boolean} - true if successful, false otherwise * @returns {boolean} - true if successful, false otherwise
*/ */
export function shortStock( export function shortStock(
stock: Stock, stock: Stock,
shares: number, shares: number,
workerScript: WorkerScript | null = null, ctx: NetscriptContext | null = null,
opts: IOptions = {}, opts: IOptions = {},
): boolean { ): boolean {
// Validate arguments // Validate arguments
@ -228,8 +229,8 @@ export function shortStock(
return false; return false;
} }
if (stock == null || isNaN(shares)) { if (stock == null || isNaN(shares)) {
if (workerScript) { if (ctx) {
workerScript.log("stock.short", () => `Invalid arguments: stock='${stock}' shares='${shares}'`); helpers.log(ctx, () => `Invalid arguments: stock='${stock}' shares='${shares}'`);
} else if (opts.suppressDialog !== true) { } else if (opts.suppressDialog !== true) {
dialogBoxCreate( dialogBoxCreate(
"Failed to initiate a short position in a stock. This is probably " + "Failed to initiate a short position in a stock. This is probably " +
@ -245,9 +246,9 @@ export function shortStock(
return false; return false;
} }
if (Player.money < totalPrice) { if (Player.money < totalPrice) {
if (workerScript) { if (ctx) {
workerScript.log( helpers.log(
"stock.short", ctx,
() => () =>
"You do not have enough " + "You do not have enough " +
"money to purchase this short position. You need " + "money to purchase this short position. You need " +
@ -266,9 +267,9 @@ export function shortStock(
// Would this purchase exceed the maximum number of shares? // Would this purchase exceed the maximum number of shares?
if (shares + stock.playerShares + stock.playerShortShares > stock.maxShares) { if (shares + stock.playerShares + stock.playerShortShares > stock.maxShares) {
if (workerScript) { if (ctx) {
workerScript.log( helpers.log(
"stock.short", ctx,
() => () =>
`This '${shares + stock.playerShares + stock.playerShortShares}' short shares would exceed ${ `This '${shares + stock.playerShares + stock.playerShortShares}' short shares would exceed ${
stock.symbol stock.symbol
@ -294,14 +295,14 @@ export function shortStock(
opts.rerenderFn(); opts.rerenderFn();
} }
if (workerScript) { if (ctx) {
const resultTxt = const resultTxt =
`Bought a short position of ${numeralWrapper.formatShares(shares)} shares of ${stock.symbol} ` + `Bought a short position of ${numeralWrapper.formatShares(shares)} shares of ${stock.symbol} ` +
`for ${numeralWrapper.formatMoney(totalPrice)}. Paid ${numeralWrapper.formatMoney( `for ${numeralWrapper.formatMoney(totalPrice)}. Paid ${numeralWrapper.formatMoney(
CONSTANTS.StockMarketCommission, CONSTANTS.StockMarketCommission,
)} ` + )} ` +
`in commission fees.`; `in commission fees.`;
workerScript.log("stock.short", () => resultTxt); helpers.log(ctx, () => resultTxt);
} else if (!opts.suppressDialog) { } else if (!opts.suppressDialog) {
dialogBoxCreate( dialogBoxCreate(
<> <>
@ -318,19 +319,19 @@ export function shortStock(
* Attempt to sell a stock in the short position * Attempt to sell a stock in the short position
* @param {Stock} stock - Stock to sell * @param {Stock} stock - Stock to sell
* @param {number} shares - Number of shares to sell * @param {number} shares - Number of shares to sell
* @param {WorkerScript} workerScript - If this is being called through Netscript * @param {NetscriptContext} ctx - If this is being called through Netscript
* @param opts - Optional configuration for this function's behavior. See top of file * @param opts - Optional configuration for this function's behavior. See top of file
* @returns {boolean} true if successfully sells given amount OR max owned, false otherwise * @returns {boolean} true if successfully sells given amount OR max owned, false otherwise
*/ */
export function sellShort( export function sellShort(
stock: Stock, stock: Stock,
shares: number, shares: number,
workerScript: WorkerScript | null = null, ctx: NetscriptContext | null = null,
opts: IOptions = {}, opts: IOptions = {},
): boolean { ): boolean {
if (stock == null || isNaN(shares) || shares < 0) { if (stock == null || isNaN(shares) || shares < 0) {
if (workerScript) { if (ctx) {
workerScript.log("stock.sellShort", () => `Invalid arguments: stock='${stock}' shares='${shares}'`); helpers.log(ctx, () => `Invalid arguments: stock='${stock}' shares='${shares}'`);
} else if (!opts.suppressDialog) { } else if (!opts.suppressDialog) {
dialogBoxCreate( dialogBoxCreate(
"Failed to sell a short position in a stock. This is probably " + "Failed to sell a short position in a stock. This is probably " +
@ -351,9 +352,9 @@ export function sellShort(
const origCost = shares * stock.playerAvgShortPx; const origCost = shares * stock.playerAvgShortPx;
const totalGain = getSellTransactionGain(stock, shares, PositionTypes.Short); const totalGain = getSellTransactionGain(stock, shares, PositionTypes.Short);
if (totalGain == null || isNaN(totalGain) || origCost == null) { if (totalGain == null || isNaN(totalGain) || origCost == null) {
if (workerScript) { if (ctx) {
workerScript.log( helpers.log(
"stock.sellShort", ctx,
() => `Failed to sell short position in a stock. This is probably either due to invalid arguments, or a bug`, () => `Failed to sell short position in a stock. This is probably either due to invalid arguments, or a bug`,
); );
} else if (!opts.suppressDialog) { } else if (!opts.suppressDialog) {
@ -369,8 +370,8 @@ export function sellShort(
profit = 0; profit = 0;
} }
Player.gainMoney(totalGain, "stock"); Player.gainMoney(totalGain, "stock");
if (workerScript) { if (ctx) {
workerScript.scriptRef.onlineMoneyMade += profit; ctx.workerScript.scriptRef.onlineMoneyMade += profit;
Player.scriptProdSinceLastAug += profit; Player.scriptProdSinceLastAug += profit;
} }
@ -384,11 +385,11 @@ export function sellShort(
opts.rerenderFn(); opts.rerenderFn();
} }
if (workerScript) { if (ctx) {
const resultTxt = const resultTxt =
`Sold your short position of ${numeralWrapper.formatShares(shares)} shares of ${stock.symbol}. ` + `Sold your short position of ${numeralWrapper.formatShares(shares)} shares of ${stock.symbol}. ` +
`After commissions, you gained a total of ${numeralWrapper.formatMoney(totalGain)}`; `After commissions, you gained a total of ${numeralWrapper.formatMoney(totalGain)}`;
workerScript.log("stock.sellShort", () => resultTxt); helpers.log(ctx, () => resultTxt);
} else if (!opts.suppressDialog) { } else if (!opts.suppressDialog) {
dialogBoxCreate( dialogBoxCreate(
<> <>

@ -10,14 +10,14 @@ import { PositionTypes } from "./data/PositionTypes";
import { StockSymbols } from "./data/StockSymbols"; import { StockSymbols } from "./data/StockSymbols";
import { CONSTANTS } from "../Constants"; import { CONSTANTS } from "../Constants";
import { WorkerScript } from "../Netscript/WorkerScript";
import { IMap } from "../types"; import { IMap } from "../types";
import { EventEmitter } from "../utils/EventEmitter";
import { numeralWrapper } from "../ui/numeralFormat"; import { numeralWrapper } from "../ui/numeralFormat";
import { dialogBoxCreate } from "../ui/React/DialogBox"; import { dialogBoxCreate } from "../ui/React/DialogBox";
import { Reviver } from "../utils/JSONReviver"; import { Reviver } from "../utils/JSONReviver";
import { NetscriptContext } from "../Netscript/APIWrapper";
import { helpers } from "../Netscript/NetscriptHelpers";
export let StockMarket: IStockMarket = { export let StockMarket: IStockMarket = {
lastUpdate: 0, lastUpdate: 0,
@ -33,19 +33,19 @@ export function placeOrder(
price: number, price: number,
type: OrderTypes, type: OrderTypes,
position: PositionTypes, position: PositionTypes,
workerScript: WorkerScript | null = null, ctx?: NetscriptContext,
): boolean { ): boolean {
if (!(stock instanceof Stock)) { if (!(stock instanceof Stock)) {
if (workerScript) { if (ctx) {
workerScript.log("stock.placeOrder", () => `Invalid stock: '${stock}'`); helpers.log(ctx, () => `Invalid stock: '${stock}'`);
} else { } else {
dialogBoxCreate(`ERROR: Invalid stock passed to placeOrder() function`); dialogBoxCreate(`ERROR: Invalid stock passed to placeOrder() function`);
} }
return false; return false;
} }
if (typeof shares !== "number" || typeof price !== "number") { if (typeof shares !== "number" || typeof price !== "number") {
if (workerScript) { if (ctx) {
workerScript.log("stock.placeOrder", () => `Invalid arguments: shares='${shares}' price='${price}'`); helpers.log(ctx, () => `Invalid arguments: shares='${shares}' price='${price}'`);
} else { } else {
dialogBoxCreate("ERROR: Invalid numeric value provided for either 'shares' or 'price' argument"); dialogBoxCreate("ERROR: Invalid numeric value provided for either 'shares' or 'price' argument");
} }
@ -85,10 +85,8 @@ export interface ICancelOrderParams {
stock?: Stock; stock?: Stock;
type?: OrderTypes; type?: OrderTypes;
} }
export function cancelOrder(params: ICancelOrderParams, workerScript: WorkerScript | null = null): boolean { export function cancelOrder(params: ICancelOrderParams, ctx?: NetscriptContext): boolean {
if (StockMarket["Orders"] == null) { if (StockMarket["Orders"] == null) return false;
return false;
}
if (params.order && params.order instanceof Order) { if (params.order && params.order instanceof Order) {
const order = params.order; const order = params.order;
// An 'Order' object is passed in // An 'Order' object is passed in
@ -120,15 +118,11 @@ export function cancelOrder(params: ICancelOrderParams, workerScript: WorkerScri
params.pos === order.pos params.pos === order.pos
) { ) {
stockOrders.splice(i, 1); stockOrders.splice(i, 1);
if (workerScript) { if (ctx) helpers.log(ctx, () => "Successfully cancelled order: " + orderTxt);
workerScript.scriptRef.log("Successfully cancelled order: " + orderTxt);
}
return true; return true;
} }
} }
if (workerScript) { if (ctx) helpers.log(ctx, () => "Failed to cancel order: " + orderTxt);
workerScript.scriptRef.log("Failed to cancel order: " + orderTxt);
}
return false; return false;
} }
return false; return false;
@ -142,9 +136,7 @@ export function loadStockMarket(saveString: string): void {
storedCycles: 0, storedCycles: 0,
ticksUntilCycle: 0, ticksUntilCycle: 0,
} as IStockMarket; } as IStockMarket;
} else { } else StockMarket = JSON.parse(saveString, Reviver);
StockMarket = JSON.parse(saveString, Reviver);
}
} }
export function deleteStockMarket(): void { export function deleteStockMarket(): void {
@ -158,9 +150,7 @@ export function deleteStockMarket(): void {
export function initStockMarket(): void { export function initStockMarket(): void {
for (const stk of Object.keys(StockMarket)) { for (const stk of Object.keys(StockMarket)) {
if (StockMarket.hasOwnProperty(stk)) { if (StockMarket.hasOwnProperty(stk)) delete StockMarket[stk];
delete StockMarket[stk];
}
} }
for (const metadata of InitStockMetadata) { for (const metadata of InitStockMetadata) {
@ -171,9 +161,7 @@ export function initStockMarket(): void {
const orders: IOrderBook = {}; const orders: IOrderBook = {};
for (const name of Object.keys(StockMarket)) { for (const name of Object.keys(StockMarket)) {
const stock = StockMarket[name]; const stock = StockMarket[name];
if (!(stock instanceof Stock)) { if (!(stock instanceof Stock)) continue;
continue;
}
orders[stock.symbol] = []; orders[stock.symbol] = [];
} }
StockMarket["Orders"] = orders; StockMarket["Orders"] = orders;
@ -200,9 +188,7 @@ export function initSymbolToStockMap(): void {
function stockMarketCycle(): void { function stockMarketCycle(): void {
for (const name of Object.keys(StockMarket)) { for (const name of Object.keys(StockMarket)) {
const stock = StockMarket[name]; const stock = StockMarket[name];
if (!(stock instanceof Stock)) { if (!(stock instanceof Stock)) continue;
continue;
}
const roll = Math.random(); const roll = Math.random();
if (roll < 0.45) { if (roll < 0.45) {
@ -230,9 +216,7 @@ export function processStockPrices(numCycles = 1): void {
// We can process the update every 4 seconds as long as there are enough // We can process the update every 4 seconds as long as there are enough
// stored cycles. This lets us account for offline time // stored cycles. This lets us account for offline time
const timeNow = new Date().getTime(); const timeNow = new Date().getTime();
if (timeNow - StockMarket.lastUpdate < 4e3) { if (timeNow - StockMarket.lastUpdate < 4e3) return;
return;
}
StockMarket.lastUpdate = timeNow; StockMarket.lastUpdate = timeNow;
StockMarket.storedCycles -= cyclesPerStockUpdate; StockMarket.storedCycles -= cyclesPerStockUpdate;
@ -242,16 +226,12 @@ export function processStockPrices(numCycles = 1): void {
StockMarket.ticksUntilCycle = TicksPerCycle; StockMarket.ticksUntilCycle = TicksPerCycle;
} }
--StockMarket.ticksUntilCycle; --StockMarket.ticksUntilCycle;
if (StockMarket.ticksUntilCycle <= 0) { if (StockMarket.ticksUntilCycle <= 0) stockMarketCycle();
stockMarketCycle();
}
const v = Math.random(); const v = Math.random();
for (const name of Object.keys(StockMarket)) { for (const name of Object.keys(StockMarket)) {
const stock = StockMarket[name]; const stock = StockMarket[name];
if (!(stock instanceof Stock)) { if (!(stock instanceof Stock)) continue;
continue;
}
let av = (v * stock.mv) / 100; let av = (v * stock.mv) / 100;
if (isNaN(av)) { if (isNaN(av)) {
av = 0.02; av = 0.02;
@ -310,5 +290,3 @@ export function initStockMarketFn(): void {
initStockMarket(); initStockMarket();
initSymbolToStockMap(); initSymbolToStockMap();
} }
export const eventEmitterForUiReset = new EventEmitter<[]>();

@ -10,6 +10,7 @@ import { getStockMarket4SDataCost, getStockMarket4STixApiCost } from "../StockMa
import { CONSTANTS } from "../../Constants"; import { CONSTANTS } from "../../Constants";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { Money } from "../../ui/React/Money"; import { Money } from "../../ui/React/Money";
import { initStockMarket } from "../StockMarket";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Link from "@mui/material/Link"; import Link from "@mui/material/Link";
@ -22,7 +23,6 @@ import { StaticModal } from "../../ui/React/StaticModal";
import { FactionNames } from "../../Faction/data/FactionNames"; import { FactionNames } from "../../Faction/data/FactionNames";
type IProps = { type IProps = {
initStockMarket: () => void;
p: IPlayer; p: IPlayer;
rerender: () => void; rerender: () => void;
}; };
@ -88,7 +88,7 @@ function PurchaseWseAccountButton(props: IProps): React.ReactElement {
return; return;
} }
props.p.hasWseAccount = true; props.p.hasWseAccount = true;
props.initStockMarket(); initStockMarket();
props.p.loseMoney(CONSTANTS.WSEAccountCost, "stock"); props.p.loseMoney(CONSTANTS.WSEAccountCost, "stock");
props.rerender(); props.rerender();
} }

@ -7,34 +7,11 @@ import { InfoAndPurchases } from "./InfoAndPurchases";
import { StockTickers } from "./StockTickers"; import { StockTickers } from "./StockTickers";
import { IStockMarket } from "../IStockMarket"; import { IStockMarket } from "../IStockMarket";
import { Stock } from "../Stock";
import { OrderTypes } from "../data/OrderTypes";
import { PositionTypes } from "../data/PositionTypes";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { EventEmitter } from "../../utils/EventEmitter";
import { WorkerScript } from "../../Netscript/WorkerScript";
import { ICancelOrderParams } from "../StockMarket";
type txFn = (stock: Stock, shares: number) => boolean;
type placeOrderFn = (
stock: Stock,
shares: number,
price: number,
ordType: OrderTypes,
posType: PositionTypes,
) => boolean;
type IProps = { type IProps = {
buyStockLong: txFn;
buyStockShort: txFn;
cancelOrder: (params: ICancelOrderParams, workerScript?: WorkerScript) => void;
eventEmitterForReset?: EventEmitter<[]>;
initStockMarket: () => void;
p: IPlayer; p: IPlayer;
placeOrder: placeOrderFn;
sellStockLong: txFn;
sellStockShort: txFn;
stockMarket: IStockMarket; stockMarket: IStockMarket;
}; };
@ -50,20 +27,8 @@ export function StockMarketRoot(props: IProps): React.ReactElement {
}, []); }, []);
return ( return (
<> <>
<InfoAndPurchases initStockMarket={props.initStockMarket} p={props.p} rerender={rerender} /> <InfoAndPurchases p={props.p} rerender={rerender} />
{props.p.hasWseAccount && ( {props.p.hasWseAccount && <StockTickers p={props.p} stockMarket={props.stockMarket} />}
<StockTickers
buyStockLong={props.buyStockLong}
buyStockShort={props.buyStockShort}
cancelOrder={props.cancelOrder}
eventEmitterForReset={props.eventEmitterForReset}
p={props.p}
placeOrder={props.placeOrder}
sellStockLong={props.sellStockLong}
sellStockShort={props.sellStockShort}
stockMarket={props.stockMarket}
/>
)}
</> </>
); );
} }

@ -14,6 +14,8 @@ import { Stock } from "../Stock";
import { getBuyTransactionCost, getSellTransactionGain, calculateBuyMaxAmount } from "../StockMarketHelpers"; import { getBuyTransactionCost, getSellTransactionGain, calculateBuyMaxAmount } from "../StockMarketHelpers";
import { OrderTypes } from "../data/OrderTypes"; import { OrderTypes } from "../data/OrderTypes";
import { PositionTypes } from "../data/PositionTypes"; import { PositionTypes } from "../data/PositionTypes";
import { placeOrder } from "../StockMarket";
import { buyStock, shortStock, sellStock, sellShort } from "../BuyingAndSelling";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { numeralWrapper } from "../../ui/numeralFormat"; import { numeralWrapper } from "../../ui/numeralFormat";
@ -31,8 +33,6 @@ import Paper from "@mui/material/Paper";
import Collapse from "@mui/material/Collapse"; import Collapse from "@mui/material/Collapse";
import ExpandMore from "@mui/icons-material/ExpandMore"; import ExpandMore from "@mui/icons-material/ExpandMore";
import ExpandLess from "@mui/icons-material/ExpandLess"; import ExpandLess from "@mui/icons-material/ExpandLess";
import { ICancelOrderParams } from "../StockMarket";
import { WorkerScript } from "../../Netscript/WorkerScript";
enum SelectorOrderType { enum SelectorOrderType {
Market = "Market Order", Market = "Market Order",
@ -40,25 +40,10 @@ enum SelectorOrderType {
Stop = "Stop Order", Stop = "Stop Order",
} }
type txFn = (stock: Stock, shares: number) => boolean;
type placeOrderFn = (
stock: Stock,
shares: number,
price: number,
ordType: OrderTypes,
posType: PositionTypes,
) => boolean;
type IProps = { type IProps = {
buyStockLong: txFn;
buyStockShort: txFn;
cancelOrder: (params: ICancelOrderParams, workerScript?: WorkerScript) => void;
orders: Order[]; orders: Order[];
p: IPlayer; p: IPlayer;
placeOrder: placeOrderFn;
rerenderAllTickers: () => void; rerenderAllTickers: () => void;
sellStockLong: txFn;
sellStockShort: txFn;
stock: Stock; stock: Stock;
}; };
@ -141,9 +126,9 @@ export function StockTicker(props: IProps): React.ReactElement {
switch (orderType) { switch (orderType) {
case SelectorOrderType.Market: { case SelectorOrderType.Market: {
if (position === PositionTypes.Short) { if (position === PositionTypes.Short) {
props.buyStockShort(props.stock, shares); shortStock(props.stock, shares);
} else { } else {
props.buyStockLong(props.stock, shares); buyStock(props.stock, shares);
} }
props.rerenderAllTickers(); props.rerenderAllTickers();
break; break;
@ -153,7 +138,7 @@ export function StockTicker(props: IProps): React.ReactElement {
setModalProps({ setModalProps({
text: "Enter the price for your Limit Order", text: "Enter the price for your Limit Order",
placeText: "Place Buy Limit Order", placeText: "Place Buy Limit Order",
place: (price: number) => props.placeOrder(props.stock, shares, price, OrderTypes.LimitBuy, position), place: (price: number) => placeOrder(props.stock, shares, price, OrderTypes.LimitBuy, position),
}); });
break; break;
} }
@ -162,7 +147,7 @@ export function StockTicker(props: IProps): React.ReactElement {
setModalProps({ setModalProps({
text: "Enter the price for your Stop Order", text: "Enter the price for your Stop Order",
placeText: "Place Buy Stop Order", placeText: "Place Buy Stop Order",
place: (price: number) => props.placeOrder(props.stock, shares, price, OrderTypes.StopBuy, position), place: (price: number) => placeOrder(props.stock, shares, price, OrderTypes.StopBuy, position),
}); });
break; break;
} }
@ -181,9 +166,9 @@ export function StockTicker(props: IProps): React.ReactElement {
switch (orderType) { switch (orderType) {
case SelectorOrderType.Market: { case SelectorOrderType.Market: {
if (position === PositionTypes.Short) { if (position === PositionTypes.Short) {
props.buyStockShort(stock, maxShares); shortStock(stock, maxShares);
} else { } else {
props.buyStockLong(stock, maxShares); buyStock(stock, maxShares);
} }
props.rerenderAllTickers(); props.rerenderAllTickers();
break; break;
@ -237,9 +222,9 @@ export function StockTicker(props: IProps): React.ReactElement {
switch (orderType) { switch (orderType) {
case SelectorOrderType.Market: { case SelectorOrderType.Market: {
if (position === PositionTypes.Short) { if (position === PositionTypes.Short) {
props.sellStockShort(props.stock, shares); sellShort(props.stock, shares);
} else { } else {
props.sellStockLong(props.stock, shares); sellStock(props.stock, shares);
} }
props.rerenderAllTickers(); props.rerenderAllTickers();
break; break;
@ -249,7 +234,7 @@ export function StockTicker(props: IProps): React.ReactElement {
setModalProps({ setModalProps({
text: "Enter the price for your Limit Order", text: "Enter the price for your Limit Order",
placeText: "Place Sell Limit Order", placeText: "Place Sell Limit Order",
place: (price: number) => props.placeOrder(props.stock, shares, price, OrderTypes.LimitSell, position), place: (price: number) => placeOrder(props.stock, shares, price, OrderTypes.LimitSell, position),
}); });
break; break;
} }
@ -258,7 +243,7 @@ export function StockTicker(props: IProps): React.ReactElement {
setModalProps({ setModalProps({
text: "Enter the price for your Stop Order", text: "Enter the price for your Stop Order",
placeText: "Place Sell Stop Order", placeText: "Place Sell Stop Order",
place: (price: number) => props.placeOrder(props.stock, shares, price, OrderTypes.StopSell, position), place: (price: number) => placeOrder(props.stock, shares, price, OrderTypes.StopSell, position),
}); });
break; break;
} }
@ -273,9 +258,9 @@ export function StockTicker(props: IProps): React.ReactElement {
switch (orderType) { switch (orderType) {
case SelectorOrderType.Market: { case SelectorOrderType.Market: {
if (position === PositionTypes.Short) { if (position === PositionTypes.Short) {
props.sellStockShort(stock, stock.playerShortShares); sellShort(stock, stock.playerShortShares);
} else { } else {
props.sellStockLong(stock, stock.playerShares); sellStock(stock, stock.playerShares);
} }
props.rerenderAllTickers(); props.rerenderAllTickers();
break; break;
@ -327,7 +312,7 @@ export function StockTicker(props: IProps): React.ReactElement {
<StockTickerTxButton onClick={handleSellAllButtonClick} text={"Sell ALL"} /> <StockTickerTxButton onClick={handleSellAllButtonClick} text={"Sell ALL"} />
</Box> </Box>
<StockTickerPositionText p={props.p} stock={props.stock} /> <StockTickerPositionText p={props.p} stock={props.stock} />
<StockTickerOrderList cancelOrder={props.cancelOrder} orders={props.orders} p={props.p} stock={props.stock} /> <StockTickerOrderList orders={props.orders} p={props.p} stock={props.stock} />
<PlaceOrderModal <PlaceOrderModal
text={modalProps.text} text={modalProps.text}

@ -11,17 +11,15 @@ import { Money } from "../../ui/React/Money";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import { ICancelOrderParams } from "../StockMarket"; import { cancelOrder } from "../StockMarket";
import { WorkerScript } from "../../Netscript/WorkerScript";
type IProps = { type IProps = {
cancelOrder: (params: ICancelOrderParams, workerScript?: WorkerScript) => void;
order: Order; order: Order;
}; };
export function StockTickerOrder(props: IProps): React.ReactElement { export function StockTickerOrder(props: IProps): React.ReactElement {
function handleCancelOrderClick(): void { function handleCancelOrderClick(): void {
props.cancelOrder({ order: props.order }); cancelOrder({ order: props.order });
} }
const order = props.order; const order = props.order;

@ -10,11 +10,8 @@ import { Order } from "../Order";
import { Stock } from "../Stock"; import { Stock } from "../Stock";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { ICancelOrderParams } from "../StockMarket";
import { WorkerScript } from "../../Netscript/WorkerScript";
type IProps = { type IProps = {
cancelOrder: (params: ICancelOrderParams, workerScript?: WorkerScript) => void;
orders: Order[]; orders: Order[];
p: IPlayer; p: IPlayer;
stock: Stock; stock: Stock;
@ -24,7 +21,7 @@ export function StockTickerOrderList(props: IProps): React.ReactElement {
const orders: React.ReactElement[] = []; const orders: React.ReactElement[] = [];
for (let i = 0; i < props.orders.length; ++i) { for (let i = 0; i < props.orders.length; ++i) {
const o = props.orders[i]; const o = props.orders[i];
orders.push(<StockTickerOrder cancelOrder={props.cancelOrder} order={o} key={i} />); orders.push(<StockTickerOrder order={o} key={i} />);
} }
return <>{orders}</>; return <>{orders}</>;

@ -10,32 +10,11 @@ import { StockTickersConfig, TickerDisplayMode } from "./StockTickersConfig";
import { IStockMarket } from "../IStockMarket"; import { IStockMarket } from "../IStockMarket";
import { Stock } from "../Stock"; import { Stock } from "../Stock";
import { OrderTypes } from "../data/OrderTypes";
import { PositionTypes } from "../data/PositionTypes";
import { IPlayer } from "../../PersonObjects/IPlayer"; import { IPlayer } from "../../PersonObjects/IPlayer";
import { EventEmitter } from "../../utils/EventEmitter";
import { WorkerScript } from "../../Netscript/WorkerScript";
import { ICancelOrderParams } from "../StockMarket";
type txFn = (stock: Stock, shares: number) => boolean;
type placeOrderFn = (
stock: Stock,
shares: number,
price: number,
ordType: OrderTypes,
posType: PositionTypes,
) => boolean;
type IProps = { type IProps = {
buyStockLong: txFn;
buyStockShort: txFn;
cancelOrder: (params: ICancelOrderParams, workerScript?: WorkerScript) => void;
eventEmitterForReset?: EventEmitter<[]>;
p: IPlayer; p: IPlayer;
placeOrder: placeOrderFn;
sellStockLong: txFn;
sellStockShort: txFn;
stockMarket: IStockMarket; stockMarket: IStockMarket;
}; };
@ -89,19 +68,7 @@ export function StockTickers(props: IProps): React.ReactElement {
} }
tickers.push( tickers.push(
<StockTicker <StockTicker key={val.symbol} orders={orders} p={props.p} rerenderAllTickers={rerender} stock={val} />,
buyStockLong={props.buyStockLong}
buyStockShort={props.buyStockShort}
cancelOrder={props.cancelOrder}
key={val.symbol}
orders={orders}
p={props.p}
placeOrder={props.placeOrder}
rerenderAllTickers={rerender}
sellStockLong={props.sellStockLong}
sellStockShort={props.sellStockShort}
stock={val}
/>,
); );
} }
} }

@ -17,14 +17,7 @@ import { prestigeAugmentation } from "../Prestige";
import { dialogBoxCreate } from "./React/DialogBox"; import { dialogBoxCreate } from "./React/DialogBox";
import { GetAllServers } from "../Server/AllServers"; import { GetAllServers } from "../Server/AllServers";
import { Factions } from "../Faction/Factions"; import { Factions } from "../Faction/Factions";
import { buyStock, sellStock, shortStock, sellShort } from "../StockMarket/BuyingAndSelling"; import { StockMarket } from "../StockMarket/StockMarket";
import {
cancelOrder,
eventEmitterForUiReset,
initStockMarketFn,
placeOrder,
StockMarket,
} from "../StockMarket/StockMarket";
import { Theme } from "@mui/material/styles"; import { Theme } from "@mui/material/styles";
import makeStyles from "@mui/styles/makeStyles"; import makeStyles from "@mui/styles/makeStyles";
@ -442,20 +435,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
break; break;
} }
case Page.StockMarket: { case Page.StockMarket: {
mainPage = ( mainPage = <StockMarketRoot p={player} stockMarket={StockMarket} />;
<StockMarketRoot
buyStockLong={buyStock}
buyStockShort={shortStock}
cancelOrder={cancelOrder}
eventEmitterForReset={eventEmitterForUiReset}
initStockMarket={initStockMarketFn}
p={player}
placeOrder={placeOrder}
sellStockLong={sellStock}
sellStockShort={sellShort}
stockMarket={StockMarket}
/>
);
break; break;
} }
case Page.City: { case Page.City: {