2023-08-01 05:08:42 +02:00
|
|
|
import { Player } from "../Player";
|
2021-10-14 09:22:02 +02:00
|
|
|
import { buyStock, sellStock, shortStock, sellShort } from "../StockMarket/BuyingAndSelling";
|
2023-10-23 10:24:30 +02:00
|
|
|
import {
|
|
|
|
StockMarket,
|
|
|
|
SymbolToStockMap,
|
|
|
|
placeOrder,
|
|
|
|
cancelOrder,
|
|
|
|
initStockMarket,
|
|
|
|
StockMarketResolvers,
|
|
|
|
} from "../StockMarket/StockMarket";
|
2021-10-14 09:22:02 +02:00
|
|
|
import { getBuyTransactionCost, getSellTransactionGain } from "../StockMarket/StockMarketHelpers";
|
2023-06-12 06:34:20 +02:00
|
|
|
import { PositionType, OrderType, StockSymbol } from "@enums";
|
2022-03-19 17:58:18 +01:00
|
|
|
import {
|
|
|
|
getStockMarket4SDataCost,
|
|
|
|
getStockMarket4STixApiCost,
|
|
|
|
getStockMarketWseCost,
|
|
|
|
getStockMarketTixApiCost,
|
|
|
|
} from "../StockMarket/StockMarketCosts";
|
2021-10-14 09:22:02 +02:00
|
|
|
import { Stock } from "../StockMarket/Stock";
|
2022-12-30 02:28:53 +01:00
|
|
|
import { StockOrder, TIX } from "@nsdefs";
|
2022-08-08 19:43:41 +02:00
|
|
|
import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper";
|
|
|
|
import { helpers } from "../Netscript/NetscriptHelpers";
|
2023-08-01 05:08:42 +02:00
|
|
|
import { StockMarketConstants } from "../StockMarket/data/Constants";
|
2021-10-14 09:22:02 +02:00
|
|
|
|
2022-08-09 21:41:47 +02:00
|
|
|
export function NetscriptStockMarket(): InternalAPI<TIX> {
|
2022-10-04 12:40:10 +02:00
|
|
|
/** Checks if the player has TIX API access. Throws an error if the player does not */
|
2022-05-08 01:08:07 +02:00
|
|
|
const checkTixApiAccess = function (ctx: NetscriptContext): void {
|
2023-08-01 05:08:42 +02:00
|
|
|
if (!Player.hasWseAccount) {
|
2022-08-08 19:43:41 +02:00
|
|
|
throw helpers.makeRuntimeErrorMsg(ctx, `You don't have WSE Access! Cannot use ${ctx.function}()`);
|
2021-10-14 09:22:02 +02:00
|
|
|
}
|
2023-08-01 05:08:42 +02:00
|
|
|
if (!Player.hasTixApiAccess) {
|
2022-08-08 19:43:41 +02:00
|
|
|
throw helpers.makeRuntimeErrorMsg(ctx, `You don't have TIX API Access! Cannot use ${ctx.function}()`);
|
2021-10-14 09:22:02 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-05-08 01:08:07 +02:00
|
|
|
const getStockFromSymbol = function (ctx: NetscriptContext, symbol: string): Stock {
|
2021-10-14 09:22:02 +02:00
|
|
|
const stock = SymbolToStockMap[symbol];
|
|
|
|
if (stock == null) {
|
2022-08-08 19:43:41 +02:00
|
|
|
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid stock symbol: '${symbol}'`);
|
2021-10-14 09:22:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return stock;
|
|
|
|
};
|
2022-03-31 15:41:19 +02:00
|
|
|
|
2021-10-14 09:22:02 +02:00
|
|
|
return {
|
2024-02-10 10:10:19 +01:00
|
|
|
getConstants: () => () => structuredClone(StockMarketConstants),
|
2023-08-01 05:08:42 +02:00
|
|
|
hasWSEAccount: () => () => Player.hasWseAccount,
|
|
|
|
hasTIXAPIAccess: () => () => Player.hasTixApiAccess,
|
|
|
|
has4SData: () => () => Player.has4SData,
|
|
|
|
has4SDataTIXAPI: () => () => Player.has4SDataTixApi,
|
2022-10-12 14:49:27 +02:00
|
|
|
getSymbols: (ctx) => () => {
|
2022-05-08 01:08:07 +02:00
|
|
|
checkTixApiAccess(ctx);
|
2023-06-12 06:34:20 +02:00
|
|
|
return Object.values(StockSymbol);
|
2021-10-14 09:22:02 +02:00
|
|
|
},
|
2022-10-12 14:49:27 +02:00
|
|
|
getPrice: (ctx) => (_symbol) => {
|
|
|
|
const symbol = helpers.string(ctx, "symbol", _symbol);
|
|
|
|
checkTixApiAccess(ctx);
|
|
|
|
const stock = getStockFromSymbol(ctx, symbol);
|
2021-10-14 09:22:02 +02:00
|
|
|
|
2022-10-12 14:49:27 +02:00
|
|
|
return stock.price;
|
|
|
|
},
|
2022-11-28 16:33:28 +01:00
|
|
|
getOrganization: (ctx) => (_symbol) => {
|
|
|
|
const symbol = helpers.string(ctx, "symbol", _symbol);
|
|
|
|
checkTixApiAccess(ctx);
|
|
|
|
const stock = getStockFromSymbol(ctx, symbol);
|
|
|
|
|
|
|
|
return stock.name;
|
|
|
|
},
|
2022-10-12 14:49:27 +02:00
|
|
|
getAskPrice: (ctx) => (_symbol) => {
|
|
|
|
const symbol = helpers.string(ctx, "symbol", _symbol);
|
|
|
|
checkTixApiAccess(ctx);
|
|
|
|
const stock = getStockFromSymbol(ctx, symbol);
|
2021-10-14 09:22:02 +02:00
|
|
|
|
2022-10-12 14:49:27 +02:00
|
|
|
return stock.getAskPrice();
|
|
|
|
},
|
|
|
|
getBidPrice: (ctx) => (_symbol) => {
|
|
|
|
const symbol = helpers.string(ctx, "symbol", _symbol);
|
|
|
|
checkTixApiAccess(ctx);
|
|
|
|
const stock = getStockFromSymbol(ctx, symbol);
|
2021-10-14 09:22:02 +02:00
|
|
|
|
2022-10-12 14:49:27 +02:00
|
|
|
return stock.getBidPrice();
|
|
|
|
},
|
|
|
|
getPosition: (ctx) => (_symbol) => {
|
|
|
|
const symbol = helpers.string(ctx, "symbol", _symbol);
|
|
|
|
checkTixApiAccess(ctx);
|
|
|
|
const stock = SymbolToStockMap[symbol];
|
|
|
|
if (stock == null) {
|
|
|
|
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid stock symbol: ${symbol}`);
|
|
|
|
}
|
|
|
|
return [stock.playerShares, stock.playerAvgPx, stock.playerShortShares, stock.playerAvgShortPx];
|
|
|
|
},
|
|
|
|
getMaxShares: (ctx) => (_symbol) => {
|
|
|
|
const symbol = helpers.string(ctx, "symbol", _symbol);
|
|
|
|
checkTixApiAccess(ctx);
|
|
|
|
const stock = getStockFromSymbol(ctx, symbol);
|
2021-10-14 09:22:02 +02:00
|
|
|
|
2022-10-12 14:49:27 +02:00
|
|
|
return stock.maxShares;
|
|
|
|
},
|
|
|
|
getPurchaseCost: (ctx) => (_symbol, _shares, _posType) => {
|
|
|
|
const symbol = helpers.string(ctx, "symbol", _symbol);
|
|
|
|
let shares = helpers.number(ctx, "shares", _shares);
|
|
|
|
const posType = helpers.string(ctx, "posType", _posType);
|
|
|
|
checkTixApiAccess(ctx);
|
|
|
|
const stock = getStockFromSymbol(ctx, symbol);
|
|
|
|
shares = Math.round(shares);
|
|
|
|
|
|
|
|
let pos;
|
|
|
|
const sanitizedPosType = posType.toLowerCase();
|
|
|
|
if (sanitizedPosType.includes("l")) {
|
2023-06-12 06:34:20 +02:00
|
|
|
pos = PositionType.Long;
|
2022-10-12 14:49:27 +02:00
|
|
|
} else if (sanitizedPosType.includes("s")) {
|
2023-06-12 06:34:20 +02:00
|
|
|
pos = PositionType.Short;
|
2022-10-12 14:49:27 +02:00
|
|
|
} else {
|
|
|
|
return Infinity;
|
|
|
|
}
|
2021-10-14 09:22:02 +02:00
|
|
|
|
2022-10-12 14:49:27 +02:00
|
|
|
const res = getBuyTransactionCost(stock, shares, pos);
|
|
|
|
if (res == null) {
|
|
|
|
return Infinity;
|
|
|
|
}
|
2021-10-14 09:22:02 +02:00
|
|
|
|
2022-10-12 14:49:27 +02:00
|
|
|
return res;
|
|
|
|
},
|
|
|
|
getSaleGain: (ctx) => (_symbol, _shares, _posType) => {
|
|
|
|
const symbol = helpers.string(ctx, "symbol", _symbol);
|
|
|
|
let shares = helpers.number(ctx, "shares", _shares);
|
|
|
|
const posType = helpers.string(ctx, "posType", _posType);
|
|
|
|
checkTixApiAccess(ctx);
|
|
|
|
const stock = getStockFromSymbol(ctx, symbol);
|
|
|
|
shares = Math.round(shares);
|
|
|
|
|
|
|
|
let pos;
|
|
|
|
const sanitizedPosType = posType.toLowerCase();
|
|
|
|
if (sanitizedPosType.includes("l")) {
|
2023-06-12 06:34:20 +02:00
|
|
|
pos = PositionType.Long;
|
2022-10-12 14:49:27 +02:00
|
|
|
} else if (sanitizedPosType.includes("s")) {
|
2023-06-12 06:34:20 +02:00
|
|
|
pos = PositionType.Short;
|
2022-10-12 14:49:27 +02:00
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
const res = getSellTransactionGain(stock, shares, pos);
|
|
|
|
if (res == null) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
},
|
|
|
|
buyStock: (ctx) => (_symbol, _shares) => {
|
|
|
|
const symbol = helpers.string(ctx, "symbol", _symbol);
|
|
|
|
const shares = helpers.number(ctx, "shares", _shares);
|
|
|
|
checkTixApiAccess(ctx);
|
|
|
|
const stock = getStockFromSymbol(ctx, symbol);
|
|
|
|
const res = buyStock(stock, shares, ctx, {});
|
|
|
|
return res ? stock.getAskPrice() : 0;
|
|
|
|
},
|
|
|
|
sellStock: (ctx) => (_symbol, _shares) => {
|
|
|
|
const symbol = helpers.string(ctx, "symbol", _symbol);
|
|
|
|
const shares = helpers.number(ctx, "shares", _shares);
|
|
|
|
checkTixApiAccess(ctx);
|
|
|
|
const stock = getStockFromSymbol(ctx, symbol);
|
|
|
|
const res = sellStock(stock, shares, ctx, {});
|
|
|
|
|
|
|
|
return res ? stock.getBidPrice() : 0;
|
|
|
|
},
|
|
|
|
buyShort: (ctx) => (_symbol, _shares) => {
|
|
|
|
const symbol = helpers.string(ctx, "symbol", _symbol);
|
|
|
|
const shares = helpers.number(ctx, "shares", _shares);
|
|
|
|
checkTixApiAccess(ctx);
|
2023-08-01 05:08:42 +02:00
|
|
|
if (Player.bitNodeN !== 8) {
|
|
|
|
if (Player.sourceFileLvl(8) <= 1) {
|
2022-10-12 14:49:27 +02:00
|
|
|
throw helpers.makeRuntimeErrorMsg(
|
|
|
|
ctx,
|
|
|
|
"You must either be in BitNode-8 or you must have Source-File 8 Level 2.",
|
|
|
|
);
|
2022-05-08 01:08:07 +02:00
|
|
|
}
|
2022-10-12 14:49:27 +02:00
|
|
|
}
|
|
|
|
const stock = getStockFromSymbol(ctx, symbol);
|
|
|
|
const res = shortStock(stock, shares, ctx, {});
|
|
|
|
|
|
|
|
return res ? stock.getBidPrice() : 0;
|
|
|
|
},
|
|
|
|
sellShort: (ctx) => (_symbol, _shares) => {
|
|
|
|
const symbol = helpers.string(ctx, "symbol", _symbol);
|
|
|
|
const shares = helpers.number(ctx, "shares", _shares);
|
|
|
|
checkTixApiAccess(ctx);
|
2023-08-01 05:08:42 +02:00
|
|
|
if (Player.bitNodeN !== 8) {
|
|
|
|
if (Player.sourceFileLvl(8) <= 1) {
|
2022-08-08 19:43:41 +02:00
|
|
|
throw helpers.makeRuntimeErrorMsg(
|
|
|
|
ctx,
|
2022-10-12 14:49:27 +02:00
|
|
|
"You must either be in BitNode-8 or you must have Source-File 8 Level 2.",
|
2022-08-08 19:43:41 +02:00
|
|
|
);
|
2022-05-08 01:08:07 +02:00
|
|
|
}
|
2022-10-12 14:49:27 +02:00
|
|
|
}
|
|
|
|
const stock = getStockFromSymbol(ctx, symbol);
|
|
|
|
const res = sellShort(stock, shares, ctx, {});
|
|
|
|
|
|
|
|
return res ? stock.getAskPrice() : 0;
|
|
|
|
},
|
|
|
|
placeOrder: (ctx) => (_symbol, _shares, _price, _type, _pos) => {
|
|
|
|
const symbol = helpers.string(ctx, "symbol", _symbol);
|
|
|
|
const shares = helpers.number(ctx, "shares", _shares);
|
|
|
|
const price = helpers.number(ctx, "price", _price);
|
|
|
|
const type = helpers.string(ctx, "type", _type);
|
|
|
|
const pos = helpers.string(ctx, "pos", _pos);
|
|
|
|
checkTixApiAccess(ctx);
|
2023-08-01 05:08:42 +02:00
|
|
|
if (Player.bitNodeN !== 8) {
|
|
|
|
if (Player.sourceFileLvl(8) <= 2) {
|
2022-10-12 14:49:27 +02:00
|
|
|
throw helpers.makeRuntimeErrorMsg(
|
|
|
|
ctx,
|
|
|
|
"You must either be in BitNode-8 or you must have Source-File 8 Level 3.",
|
|
|
|
);
|
2021-10-14 09:22:02 +02:00
|
|
|
}
|
2022-10-12 14:49:27 +02:00
|
|
|
}
|
|
|
|
const stock = getStockFromSymbol(ctx, symbol);
|
|
|
|
|
|
|
|
let orderType;
|
|
|
|
let orderPos;
|
|
|
|
const ltype = type.toLowerCase();
|
|
|
|
if (ltype.includes("limit") && ltype.includes("buy")) {
|
2023-06-12 06:34:20 +02:00
|
|
|
orderType = OrderType.LimitBuy;
|
2022-10-12 14:49:27 +02:00
|
|
|
} else if (ltype.includes("limit") && ltype.includes("sell")) {
|
2023-06-12 06:34:20 +02:00
|
|
|
orderType = OrderType.LimitSell;
|
2022-10-12 14:49:27 +02:00
|
|
|
} else if (ltype.includes("stop") && ltype.includes("buy")) {
|
2023-06-12 06:34:20 +02:00
|
|
|
orderType = OrderType.StopBuy;
|
2022-10-12 14:49:27 +02:00
|
|
|
} else if (ltype.includes("stop") && ltype.includes("sell")) {
|
2023-06-12 06:34:20 +02:00
|
|
|
orderType = OrderType.StopSell;
|
2022-10-12 14:49:27 +02:00
|
|
|
} else {
|
|
|
|
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid order type: ${type}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
const lpos = pos.toLowerCase();
|
|
|
|
if (lpos.includes("l")) {
|
2023-06-12 06:34:20 +02:00
|
|
|
orderPos = PositionType.Long;
|
2022-10-12 14:49:27 +02:00
|
|
|
} else if (lpos.includes("s")) {
|
2023-06-12 06:34:20 +02:00
|
|
|
orderPos = PositionType.Short;
|
2022-10-12 14:49:27 +02:00
|
|
|
} else {
|
|
|
|
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid position type: ${pos}`);
|
|
|
|
}
|
2021-10-14 09:22:02 +02:00
|
|
|
|
2022-10-12 14:49:27 +02:00
|
|
|
return placeOrder(stock, shares, price, orderType, orderPos, ctx);
|
|
|
|
},
|
|
|
|
cancelOrder: (ctx) => (_symbol, _shares, _price, _type, _pos) => {
|
|
|
|
const symbol = helpers.string(ctx, "symbol", _symbol);
|
|
|
|
const shares = helpers.number(ctx, "shares", _shares);
|
|
|
|
const price = helpers.number(ctx, "price", _price);
|
|
|
|
const type = helpers.string(ctx, "type", _type);
|
|
|
|
const pos = helpers.string(ctx, "pos", _pos);
|
|
|
|
checkTixApiAccess(ctx);
|
2023-08-01 05:08:42 +02:00
|
|
|
if (Player.bitNodeN !== 8) {
|
|
|
|
if (Player.sourceFileLvl(8) <= 2) {
|
2022-10-12 14:49:27 +02:00
|
|
|
throw helpers.makeRuntimeErrorMsg(
|
|
|
|
ctx,
|
|
|
|
"You must either be in BitNode-8 or you must have Source-File 8 Level 3.",
|
|
|
|
);
|
2022-05-08 01:08:07 +02:00
|
|
|
}
|
2022-10-12 14:49:27 +02:00
|
|
|
}
|
|
|
|
const stock = getStockFromSymbol(ctx, symbol);
|
|
|
|
if (isNaN(shares) || isNaN(price)) {
|
|
|
|
throw helpers.makeRuntimeErrorMsg(
|
|
|
|
ctx,
|
|
|
|
`Invalid shares or price. Must be numeric. shares=${shares}, price=${price}`,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
let orderType;
|
|
|
|
let orderPos;
|
|
|
|
const ltype = type.toLowerCase();
|
|
|
|
if (ltype.includes("limit") && ltype.includes("buy")) {
|
2023-06-12 06:34:20 +02:00
|
|
|
orderType = OrderType.LimitBuy;
|
2022-10-12 14:49:27 +02:00
|
|
|
} else if (ltype.includes("limit") && ltype.includes("sell")) {
|
2023-06-12 06:34:20 +02:00
|
|
|
orderType = OrderType.LimitSell;
|
2022-10-12 14:49:27 +02:00
|
|
|
} else if (ltype.includes("stop") && ltype.includes("buy")) {
|
2023-06-12 06:34:20 +02:00
|
|
|
orderType = OrderType.StopBuy;
|
2022-10-12 14:49:27 +02:00
|
|
|
} else if (ltype.includes("stop") && ltype.includes("sell")) {
|
2023-06-12 06:34:20 +02:00
|
|
|
orderType = OrderType.StopSell;
|
2022-10-12 14:49:27 +02:00
|
|
|
} else {
|
|
|
|
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid order type: ${type}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
const lpos = pos.toLowerCase();
|
|
|
|
if (lpos.includes("l")) {
|
2023-06-12 06:34:20 +02:00
|
|
|
orderPos = PositionType.Long;
|
2022-10-12 14:49:27 +02:00
|
|
|
} else if (lpos.includes("s")) {
|
2023-06-12 06:34:20 +02:00
|
|
|
orderPos = PositionType.Short;
|
2022-10-12 14:49:27 +02:00
|
|
|
} else {
|
|
|
|
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid position type: ${pos}`);
|
|
|
|
}
|
|
|
|
const params = {
|
|
|
|
stock: stock,
|
|
|
|
shares: shares,
|
|
|
|
price: price,
|
|
|
|
type: orderType,
|
|
|
|
pos: orderPos,
|
|
|
|
};
|
|
|
|
return cancelOrder(params, ctx);
|
|
|
|
},
|
|
|
|
getOrders: (ctx) => () => {
|
2022-05-08 01:08:07 +02:00
|
|
|
checkTixApiAccess(ctx);
|
2023-08-01 05:08:42 +02:00
|
|
|
if (Player.bitNodeN !== 8) {
|
|
|
|
if (Player.sourceFileLvl(8) <= 2) {
|
2022-08-08 19:43:41 +02:00
|
|
|
throw helpers.makeRuntimeErrorMsg(ctx, "You must either be in BitNode-8 or have Source-File 8 Level 3.");
|
2021-10-14 09:22:02 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-20 01:08:22 +02:00
|
|
|
const orders: StockOrder = {};
|
2021-10-14 09:22:02 +02:00
|
|
|
|
2023-05-05 09:55:59 +02:00
|
|
|
const stockMarketOrders = StockMarket.Orders;
|
2022-01-16 01:45:03 +01:00
|
|
|
for (const symbol of Object.keys(stockMarketOrders)) {
|
2021-10-14 09:22:02 +02:00
|
|
|
const orderBook = stockMarketOrders[symbol];
|
|
|
|
if (orderBook.constructor === Array && orderBook.length > 0) {
|
|
|
|
orders[symbol] = [];
|
|
|
|
for (let i = 0; i < orderBook.length; ++i) {
|
|
|
|
orders[symbol].push({
|
|
|
|
shares: orderBook[i].shares,
|
|
|
|
price: orderBook[i].price,
|
|
|
|
type: orderBook[i].type,
|
|
|
|
position: orderBook[i].pos,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return orders;
|
|
|
|
},
|
2022-10-12 14:49:27 +02:00
|
|
|
getVolatility: (ctx) => (_symbol) => {
|
|
|
|
const symbol = helpers.string(ctx, "symbol", _symbol);
|
2023-08-01 05:08:42 +02:00
|
|
|
if (!Player.has4SDataTixApi) {
|
2022-10-12 14:49:27 +02:00
|
|
|
throw helpers.makeRuntimeErrorMsg(ctx, "You don't have 4S Market Data TIX API Access!");
|
|
|
|
}
|
|
|
|
const stock = getStockFromSymbol(ctx, symbol);
|
2021-10-14 09:22:02 +02:00
|
|
|
|
2022-10-12 14:49:27 +02:00
|
|
|
return stock.mv / 100; // Convert from percentage to decimal
|
|
|
|
},
|
|
|
|
getForecast: (ctx) => (_symbol) => {
|
|
|
|
const symbol = helpers.string(ctx, "symbol", _symbol);
|
2023-08-01 05:08:42 +02:00
|
|
|
if (!Player.has4SDataTixApi) {
|
2022-10-12 14:49:27 +02:00
|
|
|
throw helpers.makeRuntimeErrorMsg(ctx, "You don't have 4S Market Data TIX API Access!");
|
|
|
|
}
|
|
|
|
const stock = getStockFromSymbol(ctx, symbol);
|
|
|
|
|
|
|
|
let forecast = 50;
|
|
|
|
stock.b ? (forecast += stock.otlkMag) : (forecast -= stock.otlkMag);
|
|
|
|
return forecast / 100; // Convert from percentage to decimal
|
|
|
|
},
|
|
|
|
purchase4SMarketData: (ctx) => () => {
|
2023-08-01 05:08:42 +02:00
|
|
|
if (Player.has4SData) {
|
2022-08-08 21:51:50 +02:00
|
|
|
helpers.log(ctx, () => "Already purchased 4S Market Data.");
|
2021-10-14 09:22:02 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-08-01 05:08:42 +02:00
|
|
|
if (Player.money < getStockMarket4SDataCost()) {
|
2022-08-08 21:51:50 +02:00
|
|
|
helpers.log(ctx, () => "Not enough money to purchase 4S Market Data.");
|
2021-10-14 09:22:02 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-08-01 05:08:42 +02:00
|
|
|
Player.has4SData = true;
|
|
|
|
Player.loseMoney(getStockMarket4SDataCost(), "stock");
|
2022-08-08 21:51:50 +02:00
|
|
|
helpers.log(ctx, () => "Purchased 4S Market Data");
|
2021-10-14 09:22:02 +02:00
|
|
|
return true;
|
|
|
|
},
|
2022-10-12 14:49:27 +02:00
|
|
|
purchase4SMarketDataTixApi: (ctx) => () => {
|
2022-05-08 01:08:07 +02:00
|
|
|
checkTixApiAccess(ctx);
|
2021-10-14 09:22:02 +02:00
|
|
|
|
2023-08-01 05:08:42 +02:00
|
|
|
if (Player.has4SDataTixApi) {
|
2022-08-08 21:51:50 +02:00
|
|
|
helpers.log(ctx, () => "Already purchased 4S Market Data TIX API");
|
2021-10-14 09:22:02 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-08-01 05:08:42 +02:00
|
|
|
if (Player.money < getStockMarket4STixApiCost()) {
|
2022-08-08 21:51:50 +02:00
|
|
|
helpers.log(ctx, () => "Not enough money to purchase 4S Market Data TIX API");
|
2021-10-14 09:22:02 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-08-01 05:08:42 +02:00
|
|
|
Player.has4SDataTixApi = true;
|
|
|
|
Player.loseMoney(getStockMarket4STixApiCost(), "stock");
|
2022-08-08 21:51:50 +02:00
|
|
|
helpers.log(ctx, () => "Purchased 4S Market Data TIX API");
|
2021-10-14 09:22:02 +02:00
|
|
|
return true;
|
|
|
|
},
|
2022-10-12 14:49:27 +02:00
|
|
|
purchaseWseAccount: (ctx) => () => {
|
2023-08-01 05:08:42 +02:00
|
|
|
if (Player.hasWseAccount) {
|
2022-08-08 21:51:50 +02:00
|
|
|
helpers.log(ctx, () => "Already purchased WSE Account");
|
2022-03-19 04:58:18 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-08-01 05:08:42 +02:00
|
|
|
if (Player.money < getStockMarketWseCost()) {
|
2022-08-08 21:51:50 +02:00
|
|
|
helpers.log(ctx, () => "Not enough money to purchase WSE Account Access");
|
2022-03-19 04:58:18 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-08-01 05:08:42 +02:00
|
|
|
Player.hasWseAccount = true;
|
2022-10-20 06:38:19 +02:00
|
|
|
initStockMarket();
|
2023-08-01 05:08:42 +02:00
|
|
|
Player.loseMoney(getStockMarketWseCost(), "stock");
|
2022-08-08 21:51:50 +02:00
|
|
|
helpers.log(ctx, () => "Purchased WSE Account Access");
|
2022-03-19 04:58:18 +01:00
|
|
|
return true;
|
|
|
|
},
|
2022-10-12 14:49:27 +02:00
|
|
|
purchaseTixApi: (ctx) => () => {
|
2023-08-01 05:08:42 +02:00
|
|
|
if (Player.hasTixApiAccess) {
|
2022-08-08 21:51:50 +02:00
|
|
|
helpers.log(ctx, () => "Already purchased TIX API");
|
2022-03-19 04:58:18 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-08-01 05:08:42 +02:00
|
|
|
if (Player.money < getStockMarketTixApiCost()) {
|
2022-08-08 21:51:50 +02:00
|
|
|
helpers.log(ctx, () => "Not enough money to purchase TIX API Access");
|
2022-03-19 04:58:18 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-08-01 05:08:42 +02:00
|
|
|
Player.hasTixApiAccess = true;
|
|
|
|
Player.loseMoney(getStockMarketTixApiCost(), "stock");
|
2022-08-08 21:51:50 +02:00
|
|
|
helpers.log(ctx, () => "Purchased TIX API");
|
2022-03-19 04:58:18 +01:00
|
|
|
return true;
|
|
|
|
},
|
2023-10-23 10:24:30 +02:00
|
|
|
getBonusTime: (ctx) => () => {
|
|
|
|
checkTixApiAccess(ctx);
|
|
|
|
return Math.round(StockMarket.storedCycles / 5) * 1000;
|
|
|
|
},
|
|
|
|
nextUpdate: (ctx) => () => {
|
|
|
|
checkTixApiAccess(ctx);
|
|
|
|
return new Promise<number>((res) => StockMarketResolvers.push(res));
|
|
|
|
},
|
2021-10-14 09:22:02 +02:00
|
|
|
};
|
|
|
|
}
|