typefix netscriptFunctions (see desc)

* Types for InternalFunction and ExternalFunction have been modified to actually typecheck ns functions against docs.
* Internal functions are required to use unknown for any params on the inner function.
* Return types for internal function inner-function must match the respective external function.
* Added new typecheck assertion function for asserting dynamic object types, to allow unknownifying params that were previously hardcoded objec structures.
* Because type assertion for parameter types and return types is enforced by InternalAPI, removed all duplicate type declarations on NetscriptFunction params and returns.
This commit is contained in:
omuretsu
2022-10-12 08:49:27 -04:00
parent 41b6f0b87b
commit 7a384d53f4
20 changed files with 2845 additions and 3271 deletions
+246 -276
View File
@@ -37,284 +37,258 @@ export function NetscriptStockMarket(): InternalAPI<TIX> {
};
return {
hasWSEAccount: () => (): boolean => {
hasWSEAccount: () => () => {
return player.hasWseAccount;
},
hasTIXAPIAccess: () => (): boolean => {
hasTIXAPIAccess: () => () => {
return player.hasTixApiAccess;
},
has4SData: () => (): boolean => {
has4SData: () => () => {
return player.has4SData;
},
has4SDataTIXAPI: () => (): boolean => {
has4SDataTIXAPI: () => () => {
return player.has4SDataTixApi;
},
getSymbols: (ctx: NetscriptContext) => (): string[] => {
getSymbols: (ctx) => () => {
checkTixApiAccess(ctx);
return Object.values(StockSymbols);
},
getPrice:
(ctx: NetscriptContext) =>
(_symbol: unknown): number => {
const symbol = helpers.string(ctx, "symbol", _symbol);
checkTixApiAccess(ctx);
const stock = getStockFromSymbol(ctx, symbol);
getPrice: (ctx) => (_symbol) => {
const symbol = helpers.string(ctx, "symbol", _symbol);
checkTixApiAccess(ctx);
const stock = getStockFromSymbol(ctx, symbol);
return stock.price;
},
getAskPrice:
(ctx: NetscriptContext) =>
(_symbol: unknown): number => {
const symbol = helpers.string(ctx, "symbol", _symbol);
checkTixApiAccess(ctx);
const stock = getStockFromSymbol(ctx, symbol);
return stock.price;
},
getAskPrice: (ctx) => (_symbol) => {
const symbol = helpers.string(ctx, "symbol", _symbol);
checkTixApiAccess(ctx);
const stock = getStockFromSymbol(ctx, symbol);
return stock.getAskPrice();
},
getBidPrice:
(ctx: NetscriptContext) =>
(_symbol: unknown): number => {
const symbol = helpers.string(ctx, "symbol", _symbol);
checkTixApiAccess(ctx);
const stock = getStockFromSymbol(ctx, symbol);
return stock.getAskPrice();
},
getBidPrice: (ctx) => (_symbol) => {
const symbol = helpers.string(ctx, "symbol", _symbol);
checkTixApiAccess(ctx);
const stock = getStockFromSymbol(ctx, symbol);
return stock.getBidPrice();
},
getPosition:
(ctx: NetscriptContext) =>
(_symbol: unknown): [number, number, number, number] => {
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: NetscriptContext) =>
(_symbol: unknown): number => {
const symbol = helpers.string(ctx, "symbol", _symbol);
checkTixApiAccess(ctx);
const stock = getStockFromSymbol(ctx, symbol);
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);
return stock.maxShares;
},
getPurchaseCost:
(ctx: NetscriptContext) =>
(_symbol: unknown, _shares: unknown, _posType: unknown): number => {
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);
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")) {
pos = PositionTypes.Long;
} else if (sanitizedPosType.includes("s")) {
pos = PositionTypes.Short;
} else {
return Infinity;
}
let pos;
const sanitizedPosType = posType.toLowerCase();
if (sanitizedPosType.includes("l")) {
pos = PositionTypes.Long;
} else if (sanitizedPosType.includes("s")) {
pos = PositionTypes.Short;
} else {
return Infinity;
}
const res = getBuyTransactionCost(stock, shares, pos);
if (res == null) {
return Infinity;
}
const res = getBuyTransactionCost(stock, shares, pos);
if (res == null) {
return Infinity;
}
return res;
},
getSaleGain:
(ctx: NetscriptContext) =>
(_symbol: unknown, _shares: unknown, _posType: unknown): number => {
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);
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")) {
pos = PositionTypes.Long;
} else if (sanitizedPosType.includes("s")) {
pos = PositionTypes.Short;
} else {
return 0;
}
let pos;
const sanitizedPosType = posType.toLowerCase();
if (sanitizedPosType.includes("l")) {
pos = PositionTypes.Long;
} else if (sanitizedPosType.includes("s")) {
pos = PositionTypes.Short;
} else {
return 0;
}
const res = getSellTransactionGain(stock, shares, pos);
if (res == null) {
return 0;
}
const res = getSellTransactionGain(stock, shares, pos);
if (res == null) {
return 0;
}
return res;
},
buyStock:
(ctx: NetscriptContext) =>
(_symbol: unknown, _shares: unknown): number => {
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: NetscriptContext) =>
(_symbol: unknown, _shares: unknown): number => {
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;
},
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: NetscriptContext) =>
(_symbol: unknown, _shares: unknown): number => {
const symbol = helpers.string(ctx, "symbol", _symbol);
const shares = helpers.number(ctx, "shares", _shares);
checkTixApiAccess(ctx);
if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 1) {
throw helpers.makeRuntimeErrorMsg(
ctx,
"You must either be in BitNode-8 or you must have Source-File 8 Level 2.",
);
}
}
const stock = getStockFromSymbol(ctx, symbol);
const res = shortStock(stock, shares, ctx, {});
return res ? stock.getBidPrice() : 0;
},
sellShort:
(ctx: NetscriptContext) =>
(_symbol: unknown, _shares: unknown): number => {
const symbol = helpers.string(ctx, "symbol", _symbol);
const shares = helpers.number(ctx, "shares", _shares);
checkTixApiAccess(ctx);
if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 1) {
throw helpers.makeRuntimeErrorMsg(
ctx,
"You must either be in BitNode-8 or you must have Source-File 8 Level 2.",
);
}
}
const stock = getStockFromSymbol(ctx, symbol);
const res = sellShort(stock, shares, ctx, {});
return res ? stock.getAskPrice() : 0;
},
placeOrder:
(ctx: NetscriptContext) =>
(_symbol: unknown, _shares: unknown, _price: unknown, _type: unknown, _pos: unknown): boolean => {
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);
if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 2) {
throw helpers.makeRuntimeErrorMsg(
ctx,
"You must either be in BitNode-8 or you must have Source-File 8 Level 3.",
);
}
}
const stock = getStockFromSymbol(ctx, symbol);
let orderType;
let orderPos;
const ltype = type.toLowerCase();
if (ltype.includes("limit") && ltype.includes("buy")) {
orderType = OrderTypes.LimitBuy;
} else if (ltype.includes("limit") && ltype.includes("sell")) {
orderType = OrderTypes.LimitSell;
} else if (ltype.includes("stop") && ltype.includes("buy")) {
orderType = OrderTypes.StopBuy;
} else if (ltype.includes("stop") && ltype.includes("sell")) {
orderType = OrderTypes.StopSell;
} else {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid order type: ${type}`);
}
const lpos = pos.toLowerCase();
if (lpos.includes("l")) {
orderPos = PositionTypes.Long;
} else if (lpos.includes("s")) {
orderPos = PositionTypes.Short;
} else {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid position type: ${pos}`);
}
return placeOrder(stock, shares, price, orderType, orderPos, ctx);
},
cancelOrder:
(ctx: NetscriptContext) =>
(_symbol: unknown, _shares: unknown, _price: unknown, _type: unknown, _pos: unknown): boolean => {
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);
if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 2) {
throw helpers.makeRuntimeErrorMsg(
ctx,
"You must either be in BitNode-8 or you must have Source-File 8 Level 3.",
);
}
}
const stock = getStockFromSymbol(ctx, symbol);
if (isNaN(shares) || isNaN(price)) {
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);
if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 1) {
throw helpers.makeRuntimeErrorMsg(
ctx,
`Invalid shares or price. Must be numeric. shares=${shares}, price=${price}`,
"You must either be in BitNode-8 or you must have Source-File 8 Level 2.",
);
}
let orderType;
let orderPos;
const ltype = type.toLowerCase();
if (ltype.includes("limit") && ltype.includes("buy")) {
orderType = OrderTypes.LimitBuy;
} else if (ltype.includes("limit") && ltype.includes("sell")) {
orderType = OrderTypes.LimitSell;
} else if (ltype.includes("stop") && ltype.includes("buy")) {
orderType = OrderTypes.StopBuy;
} else if (ltype.includes("stop") && ltype.includes("sell")) {
orderType = OrderTypes.StopSell;
} else {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid order type: ${type}`);
}
}
const stock = getStockFromSymbol(ctx, symbol);
const res = shortStock(stock, shares, ctx, {});
const lpos = pos.toLowerCase();
if (lpos.includes("l")) {
orderPos = PositionTypes.Long;
} else if (lpos.includes("s")) {
orderPos = PositionTypes.Short;
} else {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid position type: ${pos}`);
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);
if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 1) {
throw helpers.makeRuntimeErrorMsg(
ctx,
"You must either be in BitNode-8 or you must have Source-File 8 Level 2.",
);
}
const params = {
stock: stock,
shares: shares,
price: price,
type: orderType,
pos: orderPos,
};
return cancelOrder(params, ctx);
},
getOrders: (ctx: NetscriptContext) => (): StockOrder => {
}
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);
if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 2) {
throw helpers.makeRuntimeErrorMsg(
ctx,
"You must either be in BitNode-8 or you must have Source-File 8 Level 3.",
);
}
}
const stock = getStockFromSymbol(ctx, symbol);
let orderType;
let orderPos;
const ltype = type.toLowerCase();
if (ltype.includes("limit") && ltype.includes("buy")) {
orderType = OrderTypes.LimitBuy;
} else if (ltype.includes("limit") && ltype.includes("sell")) {
orderType = OrderTypes.LimitSell;
} else if (ltype.includes("stop") && ltype.includes("buy")) {
orderType = OrderTypes.StopBuy;
} else if (ltype.includes("stop") && ltype.includes("sell")) {
orderType = OrderTypes.StopSell;
} else {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid order type: ${type}`);
}
const lpos = pos.toLowerCase();
if (lpos.includes("l")) {
orderPos = PositionTypes.Long;
} else if (lpos.includes("s")) {
orderPos = PositionTypes.Short;
} else {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid position type: ${pos}`);
}
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);
if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 2) {
throw helpers.makeRuntimeErrorMsg(
ctx,
"You must either be in BitNode-8 or you must have Source-File 8 Level 3.",
);
}
}
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")) {
orderType = OrderTypes.LimitBuy;
} else if (ltype.includes("limit") && ltype.includes("sell")) {
orderType = OrderTypes.LimitSell;
} else if (ltype.includes("stop") && ltype.includes("buy")) {
orderType = OrderTypes.StopBuy;
} else if (ltype.includes("stop") && ltype.includes("sell")) {
orderType = OrderTypes.StopSell;
} else {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid order type: ${type}`);
}
const lpos = pos.toLowerCase();
if (lpos.includes("l")) {
orderPos = PositionTypes.Long;
} else if (lpos.includes("s")) {
orderPos = PositionTypes.Short;
} 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) => () => {
checkTixApiAccess(ctx);
if (player.bitNodeN !== 8) {
if (player.sourceFileLvl(8) <= 2) {
@@ -342,31 +316,27 @@ export function NetscriptStockMarket(): InternalAPI<TIX> {
return orders;
},
getVolatility:
(ctx: NetscriptContext) =>
(_symbol: unknown): number => {
const symbol = helpers.string(ctx, "symbol", _symbol);
if (!player.has4SDataTixApi) {
throw helpers.makeRuntimeErrorMsg(ctx, "You don't have 4S Market Data TIX API Access!");
}
const stock = getStockFromSymbol(ctx, symbol);
getVolatility: (ctx) => (_symbol) => {
const symbol = helpers.string(ctx, "symbol", _symbol);
if (!player.has4SDataTixApi) {
throw helpers.makeRuntimeErrorMsg(ctx, "You don't have 4S Market Data TIX API Access!");
}
const stock = getStockFromSymbol(ctx, symbol);
return stock.mv / 100; // Convert from percentage to decimal
},
getForecast:
(ctx: NetscriptContext) =>
(_symbol: unknown): number => {
const symbol = helpers.string(ctx, "symbol", _symbol);
if (!player.has4SDataTixApi) {
throw helpers.makeRuntimeErrorMsg(ctx, "You don't have 4S Market Data TIX API Access!");
}
const stock = getStockFromSymbol(ctx, symbol);
return stock.mv / 100; // Convert from percentage to decimal
},
getForecast: (ctx) => (_symbol) => {
const symbol = helpers.string(ctx, "symbol", _symbol);
if (!player.has4SDataTixApi) {
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: NetscriptContext) => (): boolean => {
let forecast = 50;
stock.b ? (forecast += stock.otlkMag) : (forecast -= stock.otlkMag);
return forecast / 100; // Convert from percentage to decimal
},
purchase4SMarketData: (ctx) => () => {
if (player.has4SData) {
helpers.log(ctx, () => "Already purchased 4S Market Data.");
return true;
@@ -382,7 +352,7 @@ export function NetscriptStockMarket(): InternalAPI<TIX> {
helpers.log(ctx, () => "Purchased 4S Market Data");
return true;
},
purchase4SMarketDataTixApi: (ctx: NetscriptContext) => (): boolean => {
purchase4SMarketDataTixApi: (ctx) => () => {
checkTixApiAccess(ctx);
if (player.has4SDataTixApi) {
@@ -400,7 +370,7 @@ export function NetscriptStockMarket(): InternalAPI<TIX> {
helpers.log(ctx, () => "Purchased 4S Market Data TIX API");
return true;
},
purchaseWseAccount: (ctx: NetscriptContext) => (): boolean => {
purchaseWseAccount: (ctx) => () => {
if (player.hasWseAccount) {
helpers.log(ctx, () => "Already purchased WSE Account");
return true;
@@ -417,7 +387,7 @@ export function NetscriptStockMarket(): InternalAPI<TIX> {
helpers.log(ctx, () => "Purchased WSE Account Access");
return true;
},
purchaseTixApi: (ctx: NetscriptContext) => (): boolean => {
purchaseTixApi: (ctx) => () => {
if (player.hasTixApiAccess) {
helpers.log(ctx, () => "Already purchased TIX API");
return true;