mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-18 12:15:44 +01:00
Initial implementation of BitNode-8: Ghost of Wall Street. Added TextFile.js to git. Added design for company management
This commit is contained in:
parent
153831afe9
commit
4ccad83e5e
@ -596,21 +596,25 @@ div.faction-clear {
|
|||||||
margin: 10px;
|
margin: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stock-market-qty-input {
|
.stock-market-input {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 4px;
|
||||||
|
margin: 2px;
|
||||||
|
background-color: black;
|
||||||
border: 1px solid white;
|
border: 1px solid white;
|
||||||
color: var(--my-font-color);
|
color: var(--my-font-color);
|
||||||
padding: 4px;
|
}
|
||||||
margin: 4px;
|
|
||||||
background-color:black;
|
.stock-market-position-text {
|
||||||
|
color:white;
|
||||||
|
white-space: pre;
|
||||||
|
display:block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stock-market-buy-sell-button {
|
.stock-market-buy-sell-button {
|
||||||
color: #aaa;
|
color: #aaa;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
padding: 2px;
|
|
||||||
margin: 6px;
|
|
||||||
border: 1px solid white;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.stock-market-buy-sell-button:hover,
|
.stock-market-buy-sell-button:hover,
|
||||||
@ -620,6 +624,11 @@ div.faction-clear {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stock-market-order-list {
|
||||||
|
overflow-y:auto;
|
||||||
|
max-height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Gang */
|
/* Gang */
|
||||||
#gang-container {
|
#gang-container {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
5498
dist/bundle.js
vendored
5498
dist/bundle.js
vendored
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,156 @@
|
|||||||
/* CompanyManagement.js */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Company made up of
|
Products
|
||||||
|
For certain industries, players can created their own custom products
|
||||||
|
Essentially, these are just things you give a certain name to.
|
||||||
|
|
||||||
|
Products have certain properties that affect how well they sell. These properties
|
||||||
|
are just numbers. For each Industry, only some of these properties are applicable
|
||||||
|
(e.g. Performance isnt applicable for food industry)
|
||||||
|
Demand: Determined by industry. For most industries this will slowly decrease over time, meaning
|
||||||
|
that you must create new and better products to remain successful. The speed at which this
|
||||||
|
decreases over time is dependent on industry
|
||||||
|
Competition: Determined by industry
|
||||||
|
Markup : Determined by Industry
|
||||||
|
Quality:
|
||||||
|
Performance:
|
||||||
|
Durability:
|
||||||
|
Reliability:
|
||||||
|
Aesthetics:
|
||||||
|
Features:
|
||||||
|
Location: Only valid for 'building' products like Restaurants, Hospitals, etc.
|
||||||
|
Scientific Research affects the properties of products
|
||||||
|
Materials:
|
||||||
|
To create Products, you need materials. There are different tiers of Materials
|
||||||
|
Materials have several properties that determine how profitable they can be:
|
||||||
|
Quality:
|
||||||
|
Demand:
|
||||||
|
Competition:
|
||||||
|
Markup: How much price markup a material can have before theres a significant dropoff in how much its bought
|
||||||
|
|
||||||
|
Materials Types:
|
||||||
|
1st tier:
|
||||||
|
Water - High Stable Demand, Medium competition, low markup
|
||||||
|
Energy - Suuuuuuper high stable demand, High competition, low markup
|
||||||
|
2nd Tier:
|
||||||
|
Food - High Stable Demand, Lots of competition, medium markup
|
||||||
|
Plants - Initially high but volatile demand. Decent competition, low markup
|
||||||
|
Metal - Very high stable demand, lots of competition, low markup
|
||||||
|
3rd Tier:
|
||||||
|
Hardware - Very high stable demand, lots of competition, med markup
|
||||||
|
Chemicals - High stable demand, good amount of competition, med markup
|
||||||
|
Real estate - Initially high but volatile demand. Decent competition, med markup. Tied to a certain city
|
||||||
|
4th tier:
|
||||||
|
Drugs - High stable demand, lots of competition, medium markup
|
||||||
|
Robots - Very high stable demand, looots of competition, high markup
|
||||||
|
AI Cores - Very high stable demand, looooots of competition, veeery high markup
|
||||||
|
5th tier:
|
||||||
|
Scientific Research
|
||||||
|
|
||||||
|
Industries:
|
||||||
|
- Some Industries let you create your own custom "Products", while others just produce Materials
|
||||||
|
- Each Industry has different characteristics for things
|
||||||
|
- List of Industries:
|
||||||
|
Energy - Requires hardware, real estate
|
||||||
|
Produces Energy
|
||||||
|
Can Use Hardware/AI Cores to increase production
|
||||||
|
More real estate = more production with very little dimishing returns
|
||||||
|
Production increased by scientific research
|
||||||
|
High starting cost
|
||||||
|
Utilities - Requires hardware, Real Estate
|
||||||
|
Produces Water
|
||||||
|
Can use Hardware, Robotics, and AI Cores to increase production
|
||||||
|
More real estate = more production with medium diminishing returns
|
||||||
|
Production increased by scientific research
|
||||||
|
High starting cost
|
||||||
|
Agriculture - Requires Water and Energy
|
||||||
|
Produces food and plants
|
||||||
|
Can use Hardware/Robotics/AI Cores to increase production
|
||||||
|
Production increased by scientific research
|
||||||
|
More real estate = more production with very little diminishing returns
|
||||||
|
Medium starting cost
|
||||||
|
Fishing - Requires energy
|
||||||
|
Produces lots of food
|
||||||
|
Can use Hardware/Robotics/AI Cores to increase production
|
||||||
|
Production increased by scientific research
|
||||||
|
More real estate = slightly more production with very high diminishing returns
|
||||||
|
Medium starting cost (higher than agriculture)
|
||||||
|
Mining - Requires Energy
|
||||||
|
Produces Metal
|
||||||
|
Can use hardware/Robotics/AI Cores to increase production
|
||||||
|
Production increased by scientific research
|
||||||
|
More real estate = more production with medium diminishing returns
|
||||||
|
High starting cost
|
||||||
|
Food - Create your own "restaurant" products
|
||||||
|
Restaurants require food, water, energy, and real estate
|
||||||
|
Restaurants in general are high stable demand, but lots of competition, and medium markup
|
||||||
|
Low starting cost
|
||||||
|
Production increase from real estate diminishes greatly in city. e.g. making many restaurants
|
||||||
|
in one city has high diminishing returns, but making a few in every city is good
|
||||||
|
Tobacco - Create your own tobacco products
|
||||||
|
Requires plants, water, and real estate
|
||||||
|
High volatile demand, but not much competition. Low markup
|
||||||
|
Low starting cost
|
||||||
|
Product quality significantly affected by scientific research
|
||||||
|
Chemical - Create your own chemical products.
|
||||||
|
Requires plants, energy, water, and real estate
|
||||||
|
High stable demand, high competition, low markup
|
||||||
|
Medium starting cost
|
||||||
|
Advertising does very little
|
||||||
|
Product quality significantly affected by scientific research
|
||||||
|
Pharmaceutical - Create your own drug products
|
||||||
|
Requires chemicals, energy, water, and real estate
|
||||||
|
Very high stable demand. High competition, very high markup
|
||||||
|
High starting cost
|
||||||
|
Requires constant creation of new and better products to be successful
|
||||||
|
Product quality significantly affected by scientific research
|
||||||
|
Computer - Creates 'Hardware' material
|
||||||
|
Requires metal, energy, real estate
|
||||||
|
Can use Robotics/AI Cores to increase production
|
||||||
|
More real estate = more production with high diminishing returns
|
||||||
|
Production significantly affected by scientific research
|
||||||
|
High starting cost
|
||||||
|
Robotics - Create 'Robots' material and create your own 'Robot' products
|
||||||
|
Requires hardware, energy, and real estate
|
||||||
|
Production can be improved by using AI Cores
|
||||||
|
Extremely high stable demand, medium competition, high markup
|
||||||
|
Extremely high starting cost
|
||||||
|
Product quality significantly affected by scientific research
|
||||||
|
more real estate = more production with medium diminishing returns for 'Robot' materials
|
||||||
|
Software - Create 'AI Cores' material and create your own software products
|
||||||
|
Requires hardware, energy, real estate
|
||||||
|
Very high stable demand, high competition, low markup
|
||||||
|
Low starting cost
|
||||||
|
Product quality slightly affected by scientific research
|
||||||
|
Healthcare - Open your own hospitals (each is its own product).
|
||||||
|
Requires real estate, robots, AI Cores, energy, water
|
||||||
|
Extremely high stable demand, semi-high competition, super high markup
|
||||||
|
Extremely high starting cost
|
||||||
|
Production increase from real estate diminishes greatly in city. e.g. making many hospitals
|
||||||
|
in one city has high diminishing returns, but making a few in every city is good
|
||||||
|
Real Estate - Create 'Real Estate'.
|
||||||
|
Requires metal, energy, water, hardware
|
||||||
|
Can use Hardware/Robotics/AI Cores to increase production
|
||||||
|
Production slightly affected by scientific research
|
||||||
|
Heavily affected by advertising
|
||||||
|
Biotechnology -
|
||||||
|
Entertainment -
|
||||||
|
Finance -
|
||||||
|
Mass Media -
|
||||||
|
Telecommunications -
|
||||||
|
|
||||||
|
Employees:
|
||||||
|
Has morale and energy that must be managed to maintain productivity
|
||||||
|
Stats:
|
||||||
|
Intelligence, Charisma, Experience, Creativity, Efficiency
|
||||||
|
|
||||||
|
Assigned to different positions. The productivity at each position is determined by
|
||||||
|
stats. I.e. each employe should be assigned to positions based on stats to optimize production
|
||||||
|
|
||||||
|
Position
|
||||||
|
Operations -
|
||||||
|
Engineer -
|
||||||
|
Business -
|
||||||
|
Accounting -
|
||||||
|
Management -
|
||||||
|
Research and Development -
|
||||||
*/
|
*/
|
||||||
|
@ -1132,7 +1132,7 @@ function setGangMemberClickHandlers() {
|
|||||||
//Server panel click handlers
|
//Server panel click handlers
|
||||||
var gangMemberHdrs = document.getElementsByClassName("gang-member-header");
|
var gangMemberHdrs = document.getElementsByClassName("gang-member-header");
|
||||||
if (gangMemberHdrs == null) {
|
if (gangMemberHdrs == null) {
|
||||||
console.log("ERROR: Could not find Active Scripts server panels");
|
console.log("ERROR: Could not find Gang Member Headers");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (let i = 0; i < gangMemberHdrs.length; ++i) {
|
for (let i = 0; i < gangMemberHdrs.length; ++i) {
|
||||||
@ -1243,7 +1243,6 @@ function createGangMemberDisplayElement(memberObj) {
|
|||||||
taskDescP.style.display = "inline";
|
taskDescP.style.display = "inline";
|
||||||
taskDescDiv.appendChild(taskDescP);
|
taskDescDiv.appendChild(taskDescP);
|
||||||
|
|
||||||
|
|
||||||
statsDiv.style.width = "30%";
|
statsDiv.style.width = "30%";
|
||||||
taskDiv.style.width = "30%";
|
taskDiv.style.width = "30%";
|
||||||
taskDescDiv.style.width = "30%";
|
taskDescDiv.style.width = "30%";
|
||||||
|
@ -59,8 +59,8 @@ import {printArray, powerOfTwo} from "../utils/HelperFunctio
|
|||||||
import {createRandomIp} from "../utils/IPAddress.js";
|
import {createRandomIp} from "../utils/IPAddress.js";
|
||||||
import {formatNumber, isString, isHTML} from "../utils/StringHelperFunctions.js";
|
import {formatNumber, isString, isHTML} from "../utils/StringHelperFunctions.js";
|
||||||
|
|
||||||
var hasSingularitySF = false, hasAISF = false, hasBn11SF = false;
|
var hasSingularitySF=false, hasAISF=false, hasBn11SF=false, hasWallStreetSF=false;
|
||||||
var singularitySFLvl = 1;
|
var singularitySFLvl=1, wallStreetSFLvl=1;
|
||||||
|
|
||||||
//Used to check and set flags for every Source File, despite the name of the function
|
//Used to check and set flags for every Source File, despite the name of the function
|
||||||
function initSingularitySFFlags() {
|
function initSingularitySFFlags() {
|
||||||
@ -72,6 +72,10 @@ function initSingularitySFFlags() {
|
|||||||
if (Player.sourceFiles[i].n === 5) {
|
if (Player.sourceFiles[i].n === 5) {
|
||||||
hasAISF = true;
|
hasAISF = true;
|
||||||
}
|
}
|
||||||
|
if (Player.sourceFiles[i].n === 8) {
|
||||||
|
hasWallStreetSF = true;
|
||||||
|
wallStreetSFLvl = Player.sourceFiles[i].lvl;
|
||||||
|
}
|
||||||
if (Player.sourceFiles[i].n === 11) {
|
if (Player.sourceFiles[i].n === 11) {
|
||||||
hasBn11SF = true;
|
hasBn11SF = true;
|
||||||
}
|
}
|
||||||
@ -1979,6 +1983,30 @@ function NetscriptFunctions(workerScript) {
|
|||||||
workerScript.scriptRef.log(txt);
|
workerScript.scriptRef.log(txt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Set Location to slums
|
||||||
|
switch(Player.city) {
|
||||||
|
case Locations.Aevum:
|
||||||
|
Player.location = Locations.AevumSlums;
|
||||||
|
break;
|
||||||
|
case Locations.Chongqing:
|
||||||
|
Player.location = Locations.ChongqingSlums;
|
||||||
|
break;
|
||||||
|
case Locations.Sector12:
|
||||||
|
Player.location = Locations.Sector12Slums;
|
||||||
|
break;
|
||||||
|
case Locations.NewTokyo:
|
||||||
|
Player.location = Locations.NewTokyoSlums;
|
||||||
|
break;
|
||||||
|
case Locations.Ishima:
|
||||||
|
Player.location = Locations.IshimaSlums;
|
||||||
|
break;
|
||||||
|
case Locations.Volhaven:
|
||||||
|
Player.location = Locations.VolhavenSlums;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.log("Invalid Player.city value");
|
||||||
|
}
|
||||||
|
|
||||||
crime = crime.toLowerCase();
|
crime = crime.toLowerCase();
|
||||||
if (crime.includes("shoplift")) {
|
if (crime.includes("shoplift")) {
|
||||||
workerScript.scriptRef.log("Attempting to shoplift...");
|
workerScript.scriptRef.log("Attempting to shoplift...");
|
||||||
@ -2190,4 +2218,5 @@ function NetscriptFunctions(workerScript) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export {NetscriptFunctions, initSingularitySFFlags, hasSingularitySF, hasBn11SF};
|
export {NetscriptFunctions, initSingularitySFFlags, hasSingularitySF, hasBn11SF, hasWallStreetSF,
|
||||||
|
wallStreetSFLvl};
|
||||||
|
@ -89,6 +89,7 @@ BitburnerSaveObject.prototype.saveGame = function(db) {
|
|||||||
window.localStorage.setItem("bitburnerSave", saveString);
|
window.localStorage.setItem("bitburnerSave", saveString);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
if (e.code == 22) {
|
if (e.code == 22) {
|
||||||
|
Engine.createStatusText("Save failed for localStorage! Check console(F12)");
|
||||||
console.log("Failed to save game to localStorage because the size of the save file " +
|
console.log("Failed to save game to localStorage because the size of the save file " +
|
||||||
"is too large. However, the game will still be saved to IndexedDb if your browser " +
|
"is too large. However, the game will still be saved to IndexedDb if your browser " +
|
||||||
"supports it. If you would like to save to localStorage as well, then " +
|
"supports it. If you would like to save to localStorage as well, then " +
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import {CONSTANTS} from "./Constants.js";
|
import {CONSTANTS} from "./Constants.js";
|
||||||
import {Engine} from "./engine.js";
|
import {Engine} from "./engine.js";
|
||||||
import {Locations} from "./Location.js";
|
import {Locations} from "./Location.js";
|
||||||
|
import {hasWallStreetSF, wallStreetSFLvl} from "./NetscriptFunctions.js";
|
||||||
import {WorkerScript} from "./NetscriptWorker.js";
|
import {WorkerScript} from "./NetscriptWorker.js";
|
||||||
import {Player} from "./Player.js";
|
import {Player} from "./Player.js";
|
||||||
|
|
||||||
@ -10,6 +11,11 @@ import {Reviver, Generic_toJSON,
|
|||||||
Generic_fromJSON} from "../utils/JSONReviver.js";
|
Generic_fromJSON} from "../utils/JSONReviver.js";
|
||||||
import numeral from "../utils/numeral.min.js";
|
import numeral from "../utils/numeral.min.js";
|
||||||
import {formatNumber} from "../utils/StringHelperFunctions.js";
|
import {formatNumber} from "../utils/StringHelperFunctions.js";
|
||||||
|
import {yesNoBoxCreate, yesNoTxtInpBoxCreate,
|
||||||
|
yesNoBoxGetYesButton, yesNoBoxGetNoButton,
|
||||||
|
yesNoTxtInpBoxGetYesButton, yesNoTxtInpBoxGetNoButton,
|
||||||
|
yesNoTxtInpBoxGetInput, yesNoBoxClose,
|
||||||
|
yesNoTxtInpBoxClose, yesNoBoxOpen} from "../utils/YesNoBox.js";
|
||||||
|
|
||||||
/* StockMarket.js */
|
/* StockMarket.js */
|
||||||
function Stock(name, symbol, mv, b, otlkMag, initPrice=10000) {
|
function Stock(name, symbol, mv, b, otlkMag, initPrice=10000) {
|
||||||
@ -36,9 +42,95 @@ Stock.fromJSON = function(value) {
|
|||||||
|
|
||||||
Reviver.constructors.Stock = Stock;
|
Reviver.constructors.Stock = Stock;
|
||||||
|
|
||||||
//Order types (long and short for each):
|
var OrderTypes = {
|
||||||
// - Limit Order
|
LimitBuy: "Limit Buy Order",
|
||||||
// - Stop Order
|
LimitSell: "Limit Sell Order",
|
||||||
|
StopBuy: "Stop Buy Order",
|
||||||
|
StopSell: "Stop Sell Order"
|
||||||
|
}
|
||||||
|
|
||||||
|
var PositionTypes = {
|
||||||
|
Long: "L",
|
||||||
|
Short: "S"
|
||||||
|
}
|
||||||
|
|
||||||
|
function placeOrder(stock, shares, price, type, position, workerScript=null) {
|
||||||
|
var tixApi = (workerScript instanceof WorkerScript);
|
||||||
|
var order = new Order(stock, shares, price, type, position);
|
||||||
|
if (isNaN(shares)) {
|
||||||
|
dialogBoxCreate("ERROR: Invalid number of shares specifies for order");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (StockMarket["Orders"] === null) {
|
||||||
|
var orders = {};
|
||||||
|
for (var name in StockMarket) {
|
||||||
|
if (StockMarket.hasOwnProperty(name)) {
|
||||||
|
var stock = StockMarket[name];
|
||||||
|
if (!(stock instanceof Stock)) {continue;}
|
||||||
|
orders[stock.symbol] = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StockMarket["Orders"] = orders;
|
||||||
|
}
|
||||||
|
StockMarket["Orders"].push(order);
|
||||||
|
//Process to see if it should be executed immediately
|
||||||
|
processOrders(order.stock, order.type, order.pos);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function executeOrder(order) {
|
||||||
|
var stock = order.stock;
|
||||||
|
var orderBook = StockMarket["Orders"];
|
||||||
|
var stockOrders = orderBook[stock.symbol];
|
||||||
|
var res = true;
|
||||||
|
switch (order.type) {
|
||||||
|
case OrderTypes.LimitBuy:
|
||||||
|
case OrderTypes.StopBuy:
|
||||||
|
if (order.pos === PositionTypes.Long) {
|
||||||
|
res = buyStock(order.stock, order.shares) && res;
|
||||||
|
} else if (order.pos === PositionTypes.Short) {
|
||||||
|
res = shortStock(oder.stock, order.shares) && res;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OrderTypes.LimitSell:
|
||||||
|
case OrderTypes.StopSell:
|
||||||
|
if (order.pos === PositionTypes.Long) {
|
||||||
|
res = sellStock(order.stock, order.shares) && res;
|
||||||
|
} else if (order.pos === PositionTypes.Short) {
|
||||||
|
res = sellShort(order.stock, order.shares) && res;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (res) {
|
||||||
|
//Remove order from order book
|
||||||
|
for (var i = 0; i < stockOrders.length; ++i) {
|
||||||
|
if (order == stockOrders[i]) {
|
||||||
|
stockOrders.splice(i, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log("ERROR: Could not find the following Order in Order Book: ");
|
||||||
|
console.log(order);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Order(stock, price, type, position) {
|
||||||
|
this.stock = stock;
|
||||||
|
this.shares = shares;
|
||||||
|
this.price = price;
|
||||||
|
this.type = type;
|
||||||
|
this.pos = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
Order.prototype.toJSON = function() {
|
||||||
|
return Generic_toJSON("Order", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Order.fromJSON = function(value) {
|
||||||
|
return Generic_fromJSON(Order, value.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
Reviver.constructors.Order = Order;
|
||||||
|
|
||||||
let StockMarket = {} //Full name to stock object
|
let StockMarket = {} //Full name to stock object
|
||||||
let StockSymbols = {} //Full name to symbol
|
let StockSymbols = {} //Full name to symbol
|
||||||
@ -229,6 +321,16 @@ function initStockMarket() {
|
|||||||
var titanlabs = "Titan Laboratories";
|
var titanlabs = "Titan Laboratories";
|
||||||
var titanlabsStk = new Stock(titanlabs, StockSymbols[titanlabs], 0.6, true, 11, getRandomInt(15000, 20000));
|
var titanlabsStk = new Stock(titanlabs, StockSymbols[titanlabs], 0.6, true, 11, getRandomInt(15000, 20000));
|
||||||
StockMarket[titanlabs] = titanlabsStk;
|
StockMarket[titanlabs] = titanlabsStk;
|
||||||
|
|
||||||
|
var orders = {};
|
||||||
|
for (var name in StockMarket) {
|
||||||
|
if (StockMarket.hasOwnProperty(name)) {
|
||||||
|
var stock = StockMarket[name];
|
||||||
|
if (!(stock instanceof Stock)) {continue;}
|
||||||
|
orders[stock.symbol] = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StockMarket["Orders"] = orders;
|
||||||
}
|
}
|
||||||
|
|
||||||
function initSymbolToStockMap() {
|
function initSymbolToStockMap() {
|
||||||
@ -408,6 +510,7 @@ function updateStockPrices() {
|
|||||||
var v = Math.random();
|
var v = Math.random();
|
||||||
for (var name in StockMarket) {
|
for (var name in StockMarket) {
|
||||||
if (StockMarket.hasOwnProperty(name)) {
|
if (StockMarket.hasOwnProperty(name)) {
|
||||||
|
if (!(stock instanceof Stock)) {continue;}
|
||||||
var stock = StockMarket[name];
|
var stock = StockMarket[name];
|
||||||
var av = (v * stock.mv) / 100;
|
var av = (v * stock.mv) / 100;
|
||||||
if (isNaN(av)) {av = .02;}
|
if (isNaN(av)) {av = .02;}
|
||||||
@ -424,11 +527,19 @@ function updateStockPrices() {
|
|||||||
var c = Math.random();
|
var c = Math.random();
|
||||||
if (c < chc) {
|
if (c < chc) {
|
||||||
stock.price *= (1 + av);
|
stock.price *= (1 + av);
|
||||||
|
processOrders(stock, OrderTypes.LimitBuy, PositionTypes.Short);
|
||||||
|
processOrders(stock, OrderTypes.LimitSell, PositionTypes.Long);
|
||||||
|
processOrders(stock, OrderTypes.StopBuy, PositionTypes.Long);
|
||||||
|
processOrders(stock, OrderTypes.StopSell, PositionTypes.Short);
|
||||||
if (Engine.currentPage == Engine.Page.StockMarket) {
|
if (Engine.currentPage == Engine.Page.StockMarket) {
|
||||||
updateStockTicker(stock, true);
|
updateStockTicker(stock, true);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
stock.price /= (1 + av);
|
stock.price /= (1 + av);
|
||||||
|
processOrders(stock, OrderTypes.LimitBuy, PositionTypes.Long);
|
||||||
|
processOrders(stock, OrderTypes.LimitSell, PositionTypes.Short);
|
||||||
|
processOrders(stock, OrderTypes.StopBuy, PositionTypes.Short);
|
||||||
|
processOrders(stock, OrderTypes.StopSell, PositionTypes.Long);
|
||||||
if (Engine.currentPage == Engine.Page.StockMarket) {
|
if (Engine.currentPage == Engine.Page.StockMarket) {
|
||||||
updateStockTicker(stock, false);
|
updateStockTicker(stock, false);
|
||||||
}
|
}
|
||||||
@ -447,6 +558,68 @@ function updateStockPrices() {
|
|||||||
stock.otlkMag *= -1;
|
stock.otlkMag *= -1;
|
||||||
stock.b = !stock.b;
|
stock.b = !stock.b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Checks and triggers any orders for the specified stock
|
||||||
|
function processOrders(stock, orderType, posType) {
|
||||||
|
var orderBook = StockMarket["Orders"];
|
||||||
|
if (orderBook === null) {
|
||||||
|
var orders = {};
|
||||||
|
for (var name in StockMarket) {
|
||||||
|
if (StockMarket.hasOwnProperty(name)) {
|
||||||
|
var stock = StockMarket[name];
|
||||||
|
if (!(stock instanceof Stock)) {continue;}
|
||||||
|
orders[stock.symbol] = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StockMarket["Orders"] = orders;
|
||||||
|
return; //Newly created, so no orders to process
|
||||||
|
}
|
||||||
|
var stockOrders = orderBook[stock.symbol];
|
||||||
|
if (stockOrders === null || !(stockOrders.constructor === Array)) {
|
||||||
|
console.log("ERROR: Invalid Order book for " + stock.symbol + " in processOrders()");
|
||||||
|
stockOrders = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (var i = 0; i < stockOrders.length; ++i) {
|
||||||
|
var order = stockOrders[i];
|
||||||
|
if (order.type === orderType && order.pos === posType) {
|
||||||
|
switch(order.type) {
|
||||||
|
case OrderTypes.LimitBuy:
|
||||||
|
if (order.pos === PositionTypes.Long && stock.price <= order.price) {
|
||||||
|
executeOrder/*66*/(order);
|
||||||
|
} else if (order.pos === PositionTypes.Short && stock.price >= order.price) {
|
||||||
|
executeOrder/*66*/(order);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OrderTypes.LimitSell:
|
||||||
|
if (order.pos === PositionTypes.Long && stock.price >= order.price) {
|
||||||
|
executeOrder/*66*/(order);
|
||||||
|
} else if (order.pos === PositionTypes.Short && stock.price <= order.price) {
|
||||||
|
executeOrder/*66*/(order);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OrderTypes.StopBuy:
|
||||||
|
if (order.pos === PositionTypes.Long && stock.price >= order.price) {
|
||||||
|
executeOrder/*66*/(order);
|
||||||
|
} else if (order.pos === PositionTypes.Short && stock.price <= order.price) {
|
||||||
|
executeOrder/*66*/(order);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OrderTypes.StopSell:
|
||||||
|
if (order.pos === PositionTypes.Long && stock.price <= order.price) {
|
||||||
|
executeOrder/*66*/(order);
|
||||||
|
} else if (order.pos === PositionTypes.Short && stock.price >= order.price) {
|
||||||
|
executeOrder/*66*/(order);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.log("Invalid order type: " + order.type);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -514,138 +687,14 @@ function displayStockMarketContent() {
|
|||||||
"This means all your positions are lost, so make sure to sell your stocks before installing " +
|
"This means all your positions are lost, so make sure to sell your stocks before installing " +
|
||||||
"Augmentations!";
|
"Augmentations!";
|
||||||
|
|
||||||
var hdrLi = document.createElement("li");
|
|
||||||
var hdrName = document.createElement("p");
|
|
||||||
var hdrSym = document.createElement("p");
|
|
||||||
var hdrPrice = document.createElement("p");
|
|
||||||
var hdrQty = document.createElement("p");
|
|
||||||
var hdrBuySell = document.createElement("p")
|
|
||||||
var hdrAvgPrice = document.createElement("p");
|
|
||||||
var hdrShares = document.createElement("p");
|
|
||||||
var hdrReturn = document.createElement("p");
|
|
||||||
hdrName.style.display = "inline-block";
|
|
||||||
hdrName.innerText = "Stock Name";
|
|
||||||
hdrName.style.width = "8%";
|
|
||||||
hdrSym.style.display = "inline-block";
|
|
||||||
hdrSym.innerText = "Symbol";
|
|
||||||
hdrSym.style.width = "4%";
|
|
||||||
hdrPrice.style.display = "inline-block";
|
|
||||||
hdrPrice.innerText = "Price";
|
|
||||||
hdrPrice.style.width = "8%";
|
|
||||||
hdrQty.style.display = "inline-block";
|
|
||||||
hdrQty.innerText = "Quantity";
|
|
||||||
hdrQty.style.width = "3%";
|
|
||||||
hdrBuySell.style.display = "inline-block";
|
|
||||||
hdrBuySell.innerText = "Buy/Sell";
|
|
||||||
hdrBuySell.style.width = "5%";
|
|
||||||
hdrAvgPrice.style.display = "inline-block";
|
|
||||||
hdrAvgPrice.innerText = "Avg price of owned shares";
|
|
||||||
hdrAvgPrice.style.width = "7.5%";
|
|
||||||
hdrShares.style.display = "inline-block";
|
|
||||||
hdrShares.innerText = "Shares owned";
|
|
||||||
hdrShares.style.width = "4%";
|
|
||||||
hdrReturn.style.display = "inline-block";
|
|
||||||
hdrReturn.innerText = "Total Return";
|
|
||||||
hdrReturn.style.width = "6%";
|
|
||||||
hdrLi.appendChild(hdrName);
|
|
||||||
hdrLi.appendChild(hdrSym);
|
|
||||||
hdrLi.appendChild(hdrPrice);
|
|
||||||
hdrLi.appendChild(hdrQty);
|
|
||||||
hdrLi.appendChild(hdrBuySell);
|
|
||||||
hdrLi.appendChild(hdrAvgPrice);
|
|
||||||
hdrLi.appendChild(hdrShares);
|
|
||||||
hdrLi.appendChild(hdrReturn);
|
|
||||||
stockList.appendChild(hdrLi);
|
|
||||||
|
|
||||||
for (var name in StockMarket) {
|
for (var name in StockMarket) {
|
||||||
if (StockMarket.hasOwnProperty(name)) {
|
if (StockMarket.hasOwnProperty(name)) {
|
||||||
(function() {
|
|
||||||
var stock = StockMarket[name];
|
var stock = StockMarket[name];
|
||||||
|
if (!(stock instanceof Stock)) {continue;} //orders property is an array
|
||||||
var li = document.createElement("li");
|
createStockTicker(stock);
|
||||||
var stkName = document.createElement("p");
|
|
||||||
var stkSym = document.createElement("p");
|
|
||||||
var stkPrice = document.createElement("p");
|
|
||||||
var qtyInput = document.createElement("input");
|
|
||||||
var buyButton = document.createElement("span");
|
|
||||||
var sellButton = document.createElement("span");
|
|
||||||
var avgPriceTxt = document.createElement("p");
|
|
||||||
var sharesTxt = document.createElement("p");
|
|
||||||
var returnTxt = document.createElement("p");
|
|
||||||
|
|
||||||
var tickerId = "stock-market-ticker-" + stock.symbol;
|
|
||||||
stkName.setAttribute("id", tickerId + "-name");
|
|
||||||
stkSym.setAttribute("id", tickerId + "-sym");
|
|
||||||
stkPrice.setAttribute("id", tickerId + "-price");
|
|
||||||
stkName.style.display = "inline-block";
|
|
||||||
stkName.style.width = "8%";
|
|
||||||
stkSym.style.display = "inline-block";
|
|
||||||
stkSym.style.width = "4%";
|
|
||||||
stkPrice.style.display = "inline-block";
|
|
||||||
stkPrice.style.width = "9%";
|
|
||||||
|
|
||||||
li.setAttribute("display", "inline-block");
|
|
||||||
|
|
||||||
qtyInput.setAttribute("type", "text");
|
|
||||||
qtyInput.setAttribute("id", tickerId + "-qty-input");
|
|
||||||
qtyInput.setAttribute("class", "stock-market-qty-input");
|
|
||||||
qtyInput.setAttribute("onkeydown", "return ( event.ctrlKey || event.altKey " +
|
|
||||||
" || (47<event.keyCode && event.keyCode<58 && event.shiftKey==false) " +
|
|
||||||
" || (95<event.keyCode && event.keyCode<106) " +
|
|
||||||
" || (event.keyCode==8) || (event.keyCode==9) " +
|
|
||||||
" || (event.keyCode>34 && event.keyCode<40) " +
|
|
||||||
" || (event.keyCode==46) )");
|
|
||||||
qtyInput.style.width = "3%";
|
|
||||||
qtyInput.style.display = "inline-block";
|
|
||||||
|
|
||||||
buyButton.innerHTML = "Buy";
|
|
||||||
buyButton.setAttribute("class", "stock-market-buy-sell-button");
|
|
||||||
buyButton.style.width = "3%";
|
|
||||||
buyButton.style.display = "inline-block";
|
|
||||||
buyButton.addEventListener("click", function() {
|
|
||||||
var shares = document.getElementById(tickerId + "-qty-input").value;
|
|
||||||
shares = Number(shares);
|
|
||||||
if (isNaN(shares)) {return false;}
|
|
||||||
buyStock(stock, shares);
|
|
||||||
});
|
|
||||||
sellButton.innerHTML = "Sell";
|
|
||||||
sellButton.setAttribute("class", "stock-market-buy-sell-button");
|
|
||||||
sellButton.style.width = "3%";
|
|
||||||
sellButton.style.display = "inline-block";
|
|
||||||
sellButton.addEventListener("click", function() {
|
|
||||||
var shares = document.getElementById(tickerId + "-qty-input").value;
|
|
||||||
shares = Number(shares);
|
|
||||||
if (isNaN(shares)) {return false;}
|
|
||||||
sellStock(stock, shares);
|
|
||||||
});
|
|
||||||
|
|
||||||
avgPriceTxt.setAttribute("id", tickerId + "-avgprice");
|
|
||||||
avgPriceTxt.style.display = "inline-block";
|
|
||||||
avgPriceTxt.style.width = "8%";
|
|
||||||
avgPriceTxt.style.color = "white";
|
|
||||||
sharesTxt.setAttribute("id", tickerId + "-shares");
|
|
||||||
sharesTxt.style.display = "inline-block";
|
|
||||||
sharesTxt.style.width = "4%";
|
|
||||||
sharesTxt.style.color = "white";
|
|
||||||
returnTxt.setAttribute("id", tickerId + "-return");
|
|
||||||
returnTxt.style.display = "inline-block";
|
|
||||||
returnTxt.style.width = "6%";
|
|
||||||
returnTxt.style.color = "white";
|
|
||||||
|
|
||||||
li.appendChild(stkName);
|
|
||||||
li.appendChild(stkSym);
|
|
||||||
li.appendChild(stkPrice);
|
|
||||||
li.appendChild(qtyInput);
|
|
||||||
li.appendChild(buyButton);
|
|
||||||
li.appendChild(sellButton);
|
|
||||||
li.appendChild(avgPriceTxt);
|
|
||||||
li.appendChild(sharesTxt);
|
|
||||||
li.appendChild(returnTxt);
|
|
||||||
stockList.appendChild(li);
|
|
||||||
}()); //Immediate invocation
|
|
||||||
}//End if
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
setStockTickerClickHandlers(); //Clicking headers opens/closes panels
|
||||||
stockMarketContentCreated = true;
|
stockMarketContentCreated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -660,59 +709,278 @@ function displayStockMarketContent() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//'increase' argument is a boolean indicating whether the price increased or decreased
|
function createStockTicker(stock) {
|
||||||
function updateStockTicker(stock, increase) {
|
if (!(stock instanceof Stock)) {
|
||||||
var tickerId = "stock-market-ticker-" + stock.symbol;
|
console.log("Invalid stock in createStockSticker()");
|
||||||
let stkName = document.getElementById(tickerId + "-name");
|
|
||||||
let stkSym = document.getElementById(tickerId + "-sym");
|
|
||||||
let stkPrice = document.getElementById(tickerId + "-price");
|
|
||||||
|
|
||||||
if (stkName == null || stkSym == null || stkPrice == null) {
|
|
||||||
console.log("ERROR, couldn't find elements with tickerId " + tickerId);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
stkName.innerText = stock.name;
|
var tickerId = "stock-market-ticker-" + stock.symbol;
|
||||||
stkSym.innerText = stock.symbol;
|
var li = document.createElement("li"), hdr = document.createElement("button");
|
||||||
stkPrice.innerText = "$" + formatNumber(stock.price, 2).toString();
|
hdr.classList.add("accordion-header");
|
||||||
|
hdr.setAttribute("id", tickerId + "-hdr");
|
||||||
|
hdr.innerHTML = stock.name + " - " + stock.symbol + " - $" + stock.price;
|
||||||
|
|
||||||
var returnTxt = document.getElementById(tickerId + "-return");
|
//Div for entire panel
|
||||||
var totalCost = stock.playerShares * stock.playerAvgPx;
|
var stockDiv = document.createElement("div");
|
||||||
var gains = (stock.price - stock.playerAvgPx) * stock.playerShares;
|
stockDiv.classList.add("accordion-panel");
|
||||||
var percentageGains = gains / totalCost;
|
|
||||||
if (totalCost > 0) {
|
/* Create panel DOM */
|
||||||
returnTxt.innerText = "$" + formatNumber(gains, 2) + " (" +
|
var qtyInput = document.createElement("input"),
|
||||||
formatNumber(percentageGains * 100, 2) + "%)";
|
longShortSelect = document.createElement("select"),
|
||||||
} else {
|
orderTypeSelect = document.createElement("select"),
|
||||||
returnTxt.innerText = "N/A";
|
buyButton = document.createElement("span"),
|
||||||
|
sellButton = document.createElement("span"),
|
||||||
|
positionTxt = document.createElement("p"),
|
||||||
|
orderList = document.createElement("ul");
|
||||||
|
|
||||||
|
qtyInput.classList.add("stock-market-input");
|
||||||
|
qtyInput.setAttribute("id", tickerId + "-qty-input");
|
||||||
|
qtyInput.setAttribute("onkeydown", "return ( event.ctrlKey || event.altKey " +
|
||||||
|
" || (47<event.keyCode && event.keyCode<58 && event.shiftKey==false) " +
|
||||||
|
" || (95<event.keyCode && event.keyCode<106) " +
|
||||||
|
" || (event.keyCode==8) || (event.keyCode==9) " +
|
||||||
|
" || (event.keyCode>34 && event.keyCode<40) " +
|
||||||
|
" || (event.keyCode==46) )");
|
||||||
|
|
||||||
|
longShortSelect.classList.add("stock-market-input");
|
||||||
|
longShortSelect.setAttribute("id", tickerId + "-pos-selector");
|
||||||
|
var longOpt = document.createElement("option");
|
||||||
|
longOpt.text = "Long";
|
||||||
|
longShortSelect.add(longOpt);
|
||||||
|
if (Player.bitNodeN === 8 || (hasWallStreetSF && wallStreetSFLvl >= 2)) {
|
||||||
|
var shortOpt = document.createElement("option");
|
||||||
|
shortOpt.text = "Short";
|
||||||
|
longShortSelect.add(shortOpt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
orderTypeSelect.classList.add("stock-market-input");
|
||||||
|
orderTypeSelect.setAttribute("id", tickerId + "-order-selector");
|
||||||
|
var marketOpt = document.createElement("option");
|
||||||
|
marketOpt.text = "Market Order";
|
||||||
|
orderTypeSelect.add(marketOpt);
|
||||||
|
if (Player.bitNodeN === 8 || (hasWallStreetSF && wallStreetSFLvl >= 3)) {
|
||||||
|
var limitOpt = document.createElement("option");
|
||||||
|
limitOpt.text = "Limit Order";
|
||||||
|
orderTypeSelect.add(limitOpt);
|
||||||
|
var stopOpt = document.createElement("option");
|
||||||
|
stopOpt.text = "Stop Order";
|
||||||
|
orderTypeSelect.add(stopOpt);
|
||||||
|
}
|
||||||
|
|
||||||
if (increase) {
|
buyButton.classList.add("stock-market-input");
|
||||||
stkName.style.color = "#66ff33";
|
buyButton.innerHTML = "Buy";
|
||||||
stkSym.style.color = "#66ff33";
|
buyButton.addEventListener("click", ()=>{
|
||||||
stkPrice.style.color = "#66ff33";
|
var pos = longShortSelect.options[longShortSelect.selectedIndex].text;
|
||||||
|
pos === "Long" ? pos = PositionTypes.Long : pos = PositionTypes.Short;
|
||||||
|
var ordType = orderTypeSelect.options[orderTypeSelect.selectedIndex].text;
|
||||||
|
var shares = Number(document.getElementById(tickerId + "-qty-input").value);
|
||||||
|
if (isNaN(shares)) {return false;}
|
||||||
|
switch (ordType) {
|
||||||
|
case "Market Order":
|
||||||
|
buyStock(stock, shares);
|
||||||
|
break;
|
||||||
|
case "Limit Order":
|
||||||
|
case "Stop Order":
|
||||||
|
var yesBtn = yesNoTxtInpBoxGetYesButton(),
|
||||||
|
noBtn = yesNoTxtInpBoxGetNoButton();
|
||||||
|
yesBtn.innerText = "Place Buy" + ordType;
|
||||||
|
noBtn.innerText = "Cancel Order";
|
||||||
|
yesBtn.addEventListener("click", ()=>{
|
||||||
|
var price = Number(yesNoTxtInpBoxGetInput()), type;
|
||||||
|
if (ordType === "Limit Order") {
|
||||||
|
type = OrderTypes.LimitBuy;
|
||||||
} else {
|
} else {
|
||||||
stkName.style.color = "red";
|
type = OrderTypes.StopBuy;
|
||||||
stkSym.style.color = "red";
|
}
|
||||||
stkPrice.style.color = "red";
|
placeOrder(stock, shares, price, type, pos);
|
||||||
|
});
|
||||||
|
noBtn.addEventListener("click", ()=>{
|
||||||
|
yesNoTxtInpBoxClose();
|
||||||
|
});
|
||||||
|
yesNoTxtInpBoxCreate("Enter the price for your " + ordType);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.log("ERROR: Invalid order type");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
sellButton.classList.add("stock-market-input");
|
||||||
|
sellButton.innerHTML = "Sell";
|
||||||
|
sellButton.addEventListener("click", ()=>{
|
||||||
|
var pos = longShortSelect.options[longShortSelect.selectedIndex].text;
|
||||||
|
pos === "Long" ? pos = PositionTypes.Long : pos = PositionTypes.Short;
|
||||||
|
var ordType = orderTypeSelect.options[orderTypeSelect.selectedIndex].text;
|
||||||
|
var shares = Number(document.getElementById(tickerId + "-qty-input").value);
|
||||||
|
if (isNaN(shares)) {return false;}
|
||||||
|
switch (ordType) {
|
||||||
|
case "Market Order":
|
||||||
|
buyStock(stock, shares);
|
||||||
|
break;
|
||||||
|
case "Limit Order":
|
||||||
|
case "Stop Order":
|
||||||
|
var yesBtn = yesNoTxtInpBoxGetYesButton(),
|
||||||
|
noBtn = yesNoTxtInpBoxGetNoButton();
|
||||||
|
yesBtn.innerText = "Place Sell" + ordType;
|
||||||
|
noBtn.innerText = "Cancel Order";
|
||||||
|
yesBtn.addEventListener("click", ()=>{
|
||||||
|
var price = Number(yesNoTxtInpBoxGetInput()), type;
|
||||||
|
if (ordType === "Limit Order") {
|
||||||
|
type = OrderTypes.LimitSell;
|
||||||
|
} else {
|
||||||
|
type = OrderTypes.StopSell;
|
||||||
|
}
|
||||||
|
placeOrder(stock, shares, price, type, pos);
|
||||||
|
});
|
||||||
|
noBtn.addEventListener("click", ()=>{
|
||||||
|
yesNoTxtInpBoxClose();
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.log("ERROR: Invalid order type");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
positionTxt.setAttribute("id", tickerId + "-position-text");
|
||||||
|
positionTxt.classList.add("stock-market-position-text");
|
||||||
|
|
||||||
|
orderList.setAttribute("id", tickerId + "-order-list");
|
||||||
|
orderList.classList.add("stock-market-order-list");
|
||||||
|
|
||||||
|
stockDiv.appendChild(qtyInput);
|
||||||
|
stockDiv.appendChild(longShortSelect);
|
||||||
|
stockDiv.appendChild(orderTypeSelect);
|
||||||
|
stockDiv.appendChild(buyButton);
|
||||||
|
stockDiv.appendChild(sellButton);
|
||||||
|
stockDiv.appendChild(positionTxt);
|
||||||
|
stockDiv.appendChild(orderList);
|
||||||
|
|
||||||
|
li.appendChild(hdr);
|
||||||
|
li.appendChild(stockDiv);
|
||||||
|
document.getElementById("stock-market-list").appendChild(li);
|
||||||
|
|
||||||
|
updateStockTicker(stock, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setStockTickerClickHandlers() {
|
||||||
|
var stockList = document.getElementById("stock-market-list");
|
||||||
|
var tickerHdrs = stockList.getElementsByClassName("accordion-header");
|
||||||
|
if (tickerHdrs === null) {
|
||||||
|
console.log("ERROR: Could not find header elements for stock tickers");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (var i = 0; i < tickerHdrs.length; ++i) {
|
||||||
|
tickerHdrs[i].onclick = function() {
|
||||||
|
this.classList.toggle("active");
|
||||||
|
|
||||||
|
var panel = this.nextElementSibling;
|
||||||
|
if (panel.style.display === "block") {
|
||||||
|
panel.style.display = "none";
|
||||||
|
} else {
|
||||||
|
panel.style.display = "block";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//'increase' argument is a boolean indicating whether the price increased or decreased
|
||||||
|
function updateStockTicker(stock, increase) {
|
||||||
|
if (!(stock instanceof Stock)) {
|
||||||
|
console.log("Invalid stock in updateStockTicker()");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var tickerId = "stock-market-ticker-" + stock.symbol;
|
||||||
|
var hdr = document.getElementById(tickerId + "-hdr");
|
||||||
|
|
||||||
|
if (hdr === null) {
|
||||||
|
console.log("ERROR: Couldn't find ticker element for stock: " + stock.symbol);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
hdr.innerHTML = stock.name + " - " + stock.symbol + " - $" + stock.price;
|
||||||
|
increase ? hdr.style.color = "#66ff33" : hdr.style.color = "red";
|
||||||
|
|
||||||
|
if (stock.playerShares > 0 || stock.playerShortShares > 0) {
|
||||||
|
updateStockPlayerPosition(stock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateStockPlayerPosition(stock) {
|
function updateStockPlayerPosition(stock) {
|
||||||
var tickerId = "stock-market-ticker-" + stock.symbol;
|
if (!(stock instanceof Stock)) {
|
||||||
var avgPriceTxt = document.getElementById(tickerId + "-avgprice");
|
console.log("Invalid stock in updateStockTicker()");
|
||||||
var sharesTxt = document.getElementById(tickerId + "-shares");
|
|
||||||
if (avgPriceTxt == null || sharesTxt == null) {
|
|
||||||
dialogBoxCreate("Could not find element for player positions for stock " +
|
|
||||||
stock.symbol + ". This is a bug please contact developer");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
avgPriceTxt.innerText = "$" + formatNumber(stock.playerAvgPx, 2);
|
var tickerId = "stock-market-ticker-" + stock.symbol,
|
||||||
sharesTxt.innerText = stock.playerShares.toString();
|
positionTxt = document.getElementById(tickerId + "-position-text");
|
||||||
|
if (positionTxt === null) {
|
||||||
|
console.log("ERROR: Could not find stock position element for: " + stock.symbol);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Calculate returns
|
||||||
|
var totalCost = stock.playerShares * stock.playerAvgPx,
|
||||||
|
gains = (stock.price - stock.playerAvgPx) * stock.playerShares,
|
||||||
|
percentageGains = gains / totalCost;
|
||||||
|
|
||||||
|
var shortTotalCost = stock.playerShortShares * stock.playerAvgShortPx,
|
||||||
|
shortGains = (stock.playerAvgShortPx - stock.price) * stock.playerShortShares,
|
||||||
|
shortPercentageGains = shortGains/ shortTotalCost;
|
||||||
|
|
||||||
|
positionTxt.innerHTML =
|
||||||
|
"<h1 class='tooltip stock-market-position-text'>Long Position: " +
|
||||||
|
"<span class='tooltiptext'>Shares in the long position will increase " +
|
||||||
|
"in value if the price of the corresponding stock increases</span></h1>" +
|
||||||
|
"<br>Shares: " + formatNumber(stock.playerShares, 0) +
|
||||||
|
"<br>Average Price: " + numeral(stock.playerAvgPx).format('$0.000a') +
|
||||||
|
"<br>Profit: " + numeral(gains).format('$0.000a') +
|
||||||
|
" (" + formatNumber(percentageGains, 2) + "%)<br><br>" +
|
||||||
|
"<h1 class='tooltip stock-market-position-text'>Short Position: " +
|
||||||
|
"<span class='tooltiptext'>Shares in short position will increase " +
|
||||||
|
"in value if the price of the corresponding stock decreases</span></h1>" +
|
||||||
|
"<br>Shares: " + formatNumber(stock.playerShortShares, 0) +
|
||||||
|
"<br>Average Price: " + numeral(stock.playerAvgShortPx).format('$0.000a') +
|
||||||
|
"<br>Profit: " + numeral(shortGains).format('$0.000a') +
|
||||||
|
" (" + formatNumber(shortPercentageGains, 2) + "%)";
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateStockOrderList(stock) {
|
||||||
|
var tickerId = "stock-market-ticker-" + stock.symbol;
|
||||||
|
var orderList = document.getElementById(tickerId + "-order-list");
|
||||||
|
if (orderList === null) {
|
||||||
|
console.log("ERROR: Could not find order list for " + stock.symbol);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var orderBook = StockMarket["Orders"];
|
||||||
|
if (orderBook === null) {
|
||||||
|
console.log("ERROR: Could not find order book in stock market");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var stockOrders = orderBook[stock.symbol];
|
||||||
|
if (stockOrders === null) {
|
||||||
|
console.log("ERROR: Could nto find orders for: " + stock.symbol);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Remove everything from list
|
||||||
|
while (orderList.firstChild) {
|
||||||
|
orderList.removeChild(orderList.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < stockOrders.length; ++i) {
|
||||||
|
var order = stockOrders[i];
|
||||||
|
var li = document.createElement("li");
|
||||||
|
var posText = (order.pos === PositionTypes.Long ? "Long Position" : "Short Position");
|
||||||
|
li.innerText = order.type + " - " + posText + " - " +
|
||||||
|
order.shares + " @ $" + formatNumber(order.price, 2);
|
||||||
|
orderList.appendChild(li);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export {StockMarket, StockSymbols, SymbolToStockMap, initStockSymbols,
|
export {StockMarket, StockSymbols, SymbolToStockMap, initStockSymbols,
|
||||||
initStockMarket, initSymbolToStockMap, stockMarketCycle, buyStock,
|
initStockMarket, initSymbolToStockMap, stockMarketCycle, buyStock,
|
||||||
sellStock, updateStockPrices, displayStockMarketContent,
|
sellStock, updateStockPrices, displayStockMarketContent,
|
||||||
updateStockTicker, updateStockPlayerPosition, loadStockMarket,
|
updateStockTicker, updateStockPlayerPosition, loadStockMarket,
|
||||||
setStockMarketContentCreated};
|
setStockMarketContentCreated, placeOrder, Order, OrderTypes};
|
||||||
|
77
src/TextFile.js
Normal file
77
src/TextFile.js
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import {Server} from "./Server.js";
|
||||||
|
import {dialogBoxCreate} from "../utils/DialogBox.js";
|
||||||
|
import {Reviver, Generic_toJSON,
|
||||||
|
Generic_fromJSON} from "../utils/JSONReviver.js";
|
||||||
|
|
||||||
|
function TextFile(fn="", txt="") {
|
||||||
|
this.fn = fn.endsWith(".txt") ? fn : fn + ".txt";
|
||||||
|
this.text = String(txt);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextFile.prototype.append = function(txt) {
|
||||||
|
this.text += String(txt);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextFile.prototype.write = function(txt) {
|
||||||
|
this.text = String(txt);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextFile.prototype.read = function() {
|
||||||
|
return this.txt;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextFile.prototype.show = function() {
|
||||||
|
dialogBoxCreate(this.fn + "<br><br>" + this.text);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextFile.prototype.download = function() {
|
||||||
|
var filename = this.fn;
|
||||||
|
var file = new Blob([this.text], {type: 'text/plain'});
|
||||||
|
if (window.navigator.msSaveOrOpenBlob) {// IE10+
|
||||||
|
window.navigator.msSaveOrOpenBlob(file, filename);
|
||||||
|
} else { // Others
|
||||||
|
var a = document.createElement("a"),
|
||||||
|
url = URL.createObjectURL(file);
|
||||||
|
a.href = url;
|
||||||
|
a.download = this.fn;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
setTimeout(function() {
|
||||||
|
document.body.removeChild(a);
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TextFile.prototype.toJSON = function() {
|
||||||
|
return Generic_toJSON("TextFile", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextFile.fromJSON = function(value) {
|
||||||
|
return Generic_fromJSON(TextFile, value.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
Reviver.constructors.TextFile = TextFile;
|
||||||
|
|
||||||
|
function getTextFile(fn, server) {
|
||||||
|
for (var i = 0; i < server.textFiles.length; ++i) {
|
||||||
|
if (server.textFiles[i].fn === fn) {
|
||||||
|
return server.textFiles[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Returns the TextFile object that was just created
|
||||||
|
function createTextFile(fn, txt, server) {
|
||||||
|
if (getTextFile(fn, server) !== null) {
|
||||||
|
console.log("ERROR: createTextFile failed because the specified " +
|
||||||
|
"server already has a text file with the same fn");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var file = new TextFile(fn, txt);
|
||||||
|
server.textFiles.push(file);
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {TextFile, getTextFile, createTextFile};
|
Loading…
Reference in New Issue
Block a user