mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-18 13:43:49 +01:00
Finished React components for new Stock Market UI
This commit is contained in:
parent
6b3646e981
commit
4809a21e38
@ -17,9 +17,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#stock-market-list li {
|
#stock-market-list {
|
||||||
button {
|
list-style: none;
|
||||||
font-size: $defaultFontSize;
|
|
||||||
|
li {
|
||||||
|
button {
|
||||||
|
font-size: $defaultFontSize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,11 +54,21 @@
|
|||||||
.stock-market-position-text {
|
.stock-market-position-text {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
||||||
|
p {
|
||||||
|
color: #fff;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.stock-market-order-list {
|
.stock-market-order-list {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
max-height: 100px;
|
max-height: 100px;
|
||||||
|
|
||||||
|
li {
|
||||||
|
color: #fff;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.stock-market-order-cancel-btn {
|
.stock-market-order-cancel-btn {
|
||||||
|
2
package-lock.json
generated
2
package-lock.json
generated
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "bitburner",
|
"name": "bitburner",
|
||||||
"version": "0.45.0",
|
"version": "0.46.2",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -77,7 +77,6 @@ import {
|
|||||||
StockMarket,
|
StockMarket,
|
||||||
StockSymbols,
|
StockSymbols,
|
||||||
SymbolToStockMap,
|
SymbolToStockMap,
|
||||||
initStockMarket,
|
|
||||||
initSymbolToStockMap,
|
initSymbolToStockMap,
|
||||||
buyStock,
|
buyStock,
|
||||||
sellStock,
|
sellStock,
|
||||||
@ -1708,7 +1707,7 @@ function NetscriptFunctions(workerScript) {
|
|||||||
throw makeRuntimeRejectMsg(workerScript, "ERROR: Invalid stock symbol passed into sellShort()");
|
throw makeRuntimeRejectMsg(workerScript, "ERROR: Invalid stock symbol passed into sellShort()");
|
||||||
}
|
}
|
||||||
const res = sellShort(stock, shares, workerScript);
|
const res = sellShort(stock, shares, workerScript);
|
||||||
|
|
||||||
return res ? stock.price : 0;
|
return res ? stock.price : 0;
|
||||||
},
|
},
|
||||||
placeOrder(symbol, shares, price, type, pos) {
|
placeOrder(symbol, shares, price, type, pos) {
|
||||||
|
@ -32,7 +32,10 @@ export interface IPlayer {
|
|||||||
factions: string[];
|
factions: string[];
|
||||||
firstTimeTraveled: boolean;
|
firstTimeTraveled: boolean;
|
||||||
hacknetNodes: (HacknetNode | string)[]; // HacknetNode object or IP of Hacknet Server
|
hacknetNodes: (HacknetNode | string)[]; // HacknetNode object or IP of Hacknet Server
|
||||||
|
has4SData: boolean;
|
||||||
|
has4SDataTixApi: boolean;
|
||||||
hashManager: HashManager;
|
hashManager: HashManager;
|
||||||
|
hasTixApiAccess: boolean;
|
||||||
hasWseAccount: boolean;
|
hasWseAccount: boolean;
|
||||||
homeComputer: string;
|
homeComputer: string;
|
||||||
hp: number;
|
hp: number;
|
||||||
|
5
src/StockMarket/IOrderBook.ts
Normal file
5
src/StockMarket/IOrderBook.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { Order } from "./Order";
|
||||||
|
|
||||||
|
export interface IOrderBook {
|
||||||
|
[key: string]: Order[];
|
||||||
|
}
|
10
src/StockMarket/IStockMarket.ts
Normal file
10
src/StockMarket/IStockMarket.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { IOrderBook } from "./IOrderBook";
|
||||||
|
import { Stock } from "./Stock";
|
||||||
|
|
||||||
|
export type IStockMarket = {
|
||||||
|
[key: string]: Stock;
|
||||||
|
} & {
|
||||||
|
lastUpdate: number;
|
||||||
|
storedCycles: number;
|
||||||
|
Orders: IOrderBook;
|
||||||
|
}
|
@ -73,6 +73,11 @@ export class Stock {
|
|||||||
*/
|
*/
|
||||||
readonly cap: number;
|
readonly cap: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stocks previous share price
|
||||||
|
*/
|
||||||
|
lastPrice: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum number of shares that player can own (both long and short combined)
|
* Maximum number of shares that player can own (both long and short combined)
|
||||||
*/
|
*/
|
||||||
@ -114,11 +119,6 @@ export class Stock {
|
|||||||
*/
|
*/
|
||||||
playerShortShares: number;
|
playerShortShares: number;
|
||||||
|
|
||||||
/**
|
|
||||||
* The HTML element that displays the stock's info in the UI
|
|
||||||
*/
|
|
||||||
posTxtEl: HTMLElement | null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stock's share price
|
* Stock's share price
|
||||||
*/
|
*/
|
||||||
@ -162,6 +162,7 @@ export class Stock {
|
|||||||
this.name = p.name;
|
this.name = p.name;
|
||||||
this.symbol = p.symbol;
|
this.symbol = p.symbol;
|
||||||
this.price = toNumber(p.initPrice);
|
this.price = toNumber(p.initPrice);
|
||||||
|
this.lastPrice = this.price;
|
||||||
this.playerShares = 0;
|
this.playerShares = 0;
|
||||||
this.playerAvgPx = 0;
|
this.playerAvgPx = 0;
|
||||||
this.playerShortShares = 0;
|
this.playerShortShares = 0;
|
||||||
@ -182,8 +183,11 @@ export class Stock {
|
|||||||
// Max Shares (Outstanding shares) is a percentage of total shares
|
// Max Shares (Outstanding shares) is a percentage of total shares
|
||||||
const outstandingSharePercentage: number = 0.15;
|
const outstandingSharePercentage: number = 0.15;
|
||||||
this.maxShares = Math.round((this.totalShares * outstandingSharePercentage) / 1e5) * 1e5;
|
this.maxShares = Math.round((this.totalShares * outstandingSharePercentage) / 1e5) * 1e5;
|
||||||
|
}
|
||||||
|
|
||||||
this.posTxtEl = null;
|
changePrice(newPrice: number): void {
|
||||||
|
this.lastPrice = this.price;
|
||||||
|
this.price = newPrice;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -8,6 +8,7 @@ import {
|
|||||||
getStockMarket4SDataCost,
|
getStockMarket4SDataCost,
|
||||||
getStockMarket4STixApiCost
|
getStockMarket4STixApiCost
|
||||||
} from "./StockMarketCosts";
|
} from "./StockMarketCosts";
|
||||||
|
import { InitStockMetadata } from "./data/InitStockMetadata";
|
||||||
import { StockSymbols } from "./data/StockSymbols";
|
import { StockSymbols } from "./data/StockSymbols";
|
||||||
|
|
||||||
import { CONSTANTS } from "../Constants";
|
import { CONSTANTS } from "../Constants";
|
||||||
@ -155,16 +156,6 @@ function executeOrder(order) {
|
|||||||
export let StockMarket = {}; // Maps full stock name -> Stock object
|
export let StockMarket = {}; // Maps full stock name -> Stock object
|
||||||
export let SymbolToStockMap = {}; // Maps symbol -> Stock object
|
export let SymbolToStockMap = {}; // Maps symbol -> Stock object
|
||||||
|
|
||||||
let formatHelpData = {
|
|
||||||
longestName: 0,
|
|
||||||
longestSymbol: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const key in StockSymbols) {
|
|
||||||
formatHelpData.longestName = key.length > formatHelpData.longestName ? key.length : formatHelpData.longestName;
|
|
||||||
formatHelpData.longestSymbol = StockSymbols[key].length > formatHelpData.longestSymbol ? StockSymbols[key].length : formatHelpData.longestSymbol;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function loadStockMarket(saveString) {
|
export function loadStockMarket(saveString) {
|
||||||
if (saveString === "") {
|
if (saveString === "") {
|
||||||
StockMarket = {};
|
StockMarket = {};
|
||||||
@ -174,145 +165,16 @@ export function loadStockMarket(saveString) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function initStockMarket() {
|
export function initStockMarket() {
|
||||||
for (var stk in StockMarket) {
|
for (const stk in StockMarket) {
|
||||||
if (StockMarket.hasOwnProperty(stk)) {
|
if (StockMarket.hasOwnProperty(stk)) {
|
||||||
delete StockMarket[stk];
|
delete StockMarket[stk];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const randInt = getRandomInt;
|
for (const metadata of InitStockMetadata) {
|
||||||
|
const name = metadata.name;
|
||||||
var ecorp = LocationName.AevumECorp;
|
StockMarket[name] = new Stock(metadata);
|
||||||
var ecorpStk = new Stock(ecorp, StockSymbols[ecorp], randInt(40, 50) / 100, true, 19, randInt(17e3, 28e3), 2.4e12);
|
}
|
||||||
StockMarket[ecorp] = ecorpStk;
|
|
||||||
|
|
||||||
var megacorp = LocationName.Sector12MegaCorp;
|
|
||||||
var megacorpStk = new Stock(megacorp, StockSymbols[megacorp], randInt(40,50)/100, true, 19, randInt(24e3, 34e3), 2.4e12);
|
|
||||||
StockMarket[megacorp] = megacorpStk;
|
|
||||||
|
|
||||||
var blade = LocationName.Sector12BladeIndustries;
|
|
||||||
var bladeStk = new Stock(blade, StockSymbols[blade], randInt(70, 80)/100, true, 13, randInt(12e3, 25e3), 1.6e12);
|
|
||||||
StockMarket[blade] = bladeStk;
|
|
||||||
|
|
||||||
var clarke = LocationName.AevumClarkeIncorporated;
|
|
||||||
var clarkeStk = new Stock(clarke, StockSymbols[clarke], randInt(65, 75)/100, true, 12, randInt(10e3, 25e3), 1.5e12);
|
|
||||||
StockMarket[clarke] = clarkeStk;
|
|
||||||
|
|
||||||
var omnitek = LocationName.VolhavenOmniTekIncorporated;
|
|
||||||
var omnitekStk = new Stock(omnitek, StockSymbols[omnitek], randInt(60, 70)/100, true, 12, randInt(32e3, 43e3), 1.8e12);
|
|
||||||
StockMarket[omnitek] = omnitekStk;
|
|
||||||
|
|
||||||
var foursigma = LocationName.Sector12FourSigma;
|
|
||||||
var foursigmaStk = new Stock(foursigma, StockSymbols[foursigma], randInt(100, 110)/100, true, 17, randInt(50e3, 80e3), 2e12);
|
|
||||||
StockMarket[foursigma] = foursigmaStk;
|
|
||||||
|
|
||||||
var kuaigong = LocationName.ChongqingKuaiGongInternational;
|
|
||||||
var kuaigongStk = new Stock(kuaigong, StockSymbols[kuaigong], randInt(75, 85)/100, true, 10, randInt(16e3, 28e3), 1.9e12);
|
|
||||||
StockMarket[kuaigong] = kuaigongStk;
|
|
||||||
|
|
||||||
var fulcrum = LocationName.AevumFulcrumTechnologies;
|
|
||||||
var fulcrumStk = new Stock(fulcrum, StockSymbols[fulcrum], randInt(120, 130)/100, true, 16, randInt(29e3, 36e3), 2e12);
|
|
||||||
StockMarket[fulcrum] = fulcrumStk;
|
|
||||||
|
|
||||||
var storm = LocationName.IshimaStormTechnologies;
|
|
||||||
var stormStk = new Stock(storm, StockSymbols[storm], randInt(80, 90)/100, true, 7, randInt(20e3, 25e3), 1.2e12);
|
|
||||||
StockMarket[storm] = stormStk;
|
|
||||||
|
|
||||||
var defcomm = LocationName.NewTokyoDefComm;
|
|
||||||
var defcommStk = new Stock(defcomm, StockSymbols[defcomm], randInt(60, 70)/100, true, 10, randInt(6e3, 19e3), 900e9);
|
|
||||||
StockMarket[defcomm] = defcommStk;
|
|
||||||
|
|
||||||
var helios = LocationName.VolhavenHeliosLabs;
|
|
||||||
var heliosStk = new Stock(helios, StockSymbols[helios], randInt(55, 65)/100, true, 9, randInt(10e3, 18e3), 825e9);
|
|
||||||
StockMarket[helios] = heliosStk;
|
|
||||||
|
|
||||||
var vitalife = LocationName.NewTokyoVitaLife;
|
|
||||||
var vitalifeStk = new Stock(vitalife, StockSymbols[vitalife], randInt(70, 80)/100, true, 7, randInt(8e3, 14e3), 1e12);
|
|
||||||
StockMarket[vitalife] = vitalifeStk;
|
|
||||||
|
|
||||||
var icarus = LocationName.Sector12IcarusMicrosystems;
|
|
||||||
var icarusStk = new Stock(icarus, StockSymbols[icarus], randInt(60, 70)/100, true, 7.5, randInt(12e3, 24e3), 800e9);
|
|
||||||
StockMarket[icarus] = icarusStk;
|
|
||||||
|
|
||||||
var universalenergy = LocationName.Sector12UniversalEnergy;
|
|
||||||
var universalenergyStk = new Stock(universalenergy, StockSymbols[universalenergy], randInt(50, 60)/100, true, 10, randInt(16e3, 29e3), 900e9);
|
|
||||||
StockMarket[universalenergy] = universalenergyStk;
|
|
||||||
|
|
||||||
var aerocorp = LocationName.AevumAeroCorp;
|
|
||||||
var aerocorpStk = new Stock(aerocorp, StockSymbols[aerocorp], randInt(55, 65)/100, true, 6, randInt(8e3, 17e3), 640e9);
|
|
||||||
StockMarket[aerocorp] = aerocorpStk;
|
|
||||||
|
|
||||||
var omnia = LocationName.VolhavenOmniaCybersystems;
|
|
||||||
var omniaStk = new Stock(omnia, StockSymbols[omnia], randInt(65, 75)/100, true, 4.5, randInt(6e3, 15e3), 600e9);
|
|
||||||
StockMarket[omnia] = omniaStk;
|
|
||||||
|
|
||||||
var solaris = LocationName.ChongqingSolarisSpaceSystems;
|
|
||||||
var solarisStk = new Stock(solaris, StockSymbols[solaris], randInt(70, 80)/100, true, 8.5, randInt(14e3, 28e3), 705e9);
|
|
||||||
StockMarket[solaris] = solarisStk;
|
|
||||||
|
|
||||||
var globalpharm = LocationName.NewTokyoGlobalPharmaceuticals;
|
|
||||||
var globalpharmStk = new Stock(globalpharm, StockSymbols[globalpharm], randInt(55, 65)/100, true, 10.5, randInt(12e3, 30e3), 695e9);
|
|
||||||
StockMarket[globalpharm] = globalpharmStk;
|
|
||||||
|
|
||||||
var nova = LocationName.IshimaNovaMedical;
|
|
||||||
var novaStk = new Stock(nova, StockSymbols[nova], randInt(70, 80)/100, true, 5, randInt(15e3, 27e3), 600e9);
|
|
||||||
StockMarket[nova] = novaStk;
|
|
||||||
|
|
||||||
var watchdog = LocationName.AevumWatchdogSecurity;
|
|
||||||
var watchdogStk = new Stock(watchdog, StockSymbols[watchdog], randInt(240, 260)/100, true, 1.5, randInt(4e3, 8.5e3), 450e9);
|
|
||||||
StockMarket[watchdog] = watchdogStk;
|
|
||||||
|
|
||||||
var lexocorp = LocationName.VolhavenLexoCorp;
|
|
||||||
var lexocorpStk = new Stock(lexocorp, StockSymbols[lexocorp], randInt(115, 135)/100, true, 6, randInt(4.5e3, 8e3), 300e9);
|
|
||||||
StockMarket[lexocorp] = lexocorpStk;
|
|
||||||
|
|
||||||
var rho = LocationName.AevumRhoConstruction;
|
|
||||||
var rhoStk = new Stock(rho, StockSymbols[rho], randInt(50, 70)/100, true, 1, randInt(2e3, 7e3), 180e9);
|
|
||||||
StockMarket[rho] = rhoStk;
|
|
||||||
|
|
||||||
var alpha = LocationName.Sector12AlphaEnterprises;
|
|
||||||
var alphaStk = new Stock(alpha, StockSymbols[alpha], randInt(175, 205)/100, true, 10, randInt(4e3, 8.5e3), 240e9);
|
|
||||||
StockMarket[alpha] = alphaStk;
|
|
||||||
|
|
||||||
var syscore = LocationName.VolhavenSysCoreSecurities;
|
|
||||||
var syscoreStk = new Stock(syscore, StockSymbols[syscore], randInt(150, 170)/100, true, 3, randInt(3e3, 8e3), 200e9);
|
|
||||||
StockMarket[syscore] = syscoreStk;
|
|
||||||
|
|
||||||
var computek = LocationName.VolhavenCompuTek;
|
|
||||||
var computekStk = new Stock(computek, StockSymbols[computek], randInt(80, 100)/100, true, 4, randInt(1e3, 6e3), 185e9);
|
|
||||||
StockMarket[computek] = computekStk;
|
|
||||||
|
|
||||||
var netlink = LocationName.AevumNetLinkTechnologies;
|
|
||||||
var netlinkStk = new Stock(netlink, StockSymbols[netlink], randInt(400, 430)/100, true, 1, randInt(1e3, 5e3), 58e9);
|
|
||||||
StockMarket[netlink] = netlinkStk;
|
|
||||||
|
|
||||||
var omega = LocationName.IshimaOmegaSoftware;
|
|
||||||
var omegaStk = new Stock(omega, StockSymbols[omega], randInt(90, 110)/100, true, 0.5, randInt(1e3, 8e3), 60e9);
|
|
||||||
StockMarket[omega] = omegaStk;
|
|
||||||
|
|
||||||
var fns = LocationName.Sector12FoodNStuff;
|
|
||||||
var fnsStk = new Stock(fns, StockSymbols[fns], randInt(70, 80)/100, false, 1, randInt(500, 4.5e3), 45e9);
|
|
||||||
StockMarket[fns] = fnsStk;
|
|
||||||
|
|
||||||
var sigmacosm = "Sigma Cosmetics";
|
|
||||||
var sigmacosmStk = new Stock(sigmacosm, StockSymbols[sigmacosm], randInt(260, 300)/100, true, 0, randInt(1.5e3, 3.5e3), 30e9);
|
|
||||||
StockMarket[sigmacosm] = sigmacosmStk;
|
|
||||||
|
|
||||||
var joesguns = "Joes Guns";
|
|
||||||
var joesgunsStk = new Stock(joesguns, StockSymbols[joesguns], randInt(360, 400)/100, true, 1, randInt(250, 1.5e3), 42e9);
|
|
||||||
StockMarket[joesguns] = joesgunsStk;
|
|
||||||
|
|
||||||
var catalyst = "Catalyst Ventures";
|
|
||||||
var catalystStk = new Stock(catalyst, StockSymbols[catalyst], randInt(120, 175)/100, true, 13.5, randInt(250, 1.5e3), 100e9);
|
|
||||||
StockMarket[catalyst] = catalystStk;
|
|
||||||
|
|
||||||
var microdyne = "Microdyne Technologies";
|
|
||||||
var microdyneStk = new Stock(microdyne, StockSymbols[microdyne], randInt(70, 80)/100, true, 8, randInt(15e3, 30e3), 360e9);
|
|
||||||
StockMarket[microdyne] = microdyneStk;
|
|
||||||
|
|
||||||
var titanlabs = "Titan Laboratories";
|
|
||||||
var titanlabsStk = new Stock(titanlabs, StockSymbols[titanlabs], randInt(50, 70)/100, true, 11, randInt(12e3, 24e3), 420e9);
|
|
||||||
StockMarket[titanlabs] = titanlabsStk;
|
|
||||||
|
|
||||||
var orders = {};
|
var orders = {};
|
||||||
for (var name in StockMarket) {
|
for (var name in StockMarket) {
|
||||||
@ -329,14 +191,14 @@ export function initStockMarket() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function initSymbolToStockMap() {
|
export function initSymbolToStockMap() {
|
||||||
for (var name in StockSymbols) {
|
for (const name in StockSymbols) {
|
||||||
if (StockSymbols.hasOwnProperty(name)) {
|
if (StockSymbols.hasOwnProperty(name)) {
|
||||||
var stock = StockMarket[name];
|
const stock = StockMarket[name];
|
||||||
if (stock == null) {
|
if (stock == null) {
|
||||||
console.error(`Could not find Stock for ${name}`);
|
console.error(`Could not find Stock for ${name}`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var symbol = StockSymbols[name];
|
const symbol = StockSymbols[name];
|
||||||
SymbolToStockMap[symbol] = stock;
|
SymbolToStockMap[symbol] = stock;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
11
src/StockMarket/data/TickerHeaderFormatData.ts
Normal file
11
src/StockMarket/data/TickerHeaderFormatData.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { StockSymbols } from "./StockSymbols";
|
||||||
|
|
||||||
|
export const TickerHeaderFormatData = {
|
||||||
|
longestName: 0,
|
||||||
|
longestSymbol: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const key in StockSymbols) {
|
||||||
|
TickerHeaderFormatData.longestName = Math.max(key.length, TickerHeaderFormatData.longestName);
|
||||||
|
TickerHeaderFormatData.longestSymbol = Math.max(StockSymbols[key].length, TickerHeaderFormatData.longestSymbol);
|
||||||
|
}
|
222
src/StockMarket/ui/InfoAndPurchases.tsx
Normal file
222
src/StockMarket/ui/InfoAndPurchases.tsx
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
/**
|
||||||
|
* React component for the Stock Market UI. This component displays
|
||||||
|
* general information about the stock market, buttons for the various purchases,
|
||||||
|
* and a link to the documentation (Investopedia)
|
||||||
|
*/
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import {
|
||||||
|
getStockMarket4SDataCost,
|
||||||
|
getStockMarket4STixApiCost
|
||||||
|
} from "../StockMarketCosts";
|
||||||
|
|
||||||
|
import { CONSTANTS } from "../../Constants";
|
||||||
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
|
import { StdButton } from "../../ui/React/StdButton";
|
||||||
|
import { StdButtonPurchased } from "../../ui/React/StdButtonPurchased";
|
||||||
|
|
||||||
|
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
initStockMarket: () => void;
|
||||||
|
p: IPlayer;
|
||||||
|
rerender: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const blockStyleMarkup = {
|
||||||
|
display: "block",
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InfoAndPurchases extends React.Component<IProps, any> {
|
||||||
|
constructor(props: IProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.handleClick4SMarketDataHelpTip = this.handleClick4SMarketDataHelpTip.bind(this);
|
||||||
|
this.purchaseWseAccount = this.purchaseWseAccount.bind(this);
|
||||||
|
this.purchaseTixApiAccess = this.purchaseTixApiAccess.bind(this);
|
||||||
|
this.purchase4SMarketData = this.purchase4SMarketData.bind(this);
|
||||||
|
this.purchase4SMarketDataTixApiAccess = this.purchase4SMarketDataTixApiAccess.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleClick4SMarketDataHelpTip() {
|
||||||
|
dialogBoxCreate(
|
||||||
|
"Access to the 4S Market Data feed will display two additional pieces " +
|
||||||
|
"of information about each stock: Price Forecast & Volatility<br><br>" +
|
||||||
|
"Price Forecast indicates the probability the stock has of increasing or " +
|
||||||
|
"decreasing. A '+' forecast means the stock has a higher chance of increasing " +
|
||||||
|
"than decreasing, and a '-' means the opposite. The number of '+/-' symbols " +
|
||||||
|
"is used to illustrate the magnitude of these probabilities. For example, " +
|
||||||
|
"'+++' means that the stock has a significantly higher chance of increasing " +
|
||||||
|
"than decreasing, while '+' means that the stock only has a slightly higher chance " +
|
||||||
|
"of increasing than decreasing.<br><br>" +
|
||||||
|
"Volatility represents the maximum percentage by which a stock's price " +
|
||||||
|
"can change every tick (a tick occurs every few seconds while the game " +
|
||||||
|
"is running).<br><br>" +
|
||||||
|
"A stock's price forecast can change over time. This is also affected by volatility. " +
|
||||||
|
"The more volatile a stock is, the more its price forecast will change."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
purchaseWseAccount() {
|
||||||
|
if (this.props.p.hasWseAccount) { return; }
|
||||||
|
if (!this.props.p.canAfford(CONSTANTS.WSEAccountCost)) { return; }
|
||||||
|
this.props.p.hasWseAccount = true;
|
||||||
|
this.props.initStockMarket();
|
||||||
|
this.props.p.loseMoney(CONSTANTS.WSEAccountCost);
|
||||||
|
this.props.rerender();
|
||||||
|
}
|
||||||
|
|
||||||
|
purchaseTixApiAccess() {
|
||||||
|
if (this.props.p.hasTixApiAccess) { return; }
|
||||||
|
if (!this.props.p.canAfford(CONSTANTS.TIXAPICost)) { return; }
|
||||||
|
this.props.p.hasTixApiAccess = true;
|
||||||
|
this.props.p.loseMoney(CONSTANTS.TIXAPICost);
|
||||||
|
this.props.rerender();
|
||||||
|
}
|
||||||
|
|
||||||
|
purchase4SMarketData() {
|
||||||
|
if (this.props.p.has4SData) { return; }
|
||||||
|
if (!this.props.p.canAfford(getStockMarket4SDataCost())) { return; }
|
||||||
|
this.props.p.has4SData = true;
|
||||||
|
this.props.p.loseMoney(getStockMarket4SDataCost());
|
||||||
|
this.props.rerender();
|
||||||
|
}
|
||||||
|
|
||||||
|
purchase4SMarketDataTixApiAccess() {
|
||||||
|
if (this.props.p.has4SDataTixApi) { return; }
|
||||||
|
if (!this.props.p.canAfford(getStockMarket4STixApiCost())) { return; }
|
||||||
|
this.props.p.has4SDataTixApi = true;
|
||||||
|
this.props.p.loseMoney(getStockMarket4STixApiCost());
|
||||||
|
this.props.rerender();
|
||||||
|
}
|
||||||
|
|
||||||
|
renderPurchaseWseAccountButton(): React.ReactElement {
|
||||||
|
if (this.props.p.hasWseAccount) {
|
||||||
|
return (
|
||||||
|
<StdButtonPurchased text={"WSE Account - Purchased"} />
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
const cost = CONSTANTS.WSEAccountCost;
|
||||||
|
return (
|
||||||
|
<StdButton
|
||||||
|
disabled={!this.props.p.canAfford(cost)}
|
||||||
|
onClick={this.purchaseWseAccount}
|
||||||
|
text={`Buy WSE Account - ${numeralWrapper.formatMoney(cost)}`}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderPurchaseTixApiAccessButton(): React.ReactElement {
|
||||||
|
if (this.props.p.hasTixApiAccess) {
|
||||||
|
return (
|
||||||
|
<StdButtonPurchased text={"TIX API Access - Purchased"} />
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
const cost = CONSTANTS.TIXAPICost;
|
||||||
|
return (
|
||||||
|
<StdButton
|
||||||
|
disabled={!this.props.p.canAfford(cost)}
|
||||||
|
onClick={this.purchaseTixApiAccess}
|
||||||
|
style={blockStyleMarkup}
|
||||||
|
text={`Buy Trade Information eXchange (TIX) API Access - ${numeralWrapper.formatMoney(cost)}`}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderPurchase4SMarketDataButton(): React.ReactElement {
|
||||||
|
if (this.props.p.has4SData) {
|
||||||
|
return (
|
||||||
|
<StdButtonPurchased
|
||||||
|
text={"4S Market Data - Purchased"}
|
||||||
|
tooltip={"Lets you view additional pricing and volatility information about stocks"}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
const cost = getStockMarket4SDataCost();
|
||||||
|
return (
|
||||||
|
<StdButton
|
||||||
|
disabled={!this.props.p.canAfford(cost)}
|
||||||
|
onClick={this.purchase4SMarketData}
|
||||||
|
text={`Buy 4S Market Data Access - ${numeralWrapper.formatMoney(cost)}`}
|
||||||
|
tooltip={"Lets you view additional pricing and volatility information about stocks"}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderPurchase4SMarketDataTixApiAccessButton(): React.ReactElement {
|
||||||
|
if (!this.props.p.hasTixApiAccess) {
|
||||||
|
return (
|
||||||
|
<StdButton
|
||||||
|
disabled={true}
|
||||||
|
text={`Buy 4S Market Data TIX API Access`}
|
||||||
|
tooltip={"Requires TIX API Access"}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
} else if (this.props.p.has4SDataTixApi) {
|
||||||
|
return (
|
||||||
|
<StdButtonPurchased
|
||||||
|
text={"4S Market Data TIX API - Purchased"}
|
||||||
|
tooltip={"Let you access 4S Market Data through Netscript"}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
const cost = getStockMarket4STixApiCost();
|
||||||
|
return (
|
||||||
|
<StdButton
|
||||||
|
disabled={!this.props.p.canAfford(cost)}
|
||||||
|
onClick={this.purchase4SMarketDataTixApiAccess}
|
||||||
|
text={`Buy 4S Market Data TIX API Access - ${numeralWrapper.formatMoney(cost)}`}
|
||||||
|
tooltip={"Let you access 4S Market Data through Netscript"}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const documentationLink = "https://bitburner.readthedocs.io/en/latest/basicgameplay/stockmarket.html";
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<p>Welcome to the World Stock Exchange (WSE)!</p><br /><br />
|
||||||
|
<p>
|
||||||
|
To begin trading, you must first purchase an account.
|
||||||
|
</p>
|
||||||
|
{this.renderPurchaseWseAccountButton()}
|
||||||
|
<a className={"std-button"} href={documentationLink} target={"_blank"}>
|
||||||
|
Investopedia
|
||||||
|
</a>
|
||||||
|
<h2>Trade Information eXchange (TIX) API</h2>
|
||||||
|
<p>
|
||||||
|
TIX, short for Trade Information eXchange, is the communications protocol
|
||||||
|
used by the WSE. Purchasing access to the TIX API lets you write code to create
|
||||||
|
your own algorithmic/automated trading strategies.
|
||||||
|
</p>
|
||||||
|
{this.renderPurchaseTixApiAccessButton()}
|
||||||
|
<h2>Four Sigma (4S) Market Data Feed</h2>
|
||||||
|
<p>
|
||||||
|
Four Sigma's (4S) Market Data Feed provides information about stocks that will help
|
||||||
|
your trading strategies.
|
||||||
|
</p>
|
||||||
|
{this.renderPurchase4SMarketDataButton()}
|
||||||
|
<button className={"help-tip-big"} onClick={this.handleClick4SMarketDataHelpTip}>
|
||||||
|
?
|
||||||
|
</button>
|
||||||
|
{this.renderPurchase4SMarketDataTixApiAccessButton()}
|
||||||
|
<p>
|
||||||
|
Commission Fees: Every transaction you make has
|
||||||
|
a {numeralWrapper.formatMoney(CONSTANTS.StockMarketCommission)} commission fee.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
WARNING: When you reset after installing Augmentations, the Stock
|
||||||
|
Market is reset. You will retain your WSE Account, access to the
|
||||||
|
TIX API, and 4S Market Data access. However, all of your stock
|
||||||
|
positions are lost, so make sure to sell your stocks before
|
||||||
|
installing Augmentations!
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
75
src/StockMarket/ui/Root.tsx
Normal file
75
src/StockMarket/ui/Root.tsx
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/**
|
||||||
|
* Root React component for the Stock Market UI
|
||||||
|
*/
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { InfoAndPurchases } from "./InfoAndPurchases";
|
||||||
|
import { StockTickers } from "./StockTickers";
|
||||||
|
|
||||||
|
import { IStockMarket } from "../IStockMarket";
|
||||||
|
import { Stock } from "../Stock";
|
||||||
|
import { OrderTypes } from "../data/OrderTypes";
|
||||||
|
import { PositionTypes } from "../data/PositionTypes";
|
||||||
|
|
||||||
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
|
||||||
|
type txFn = (stock: Stock, shares: number) => boolean;
|
||||||
|
export type placeOrderFn = (stock: Stock, shares: number, price: number, ordType: OrderTypes, posType: PositionTypes) => boolean;
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
buyStockLong: txFn;
|
||||||
|
buyStockShort: txFn;
|
||||||
|
cancelOrder: (params: object) => void;
|
||||||
|
initStockMarket: () => void;
|
||||||
|
p: IPlayer;
|
||||||
|
placeOrder: placeOrderFn;
|
||||||
|
sellStockLong: txFn;
|
||||||
|
sellStockShort: txFn;
|
||||||
|
stockMarket: IStockMarket;
|
||||||
|
}
|
||||||
|
|
||||||
|
type IState = {
|
||||||
|
rerenderFlag: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StockMarketRoot extends React.Component<IProps, IState> {
|
||||||
|
constructor(props: IProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
rerenderFlag: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
this.rerender = this.rerender.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
rerender(): void {
|
||||||
|
this.setState((prevState) => {
|
||||||
|
return {
|
||||||
|
rerenderFlag: !prevState.rerenderFlag,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<InfoAndPurchases
|
||||||
|
initStockMarket={this.props.initStockMarket}
|
||||||
|
p={this.props.p}
|
||||||
|
rerender={this.rerender}
|
||||||
|
/>
|
||||||
|
<StockTickers
|
||||||
|
buyStockLong={this.props.buyStockLong}
|
||||||
|
buyStockShort={this.props.buyStockShort}
|
||||||
|
cancelOrder={this.props.cancelOrder}
|
||||||
|
p={this.props.p}
|
||||||
|
placeOrder={this.props.placeOrder}
|
||||||
|
sellStockLong={this.props.sellStockLong}
|
||||||
|
sellStockShort={this.props.sellStockShort}
|
||||||
|
stockMarket={this.props.stockMarket}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
343
src/StockMarket/ui/StockTicker.tsx
Normal file
343
src/StockMarket/ui/StockTicker.tsx
Normal file
@ -0,0 +1,343 @@
|
|||||||
|
/**
|
||||||
|
* React Component for a single stock ticker in the Stock Market UI
|
||||||
|
*/
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { StockTickerHeaderText } from "./StockTickerHeaderText";
|
||||||
|
import { StockTickerOrderList } from "./StockTickerOrderList";
|
||||||
|
import { StockTickerPositionText } from "./StockTickerPositionText";
|
||||||
|
import { StockTickerTxButton } from "./StockTickerTxButton";
|
||||||
|
|
||||||
|
import { Order } from "../Order";
|
||||||
|
import { Stock } from "../Stock";
|
||||||
|
import { OrderTypes } from "../data/OrderTypes";
|
||||||
|
import { PositionTypes } from "../data/PositionTypes";
|
||||||
|
|
||||||
|
import { CONSTANTS } from "../../Constants";
|
||||||
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
|
||||||
|
|
||||||
|
import { dialogBoxCreate } from "../../../utils/DialogBox";
|
||||||
|
import {
|
||||||
|
yesNoTxtInpBoxClose,
|
||||||
|
yesNoTxtInpBoxCreate,
|
||||||
|
yesNoTxtInpBoxGetInput,
|
||||||
|
yesNoTxtInpBoxGetNoButton,
|
||||||
|
yesNoTxtInpBoxGetYesButton,
|
||||||
|
} from "../../../utils/YesNoBox";
|
||||||
|
|
||||||
|
enum SelectorOrderType {
|
||||||
|
Market = "Market Order",
|
||||||
|
Limit = "Limit Order",
|
||||||
|
Stop = "Stop Order",
|
||||||
|
}
|
||||||
|
|
||||||
|
export type txFn = (stock: Stock, shares: number) => boolean;
|
||||||
|
export type placeOrderFn = (stock: Stock, shares: number, price: number, ordType: OrderTypes, posType: PositionTypes) => boolean;
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
buyStockLong: txFn;
|
||||||
|
buyStockShort: txFn;
|
||||||
|
cancelOrder: (params: object) => void;
|
||||||
|
orders: Order[];
|
||||||
|
p: IPlayer;
|
||||||
|
placeOrder: placeOrderFn;
|
||||||
|
sellStockLong: txFn;
|
||||||
|
sellStockShort: txFn;
|
||||||
|
stock: Stock;
|
||||||
|
}
|
||||||
|
|
||||||
|
type IState = {
|
||||||
|
orderType: SelectorOrderType;
|
||||||
|
position: PositionTypes;
|
||||||
|
qty: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StockTicker extends React.Component<IProps, IState> {
|
||||||
|
constructor(props: IProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
orderType: SelectorOrderType.Market,
|
||||||
|
position: PositionTypes.Long,
|
||||||
|
qty: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
this.handleBuyButtonClick = this.handleBuyButtonClick.bind(this);
|
||||||
|
this.handleBuyMaxButtonClick = this.handleBuyMaxButtonClick.bind(this);
|
||||||
|
this.handleOrderTypeChange = this.handleOrderTypeChange.bind(this);
|
||||||
|
this.handlePositionTypeChange = this.handlePositionTypeChange.bind(this);
|
||||||
|
this.handleQuantityChange = this.handleQuantityChange.bind(this);
|
||||||
|
this.handleSellButtonClick = this.handleSellButtonClick.bind(this);
|
||||||
|
this.handleSellAllButtonClick = this.handleSellAllButtonClick.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
createPlaceOrderPopupBox(yesTxt: string, popupTxt: string, yesBtnCb: (price: number) => void) {
|
||||||
|
const yesBtn = yesNoTxtInpBoxGetYesButton();
|
||||||
|
const noBtn = yesNoTxtInpBoxGetNoButton();
|
||||||
|
|
||||||
|
yesBtn!.innerText = yesTxt;
|
||||||
|
yesBtn!.addEventListener("click", () => {
|
||||||
|
const price = parseFloat(yesNoTxtInpBoxGetInput());
|
||||||
|
if (isNaN(price)) {
|
||||||
|
dialogBoxCreate(`Invalid input for price: ${yesNoTxtInpBoxGetInput()}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
yesBtnCb(price);
|
||||||
|
yesNoTxtInpBoxClose();
|
||||||
|
});
|
||||||
|
|
||||||
|
noBtn!.innerText = "Cancel Order";
|
||||||
|
noBtn!.addEventListener("click", () => {
|
||||||
|
yesNoTxtInpBoxClose();
|
||||||
|
});
|
||||||
|
|
||||||
|
yesNoTxtInpBoxCreate(popupTxt);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleBuyButtonClick() {
|
||||||
|
const shares = parseInt(this.state.qty);
|
||||||
|
if (isNaN(shares)) {
|
||||||
|
dialogBoxCreate(`Invalid input for quantity (number of shares): ${this.state.qty}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (this.state.orderType) {
|
||||||
|
case SelectorOrderType.Market: {
|
||||||
|
if (this.state.position === PositionTypes.Short) {
|
||||||
|
this.props.buyStockShort(this.props.stock, shares);
|
||||||
|
} else {
|
||||||
|
this.props.buyStockLong(this.props.stock, shares);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SelectorOrderType.Limit: {
|
||||||
|
this.createPlaceOrderPopupBox(
|
||||||
|
"Place Buy Limit Order",
|
||||||
|
"Enter the price for your Limit Order",
|
||||||
|
(price: number) => {
|
||||||
|
this.props.placeOrder(this.props.stock, shares, price, OrderTypes.LimitBuy, this.state.position);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SelectorOrderType.Stop: {
|
||||||
|
this.createPlaceOrderPopupBox(
|
||||||
|
"Place Buy Stop Order",
|
||||||
|
"Enter the price for your Stop Order",
|
||||||
|
(price: number) => {
|
||||||
|
this.props.placeOrder(this.props.stock, shares, price, OrderTypes.StopBuy, this.state.position);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleBuyMaxButtonClick() {
|
||||||
|
const playerMoney: number = this.props.p.money.toNumber();
|
||||||
|
|
||||||
|
const stock = this.props.stock;
|
||||||
|
let maxShares = Math.floor((playerMoney - CONSTANTS.StockMarketCommission) / this.props.stock.price);
|
||||||
|
maxShares = Math.min(maxShares, Math.round(stock.maxShares - stock.playerShares - stock.playerShortShares));
|
||||||
|
|
||||||
|
switch (this.state.orderType) {
|
||||||
|
case SelectorOrderType.Market: {
|
||||||
|
if (this.state.position === PositionTypes.Short) {
|
||||||
|
this.props.buyStockShort(stock, maxShares);
|
||||||
|
} else {
|
||||||
|
this.props.buyStockLong(stock, maxShares);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SelectorOrderType.Limit: {
|
||||||
|
this.createPlaceOrderPopupBox(
|
||||||
|
"Place Buy Limit Order",
|
||||||
|
"Enter the price for your Limit Order",
|
||||||
|
(price: number) => {
|
||||||
|
this.props.placeOrder(stock, maxShares, price, OrderTypes.LimitBuy, this.state.position);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SelectorOrderType.Stop: {
|
||||||
|
this.createPlaceOrderPopupBox(
|
||||||
|
"Place Buy Stop Order",
|
||||||
|
"Enter the price for your Stop Order",
|
||||||
|
(price: number) => {
|
||||||
|
this.props.placeOrder(stock, maxShares, price, OrderTypes.StopBuy, this.state.position);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleOrderTypeChange(e: React.ChangeEvent<HTMLSelectElement>) {
|
||||||
|
const val = e.target.value;
|
||||||
|
|
||||||
|
// The select value returns a string. Afaik TypeScript doesnt make it easy
|
||||||
|
// to convert that string back to an enum type so we'll just do this for now
|
||||||
|
switch (val) {
|
||||||
|
case SelectorOrderType.Limit:
|
||||||
|
this.setState({
|
||||||
|
orderType: SelectorOrderType.Limit,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case SelectorOrderType.Stop:
|
||||||
|
this.setState({
|
||||||
|
orderType: SelectorOrderType.Stop,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case SelectorOrderType.Market:
|
||||||
|
default:
|
||||||
|
this.setState({
|
||||||
|
orderType: SelectorOrderType.Market,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePositionTypeChange(e: React.ChangeEvent<HTMLSelectElement>) {
|
||||||
|
const val = e.target.value;
|
||||||
|
|
||||||
|
if (val === "Short") {
|
||||||
|
this.setState({
|
||||||
|
position: PositionTypes.Short,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
position: PositionTypes.Long,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
handleQuantityChange(e: React.ChangeEvent<HTMLInputElement>) {
|
||||||
|
this.setState({
|
||||||
|
qty: e.target.value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSellButtonClick() {
|
||||||
|
const shares = parseInt(this.state.qty);
|
||||||
|
if (isNaN(shares)) {
|
||||||
|
dialogBoxCreate(`Invalid input for quantity (number of shares): ${this.state.qty}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (this.state.orderType) {
|
||||||
|
case SelectorOrderType.Market: {
|
||||||
|
if (this.state.position === PositionTypes.Short) {
|
||||||
|
this.props.sellStockShort(this.props.stock, shares);
|
||||||
|
} else {
|
||||||
|
this.props.sellStockLong(this.props.stock, shares);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SelectorOrderType.Limit: {
|
||||||
|
this.createPlaceOrderPopupBox(
|
||||||
|
"Place Sell Limit Order",
|
||||||
|
"Enter the price for your Limit Order",
|
||||||
|
(price: number) => {
|
||||||
|
this.props.placeOrder(this.props.stock, shares, price, OrderTypes.LimitSell, this.state.position);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SelectorOrderType.Stop: {
|
||||||
|
this.createPlaceOrderPopupBox(
|
||||||
|
"Place Sell Stop Order",
|
||||||
|
"Enter the price for your Stop Order",
|
||||||
|
(price: number) => {
|
||||||
|
this.props.placeOrder(this.props.stock, shares, price, OrderTypes.StopSell, this.state.position);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSellAllButtonClick() {
|
||||||
|
const stock = this.props.stock;
|
||||||
|
|
||||||
|
switch (this.state.orderType) {
|
||||||
|
case SelectorOrderType.Market: {
|
||||||
|
if (this.state.position === PositionTypes.Short) {
|
||||||
|
this.props.sellStockShort(stock, stock.playerShortShares);
|
||||||
|
} else {
|
||||||
|
this.props.sellStockLong(stock, stock.playerShares);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
dialogBoxCreate(`ERROR: 'Sell All' only works for Market Orders`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whether the player has access to orders besides market orders (limit/stop)
|
||||||
|
hasOrderAccess(): boolean {
|
||||||
|
return (this.props.p.bitNodeN === 8 || (SourceFileFlags[8] >= 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whether the player has access to shorting stocks
|
||||||
|
hasShortAccess(): boolean {
|
||||||
|
return (this.props.p.bitNodeN === 8 || (SourceFileFlags[8] >= 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<li>
|
||||||
|
<button className={"accordion-header"}>
|
||||||
|
<StockTickerHeaderText p={this.props.p} stock={this.props.stock} />
|
||||||
|
</button>
|
||||||
|
<div className={"accordion-panel"}>
|
||||||
|
<input
|
||||||
|
className={"stock-market-input"}
|
||||||
|
onChange={this.handleQuantityChange}
|
||||||
|
placeholder={"Quantity (Shares)"}
|
||||||
|
value={this.state.qty}
|
||||||
|
/>
|
||||||
|
<select className={"stock-market-input dropdown"} onChange={this.handlePositionTypeChange} value={this.state.position}>
|
||||||
|
<option value={"Long"}>Long</option>
|
||||||
|
{
|
||||||
|
this.hasShortAccess() &&
|
||||||
|
<option value={"Short"}>Short</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
<select className={"stock-market-input dropdown"} onChange={this.handleOrderTypeChange} value={this.state.orderType}>
|
||||||
|
<option value={SelectorOrderType.Market}>{SelectorOrderType.Market}</option>
|
||||||
|
{
|
||||||
|
this.hasOrderAccess() &&
|
||||||
|
<option value={SelectorOrderType.Limit}>{SelectorOrderType.Limit}</option>
|
||||||
|
}
|
||||||
|
{
|
||||||
|
this.hasOrderAccess() &&
|
||||||
|
<option value={SelectorOrderType.Stop}>{SelectorOrderType.Stop}</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<StockTickerTxButton onClick={this.handleBuyButtonClick} text={"Buy"} />
|
||||||
|
<StockTickerTxButton onClick={this.handleSellButtonClick} text={"Sell"} />
|
||||||
|
<StockTickerTxButton onClick={this.handleBuyMaxButtonClick} text={"Buy MAX"} />
|
||||||
|
<StockTickerTxButton onClick={this.handleSellAllButtonClick} text={"Sell ALL"} />
|
||||||
|
<StockTickerPositionText p={this.props.p} stock={this.props.stock} />
|
||||||
|
<StockTickerOrderList
|
||||||
|
cancelOrder={this.props.cancelOrder}
|
||||||
|
orders={this.props.orders}
|
||||||
|
p={this.props.p}
|
||||||
|
stock={this.props.stock}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
41
src/StockMarket/ui/StockTickerHeaderText.tsx
Normal file
41
src/StockMarket/ui/StockTickerHeaderText.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* React Component for the text on a stock ticker's header. This text displays
|
||||||
|
* general information on the stock such as the name, symbol, price, and
|
||||||
|
* 4S Market Data
|
||||||
|
*/
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { Stock } from "../Stock";
|
||||||
|
import { TickerHeaderFormatData } from "../data/TickerHeaderFormatData";
|
||||||
|
|
||||||
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
p: IPlayer;
|
||||||
|
stock: Stock;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function StockTickerHeaderText(props: IProps): React.ReactElement {
|
||||||
|
const stock = props.stock;
|
||||||
|
const p = props.p;
|
||||||
|
|
||||||
|
const stockPriceFormat = numeralWrapper.formatMoney(stock.price);
|
||||||
|
|
||||||
|
let hdrText = `${stock.name}${" ".repeat(1 + TickerHeaderFormatData.longestName - stock.name.length + (TickerHeaderFormatData.longestSymbol - stock.symbol.length))}${stock.symbol} -${" ".repeat(10 - stockPriceFormat.length)}${stockPriceFormat}`;
|
||||||
|
if (props.p.has4SData) {
|
||||||
|
hdrText += ` - Volatility: ${numeralWrapper.format(stock.mv, '0,0.00')}% - Price Forecast: `;
|
||||||
|
hdrText += (stock.b ? "+" : "-").repeat(Math.floor(stock.otlkMag / 10) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let styleMarkup = {
|
||||||
|
color: "#66ff33"
|
||||||
|
};
|
||||||
|
if (stock.lastPrice === stock.price) {
|
||||||
|
styleMarkup.color = "white";
|
||||||
|
} else if (stock.lastPrice > stock.price) {
|
||||||
|
styleMarkup.color = "red";
|
||||||
|
}
|
||||||
|
|
||||||
|
return <pre style={styleMarkup}>{hdrText}</pre>;
|
||||||
|
}
|
42
src/StockMarket/ui/StockTickerOrder.tsx
Normal file
42
src/StockMarket/ui/StockTickerOrder.tsx
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* React component for displaying a single order in a stock's order book
|
||||||
|
*/
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { Order } from "../Order";
|
||||||
|
import { PositionTypes } from "../data/PositionTypes";
|
||||||
|
|
||||||
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
cancelOrder: (params: object) => void;
|
||||||
|
order: Order;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StockTickerOrder extends React.Component<IProps, any> {
|
||||||
|
constructor(props: IProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.handleCancelOrderClick = this.handleCancelOrderClick.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCancelOrderClick() {
|
||||||
|
this.props.cancelOrder({ order: this.props.order });
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const order = this.props.order;
|
||||||
|
|
||||||
|
const posTxt = order.pos === PositionTypes.Long ? "Long Position" : "Short Position";
|
||||||
|
const txt = `${order.type} - ${posTxt} - ${order.shares} @ ${numeralWrapper.formatMoney(order.price)}`
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li>
|
||||||
|
{txt}
|
||||||
|
<button className={"std-button stock-market-order-cancel-btn"} onClick={this.handleCancelOrderClick}>
|
||||||
|
Cancel Order
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
33
src/StockMarket/ui/StockTickerOrderList.tsx
Normal file
33
src/StockMarket/ui/StockTickerOrderList.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* React component for displaying a stock's order list in the Stock Market UI.
|
||||||
|
* This component resides in the stock ticker
|
||||||
|
*/
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { StockTickerOrder } from "./StockTickerOrder";
|
||||||
|
|
||||||
|
import { Order } from "../Order";
|
||||||
|
import { Stock } from "../Stock";
|
||||||
|
|
||||||
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
cancelOrder: (params: object) => void;
|
||||||
|
orders: Order[];
|
||||||
|
p: IPlayer;
|
||||||
|
stock: Stock;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StockTickerOrderList extends React.Component<IProps, any> {
|
||||||
|
render() {
|
||||||
|
const orders: React.ReactElement[] = [];
|
||||||
|
for (let i = 0; i < this.props.orders.length; ++i) {
|
||||||
|
const o = this.props.orders[i];
|
||||||
|
orders.push(<StockTickerOrder cancelOrder={this.props.cancelOrder} order={o} key={i} />);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ul className={"stock-market-order-list"}>{orders}</ul>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
117
src/StockMarket/ui/StockTickerPositionText.tsx
Normal file
117
src/StockMarket/ui/StockTickerPositionText.tsx
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
/**
|
||||||
|
* React Component for the text on a stock ticker that display's information
|
||||||
|
* about the player's position in that stock
|
||||||
|
*/
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { Stock } from "../Stock";
|
||||||
|
|
||||||
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
|
import { SourceFileFlags } from "../../SourceFile/SourceFileFlags";
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
p: IPlayer;
|
||||||
|
stock: Stock;
|
||||||
|
}
|
||||||
|
|
||||||
|
const blockStyleMarkup = {
|
||||||
|
display: "block",
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StockTickerPositionText extends React.Component<IProps, any> {
|
||||||
|
renderLongPosition(): React.ReactElement {
|
||||||
|
const stock = this.props.stock;
|
||||||
|
|
||||||
|
// Caculate total returns
|
||||||
|
const totalCost = stock.playerShares * stock.playerAvgPx;
|
||||||
|
const gains = (stock.price - stock.playerAvgPx) * stock.playerShares;
|
||||||
|
let percentageGains = gains / totalCost;
|
||||||
|
if (isNaN(percentageGains)) { percentageGains = 0; }
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h3 className={"tooltip"}>
|
||||||
|
Short Position:
|
||||||
|
<span className={"tooltiptext"}>
|
||||||
|
Shares in the long position will increase in value if the price
|
||||||
|
of the corresponding stock increases
|
||||||
|
</span>
|
||||||
|
</h3>
|
||||||
|
<p>
|
||||||
|
Shares: {numeralWrapper.format(stock.playerShares, "0,0")}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Average Price: {numeralWrapper.formatMoney(stock.playerAvgPx)}
|
||||||
|
(Total Cost: {numeralWrapper.formatMoney(totalCost)})
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Profit: {numeralWrapper.formatMoney(gains)}
|
||||||
|
({numeralWrapper.formatPercentage(percentageGains)})
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
renderShortPosition(): React.ReactElement | null {
|
||||||
|
const stock = this.props.stock;
|
||||||
|
|
||||||
|
// Caculate total returns
|
||||||
|
const totalCost = stock.playerShortShares * stock.playerAvgShortPx;
|
||||||
|
const gains = (stock.playerAvgShortPx - stock.price) * stock.playerShortShares;
|
||||||
|
let percentageGains = gains / totalCost;
|
||||||
|
if (isNaN(percentageGains)) { percentageGains = 0; }
|
||||||
|
|
||||||
|
if (this.props.p.bitNodeN === 8 || (SourceFileFlags[8] >= 2)) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h3 className={"tooltip"}>
|
||||||
|
Short Position:
|
||||||
|
<span className={"tooltiptext"}>
|
||||||
|
Shares in the short position will increase in value if the
|
||||||
|
price of the corresponding stock decreases
|
||||||
|
</span>
|
||||||
|
</h3>
|
||||||
|
<p>
|
||||||
|
Shares: {numeralWrapper.format(stock.playerShortShares, "0,0")}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Average Price: {numeralWrapper.formatMoney(stock.playerAvgShortPx)}
|
||||||
|
(Total Cost: {numeralWrapper.formatMoney(totalCost)})
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Profit: {numeralWrapper.formatMoney(gains)}
|
||||||
|
({numeralWrapper.formatPercentage(percentageGains)})
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const stock = this.props.stock;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={"stock-market-position-text"}>
|
||||||
|
<p style={blockStyleMarkup}>
|
||||||
|
Max Shares: ${numeralWrapper.formatMoney(stock.maxShares)}
|
||||||
|
</p>
|
||||||
|
<p className={"tooltip"} >
|
||||||
|
Ask Price: {numeralWrapper.formatMoney(stock.getAskPrice())}
|
||||||
|
<span className={"tooltiptext"}>
|
||||||
|
See Investopedia for details on what this is
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p className={"tooltip"} >
|
||||||
|
<span className={"tooltiptext"}>
|
||||||
|
See Investopedia for details on what this is
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
{this.renderLongPosition()}
|
||||||
|
{this.renderShortPosition()}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
18
src/StockMarket/ui/StockTickerTxButton.tsx
Normal file
18
src/StockMarket/ui/StockTickerTxButton.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* React Component for a button that initiates a transaction on the Stock Market UI
|
||||||
|
* (Buy, Sell, Buy Max, etc.)
|
||||||
|
*/
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
onClick: () => void;
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function StockTickerTxButton(props: IProps): React.ReactElement {
|
||||||
|
return (
|
||||||
|
<button className={"stock-market-input std-button"} onClick={props.onClick}>
|
||||||
|
{props.text}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
129
src/StockMarket/ui/StockTickers.tsx
Normal file
129
src/StockMarket/ui/StockTickers.tsx
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
/**
|
||||||
|
* React Component for the Stock Market UI. This is the container for all
|
||||||
|
* of the stock tickers. It also contains the configuration for the
|
||||||
|
* stock ticker UI (watchlist filter, portfolio vs all mode, etc.)
|
||||||
|
*/
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { StockTicker } from "./StockTicker";
|
||||||
|
import { StockTickersConfig, TickerDisplayMode } from "./StockTickersConfig";
|
||||||
|
|
||||||
|
import { IStockMarket } from "../IStockMarket";
|
||||||
|
import { Stock } from "../Stock";
|
||||||
|
import { OrderTypes } from "../data/OrderTypes";
|
||||||
|
import { PositionTypes } from "../data/PositionTypes";
|
||||||
|
|
||||||
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
|
|
||||||
|
export type txFn = (stock: Stock, shares: number) => boolean;
|
||||||
|
export type placeOrderFn = (stock: Stock, shares: number, price: number, ordType: OrderTypes, posType: PositionTypes) => boolean;
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
buyStockLong: txFn;
|
||||||
|
buyStockShort: txFn;
|
||||||
|
cancelOrder: (params: object) => void;
|
||||||
|
p: IPlayer;
|
||||||
|
placeOrder: placeOrderFn;
|
||||||
|
sellStockLong: txFn;
|
||||||
|
sellStockShort: txFn;
|
||||||
|
stockMarket: IStockMarket;
|
||||||
|
}
|
||||||
|
|
||||||
|
type IState = {
|
||||||
|
rerenderFlag: boolean;
|
||||||
|
tickerDisplayMode: TickerDisplayMode;
|
||||||
|
watchlistFilter: string;
|
||||||
|
watchlistSymbols: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StockTickers extends React.Component<IProps, IState> {
|
||||||
|
constructor(props: IProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
rerenderFlag: false,
|
||||||
|
tickerDisplayMode: TickerDisplayMode.AllStocks,
|
||||||
|
watchlistFilter: "",
|
||||||
|
watchlistSymbols: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
this.changeDisplayMode = this.changeDisplayMode.bind(this);
|
||||||
|
this.changeWatchlistFilter = this.changeWatchlistFilter.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
changeDisplayMode() {
|
||||||
|
if (this.state.tickerDisplayMode === TickerDisplayMode.AllStocks) {
|
||||||
|
this.setState({
|
||||||
|
tickerDisplayMode: TickerDisplayMode.Portfolio,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
tickerDisplayMode: TickerDisplayMode.AllStocks,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
changeWatchlistFilter(e: React.ChangeEvent<HTMLInputElement>) {
|
||||||
|
const watchlist = e.target.value;
|
||||||
|
const sanitizedWatchlist = watchlist.replace(/\s/g, '');
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
watchlistFilter: watchlist,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (sanitizedWatchlist !== "") {
|
||||||
|
this.setState({
|
||||||
|
watchlistSymbols: sanitizedWatchlist.split(","),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rerender() {
|
||||||
|
this.setState((prevState) => {
|
||||||
|
return {
|
||||||
|
rerenderFlag: !prevState.rerenderFlag,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const tickers: React.ReactElement[] = [];
|
||||||
|
for (const stockMarketProp in this.props.stockMarket) {
|
||||||
|
const val = this.props.stockMarket[stockMarketProp];
|
||||||
|
if (val instanceof Stock) {
|
||||||
|
let orders = this.props.stockMarket.Orders[val.symbol];
|
||||||
|
if (orders == null) {
|
||||||
|
orders = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
tickers.push(
|
||||||
|
<StockTicker
|
||||||
|
buyStockLong={this.props.buyStockLong}
|
||||||
|
buyStockShort={this.props.buyStockShort}
|
||||||
|
cancelOrder={this.props.cancelOrder}
|
||||||
|
orders={orders}
|
||||||
|
p={this.props.p}
|
||||||
|
placeOrder={this.props.placeOrder}
|
||||||
|
sellStockLong={this.props.sellStockLong}
|
||||||
|
sellStockShort={this.props.sellStockShort}
|
||||||
|
stock={val}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<StockTickersConfig
|
||||||
|
changeDisplayMode={this.changeDisplayMode}
|
||||||
|
changeWatchlistFilter={this.changeWatchlistFilter}
|
||||||
|
tickerDisplayMode={this.state.tickerDisplayMode}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ul id="stock-market-list">
|
||||||
|
{tickers}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
61
src/StockMarket/ui/StockTickersConfig.tsx
Normal file
61
src/StockMarket/ui/StockTickersConfig.tsx
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* React component for the tickers configuration section of the Stock Market UI.
|
||||||
|
* This config lets you change the way stock tickers are displayed (watchlist,
|
||||||
|
* all/portoflio mode, etc)
|
||||||
|
*/
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { StdButton } from "../../ui/React/StdButton";
|
||||||
|
|
||||||
|
export enum TickerDisplayMode {
|
||||||
|
AllStocks,
|
||||||
|
Portfolio,
|
||||||
|
}
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
changeDisplayMode: () => void;
|
||||||
|
changeWatchlistFilter: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||||
|
tickerDisplayMode: TickerDisplayMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StockTickersConfig extends React.Component<IProps, any> {
|
||||||
|
constructor(props: IProps) {
|
||||||
|
super(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderDisplayModeButton() {
|
||||||
|
let txt: string = "";
|
||||||
|
let tooltip: string = "";
|
||||||
|
if (this.props.tickerDisplayMode === TickerDisplayMode.Portfolio) {
|
||||||
|
txt = "Switch to 'All Stocks' Mode";
|
||||||
|
tooltip = "Displays all stocks on the WSE";
|
||||||
|
} else {
|
||||||
|
txt = "Switch to 'Portfolio' Mode";
|
||||||
|
tooltip = "Displays only the stocks for which you have shares or orders";
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StdButton
|
||||||
|
onClick={this.props.changeDisplayMode}
|
||||||
|
text={txt}
|
||||||
|
tooltip={tooltip}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{this.renderDisplayModeButton()}
|
||||||
|
|
||||||
|
<input
|
||||||
|
className="text-input"
|
||||||
|
id="stock-market-watchlist-filter"
|
||||||
|
onChange={this.props.changeWatchlistFilter}
|
||||||
|
placeholder="Filter Stocks by symbol (comma-separated list)"
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -290,53 +290,7 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="stock-market-container" class="generic-menupage-container">
|
<div id="stock-market-container" class="generic-menupage-container">
|
||||||
<p>
|
<!-- React Component -->
|
||||||
Welcome to the World Stock Exchange (WSE)! <br /><br />
|
|
||||||
|
|
||||||
To begin trading, you must first purchase an account. WSE accounts will persist
|
|
||||||
after you 'reset' by installing Augmentations.
|
|
||||||
</p>
|
|
||||||
<a id="stock-market-buy-account" class="a-link-button-inactive"> Buy WSE Account </a>
|
|
||||||
<a id="stock-market-investopedia" class="a-link-button">Investopedia</a>
|
|
||||||
|
|
||||||
<h2> Trade Information eXchange (TIX) API </h2>
|
|
||||||
<p>
|
|
||||||
TIX, short for Trade Information eXchange, is the communications protocol supported by the WSE.
|
|
||||||
Purchasing access to the TIX API lets you write code to create your own algorithmic/automated
|
|
||||||
trading strategies.
|
|
||||||
<br /><br />
|
|
||||||
If you purchase access to the TIX API, you will retain that access even after
|
|
||||||
you 'reset' by installing Augmentations.
|
|
||||||
</p>
|
|
||||||
<a id="stock-market-buy-tix-api" class="a-link-button-inactive">Buy Trade Information eXchange (TIX) API Access</a>
|
|
||||||
|
|
||||||
<h2> Four Sigma (4S) Market Data Feed </h2>
|
|
||||||
<p>
|
|
||||||
Four Sigma's (4S) Market Data Feed provides information about stocks
|
|
||||||
that will help your trading strategies.
|
|
||||||
<br /><br />
|
|
||||||
If you purchase access to 4S Market Data and/or the 4S TIX API, you will
|
|
||||||
retain that access even after you 'reset' by installing Augmentations.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<a id="stock-market-buy-4s-data" class="a-link-button-inactive tooltip">
|
|
||||||
Buy 4S Market Data Feed
|
|
||||||
</a>
|
|
||||||
<div class="help-tip-big" id="stock-market-4s-data-help-tip">?</div>
|
|
||||||
|
|
||||||
<a id="stock-market-buy-4s-tix-api" class="a-link-button-inactive tooltip">
|
|
||||||
Buy 4S Market Data TIX API Access
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<p id="stock-market-commission"> </p>
|
|
||||||
<a id="stock-market-mode" class="a-link-button tooltip"></a>
|
|
||||||
<a id="stock-market-expand-tickers" class="a-link-button tooltip">Expand tickers</a>
|
|
||||||
<a id="stock-market-collapse-tickers" class="a-link-button tooltip">Collapse tickers</a>
|
|
||||||
<br /><br />
|
|
||||||
<input id="stock-market-watchlist-filter" class="text-input" type="text" placeholder="Filter Stocks by symbol (comma-separated list)"/>
|
|
||||||
<a id="stock-market-watchlist-filter-update" class="a-link-button"> Update Watchlist </a>
|
|
||||||
<ul id="stock-market-list" style="list-style:none;">
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Log Box -->
|
<!-- Log Box -->
|
||||||
|
@ -7,13 +7,36 @@ interface IStdButtonPurchasedProps {
|
|||||||
onClick?: (e: React.MouseEvent<HTMLElement>) => any;
|
onClick?: (e: React.MouseEvent<HTMLElement>) => any;
|
||||||
style?: object;
|
style?: object;
|
||||||
text: string;
|
text: string;
|
||||||
|
tooltip?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type IInnerHTMLMarkup = {
|
||||||
|
__html: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class StdButtonPurchased extends React.Component<IStdButtonPurchasedProps, any> {
|
export class StdButtonPurchased extends React.Component<IStdButtonPurchasedProps, any> {
|
||||||
render() {
|
render() {
|
||||||
|
const hasTooltip = this.props.tooltip != null && this.props.tooltip !== "";
|
||||||
|
let className = "std-button-bought";
|
||||||
|
if (hasTooltip) {
|
||||||
|
className += " tooltip";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tooltip will be set using inner HTML
|
||||||
|
let tooltipMarkup: IInnerHTMLMarkup | null;
|
||||||
|
if (hasTooltip) {
|
||||||
|
tooltipMarkup = {
|
||||||
|
__html: this.props.tooltip!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button className={"std-button-bought"} onClick={this.props.onClick} style={this.props.style}>
|
<button className={className} onClick={this.props.onClick} style={this.props.style}>
|
||||||
{this.props.text}
|
{this.props.text}
|
||||||
|
{
|
||||||
|
hasTooltip &&
|
||||||
|
<span className={"tooltiptext"} dangerouslySetInnerHTML={tooltipMarkup!}></span>
|
||||||
|
}
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user