mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-19 04:35:46 +01:00
Removed stock market price movement. Now only forecast is influenced by big transactions
This commit is contained in:
parent
6effda29a9
commit
c485fdfa87
@ -22,8 +22,6 @@ access even after you 'reset' by installing Augmentations
|
|||||||
getStockBidPrice() <tixapi/getStockBidPrice>
|
getStockBidPrice() <tixapi/getStockBidPrice>
|
||||||
getStockPosition() <tixapi/getStockPosition>
|
getStockPosition() <tixapi/getStockPosition>
|
||||||
getStockMaxShares() <tixapi/getStockMaxShares>
|
getStockMaxShares() <tixapi/getStockMaxShares>
|
||||||
getStockPurchaseCost() <tixapi/getStockPurchaseCost>
|
|
||||||
getStockSaleGain() <tixapi/getStockSaleGain>
|
|
||||||
buyStock() <tixapi/buyStock>
|
buyStock() <tixapi/buyStock>
|
||||||
sellStock() <tixapi/sellStock>
|
sellStock() <tixapi/sellStock>
|
||||||
shortStock() <tixapi/shortStock>
|
shortStock() <tixapi/shortStock>
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
getStockPurchaseCost() Netscript Function
|
|
||||||
=========================================
|
|
||||||
|
|
||||||
.. js:function:: getStockPurchaseCost(sym, shares, posType)
|
|
||||||
|
|
||||||
:param string sym: Stock symbol
|
|
||||||
:param number shares: Number of shares to purchase
|
|
||||||
:param string posType: Specifies whether the order is a "Long" or "Short" position.
|
|
||||||
The values "L" or "S" can also be used.
|
|
||||||
:RAM cost: 2 GB
|
|
||||||
|
|
||||||
Calculates and returns how much it would cost to buy a given number of
|
|
||||||
shares of a stock. This takes into account :ref:`spread <gameplay_stock_market_spread>`,
|
|
||||||
:ref:`large transactions influencing the price of the stock <gameplay_stock_market_spread_price_movement>`
|
|
||||||
and commission fees.
|
|
@ -1,15 +0,0 @@
|
|||||||
getStockSaleGain() Netscript Function
|
|
||||||
=====================================
|
|
||||||
|
|
||||||
.. js:function:: getStockSaleGain(sym, shares, posType)
|
|
||||||
|
|
||||||
:param string sym: Stock symbol
|
|
||||||
:param number shares: Number of shares to sell
|
|
||||||
:param string posType: Specifies whether the order is a "Long" or "Short" position.
|
|
||||||
The values "L" or "S" can also be used.
|
|
||||||
:RAM cost: 2 GB
|
|
||||||
|
|
||||||
Calculates and returns how much you would gain from selling a given number of
|
|
||||||
shares of a stock. This takes into account :ref:`spread <gameplay_stock_market_spread>`,
|
|
||||||
:ref:`large transactions influencing the price of the stock <gameplay_stock_market_spread_price_movement>`
|
|
||||||
and commission fees.
|
|
@ -222,7 +222,11 @@ export let CONSTANTS: IMap<any> = {
|
|||||||
LatestUpdate:
|
LatestUpdate:
|
||||||
`
|
`
|
||||||
v0.47.0
|
v0.47.0
|
||||||
*
|
* Stock Market changes:
|
||||||
|
** Transactions no longer influence stock prices (but they still influence forecast)
|
||||||
|
** Removed getStockPurchaseCost() and getStockSaleGain() Netscript functions
|
||||||
|
**
|
||||||
|
|
||||||
* Scripts now start/stop instantly
|
* Scripts now start/stop instantly
|
||||||
|
|
||||||
v0.47.0
|
v0.47.0
|
||||||
|
@ -98,10 +98,6 @@ import {
|
|||||||
cancelOrder,
|
cancelOrder,
|
||||||
displayStockMarketContent,
|
displayStockMarketContent,
|
||||||
} from "./StockMarket/StockMarket";
|
} from "./StockMarket/StockMarket";
|
||||||
import {
|
|
||||||
getBuyTransactionCost,
|
|
||||||
getSellTransactionGain,
|
|
||||||
} from "./StockMarket/StockMarketHelpers";
|
|
||||||
import { OrderTypes } from "./StockMarket/data/OrderTypes";
|
import { OrderTypes } from "./StockMarket/data/OrderTypes";
|
||||||
import { PositionTypes } from "./StockMarket/data/PositionTypes";
|
import { PositionTypes } from "./StockMarket/data/PositionTypes";
|
||||||
import { StockSymbols } from "./StockMarket/data/StockSymbols";
|
import { StockSymbols } from "./StockMarket/data/StockSymbols";
|
||||||
@ -1496,48 +1492,6 @@ function NetscriptFunctions(workerScript) {
|
|||||||
|
|
||||||
return stock.maxShares;
|
return stock.maxShares;
|
||||||
},
|
},
|
||||||
getStockPurchaseCost: function(symbol, shares, posType) {
|
|
||||||
updateDynamicRam("getStockPurchaseCost", getRamCost("getStockPurchaseCost"));
|
|
||||||
checkTixApiAccess("getStockPurchaseCost");
|
|
||||||
const stock = getStockFromSymbol(symbol, "getStockPurchaseCost");
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = getBuyTransactionCost(stock, shares, pos);
|
|
||||||
if (res == null) { return Infinity; }
|
|
||||||
|
|
||||||
return res;
|
|
||||||
},
|
|
||||||
getStockSaleGain: function(symbol, shares, posType) {
|
|
||||||
updateDynamicRam("getStockSaleGain", getRamCost("getStockSaleGain"));
|
|
||||||
checkTixApiAccess("getStockSaleGain");
|
|
||||||
const stock = getStockFromSymbol(symbol, "getStockSaleGain");
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = getSellTransactionGain(stock, shares, pos);
|
|
||||||
if (res == null) { return 0; }
|
|
||||||
|
|
||||||
return res;
|
|
||||||
},
|
|
||||||
buyStock: function(symbol, shares) {
|
buyStock: function(symbol, shares) {
|
||||||
updateDynamicRam("buyStock", getRamCost("buyStock"));
|
updateDynamicRam("buyStock", getRamCost("buyStock"));
|
||||||
checkTixApiAccess("buyStock");
|
checkTixApiAccess("buyStock");
|
||||||
|
@ -6,8 +6,7 @@ import { Stock } from "./Stock";
|
|||||||
import {
|
import {
|
||||||
getBuyTransactionCost,
|
getBuyTransactionCost,
|
||||||
getSellTransactionGain,
|
getSellTransactionGain,
|
||||||
processBuyTransactionPriceMovement,
|
processTransactionForecastMovement,
|
||||||
processSellTransactionPriceMovement
|
|
||||||
} from "./StockMarketHelpers";
|
} from "./StockMarketHelpers";
|
||||||
|
|
||||||
import { PositionTypes } from "./data/PositionTypes";
|
import { PositionTypes } from "./data/PositionTypes";
|
||||||
@ -81,7 +80,7 @@ export function buyStock(stock: Stock, shares: number, workerScript: WorkerScrip
|
|||||||
const newTotal = origTotal + totalPrice - CONSTANTS.StockMarketCommission;
|
const newTotal = origTotal + totalPrice - CONSTANTS.StockMarketCommission;
|
||||||
stock.playerShares = Math.round(stock.playerShares + shares);
|
stock.playerShares = Math.round(stock.playerShares + shares);
|
||||||
stock.playerAvgPx = newTotal / stock.playerShares;
|
stock.playerAvgPx = newTotal / stock.playerShares;
|
||||||
processBuyTransactionPriceMovement(stock, shares, PositionTypes.Long);
|
processTransactionForecastMovement(stock, shares);
|
||||||
if (opts.rerenderFn != null && typeof opts.rerenderFn === "function") {
|
if (opts.rerenderFn != null && typeof opts.rerenderFn === "function") {
|
||||||
opts.rerenderFn();
|
opts.rerenderFn();
|
||||||
}
|
}
|
||||||
@ -138,7 +137,7 @@ export function sellStock(stock: Stock, shares: number, workerScript: WorkerScri
|
|||||||
stock.playerAvgPx = 0;
|
stock.playerAvgPx = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
processSellTransactionPriceMovement(stock, shares, PositionTypes.Long);
|
processTransactionForecastMovement(stock, shares);
|
||||||
|
|
||||||
if (opts.rerenderFn != null && typeof opts.rerenderFn === "function") {
|
if (opts.rerenderFn != null && typeof opts.rerenderFn === "function") {
|
||||||
opts.rerenderFn();
|
opts.rerenderFn();
|
||||||
@ -211,7 +210,7 @@ export function shortStock(stock: Stock, shares: number, workerScript: WorkerScr
|
|||||||
const newTotal = origTotal + totalPrice - CONSTANTS.StockMarketCommission;
|
const newTotal = origTotal + totalPrice - CONSTANTS.StockMarketCommission;
|
||||||
stock.playerShortShares = Math.round(stock.playerShortShares + shares);
|
stock.playerShortShares = Math.round(stock.playerShortShares + shares);
|
||||||
stock.playerAvgShortPx = newTotal / stock.playerShortShares;
|
stock.playerAvgShortPx = newTotal / stock.playerShortShares;
|
||||||
processBuyTransactionPriceMovement(stock, shares, PositionTypes.Short);
|
processTransactionForecastMovement(stock, shares);
|
||||||
|
|
||||||
if (opts.rerenderFn != null && typeof opts.rerenderFn === "function") {
|
if (opts.rerenderFn != null && typeof opts.rerenderFn === "function") {
|
||||||
opts.rerenderFn();
|
opts.rerenderFn();
|
||||||
@ -278,7 +277,7 @@ export function sellShort(stock: Stock, shares: number, workerScript: WorkerScri
|
|||||||
if (stock.playerShortShares === 0) {
|
if (stock.playerShortShares === 0) {
|
||||||
stock.playerAvgShortPx = 0;
|
stock.playerAvgShortPx = 0;
|
||||||
}
|
}
|
||||||
processSellTransactionPriceMovement(stock, shares, PositionTypes.Short);
|
processTransactionForecastMovement(stock, shares);
|
||||||
|
|
||||||
if (opts.rerenderFn != null && typeof opts.rerenderFn === "function") {
|
if (opts.rerenderFn != null && typeof opts.rerenderFn === "function") {
|
||||||
opts.rerenderFn();
|
opts.rerenderFn();
|
||||||
|
@ -107,8 +107,6 @@ function executeOrder(order: Order, refs: IProcessOrderRefs) {
|
|||||||
const stockMarket = refs.stockMarket;
|
const stockMarket = refs.stockMarket;
|
||||||
const orderBook = stockMarket["Orders"];
|
const orderBook = stockMarket["Orders"];
|
||||||
const stockOrders = orderBook[stock.symbol];
|
const stockOrders = orderBook[stock.symbol];
|
||||||
const isLimit = (order.type === OrderTypes.LimitBuy || order.type === OrderTypes.LimitSell);
|
|
||||||
let sharesTransacted = 0;
|
|
||||||
|
|
||||||
// When orders are executed, the buying and selling functions shouldn't
|
// When orders are executed, the buying and selling functions shouldn't
|
||||||
// emit popup dialog boxes. This options object configures the functions for that
|
// emit popup dialog boxes. This options object configures the functions for that
|
||||||
@ -120,124 +118,37 @@ function executeOrder(order: Order, refs: IProcessOrderRefs) {
|
|||||||
let res = true;
|
let res = true;
|
||||||
let isBuy = false;
|
let isBuy = false;
|
||||||
switch (order.type) {
|
switch (order.type) {
|
||||||
case OrderTypes.LimitBuy: {
|
case OrderTypes.LimitBuy:
|
||||||
|
case OrderTypes.StopBuy:
|
||||||
isBuy = true;
|
isBuy = true;
|
||||||
|
|
||||||
// We only execute limit orders until the price fails to match the order condition
|
|
||||||
const isLong = (order.pos === PositionTypes.Long);
|
|
||||||
const firstShares = Math.min(order.shares, isLong ? stock.shareTxUntilMovementUp : stock.shareTxUntilMovementDown);
|
|
||||||
|
|
||||||
// First transaction to trigger movement
|
|
||||||
let res = (isLong ? buyStock(stock, firstShares, null, opts) : shortStock(stock, firstShares, null, opts));
|
|
||||||
if (res) {
|
|
||||||
sharesTransacted = firstShares;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let remainingShares = order.shares - firstShares;
|
|
||||||
let remainingIterations = Math.ceil(remainingShares / stock.shareTxForMovement);
|
|
||||||
for (let i = 0; i < remainingIterations; ++i) {
|
|
||||||
if (isLong && stock.price > order.price) {
|
|
||||||
break;
|
|
||||||
} else if (!isLong && stock.price < order.price) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const shares = Math.min(remainingShares, stock.shareTxForMovement);
|
|
||||||
let res = (isLong ? buyStock(stock, shares, null, opts) : shortStock(stock, shares, null, opts));
|
|
||||||
if (res) {
|
|
||||||
sharesTransacted += shares;
|
|
||||||
remainingShares -= shares;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case OrderTypes.StopBuy: {
|
|
||||||
isBuy = true;
|
|
||||||
sharesTransacted = order.shares;
|
|
||||||
if (order.pos === PositionTypes.Long) {
|
if (order.pos === PositionTypes.Long) {
|
||||||
res = buyStock(stock, order.shares, null, opts) && res;
|
res = buyStock(stock, order.shares, null, opts) && res;
|
||||||
} else if (order.pos === PositionTypes.Short) {
|
} else if (order.pos === PositionTypes.Short) {
|
||||||
res = shortStock(stock, order.shares, null, opts) && res;
|
res = shortStock(stock, order.shares, null, opts) && res;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
case OrderTypes.LimitSell:
|
||||||
case OrderTypes.LimitSell: {
|
case OrderTypes.StopSell:
|
||||||
// TODO need to account for player's shares here
|
|
||||||
// We only execute limit orders until the price fails to match the order condition
|
|
||||||
const isLong = (order.pos === PositionTypes.Long);
|
|
||||||
const totalShares = Math.min((isLong ? stock.playerShares : stock.playerShortShares), order.shares);
|
|
||||||
if (totalShares === 0) {
|
|
||||||
return; // Player has no shares
|
|
||||||
}
|
|
||||||
const firstShares = Math.min(totalShares, isLong ? stock.shareTxUntilMovementDown : stock.shareTxUntilMovementUp);
|
|
||||||
|
|
||||||
// First transaction to trigger movement
|
|
||||||
if (isLong ? sellStock(stock, firstShares, null, opts) : sellShort(stock, firstShares, null, opts)) {
|
|
||||||
sharesTransacted = firstShares;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let remainingShares = totalShares - firstShares;
|
|
||||||
let remainingIterations = Math.ceil(remainingShares / stock.shareTxForMovement);
|
|
||||||
for (let i = 0; i < remainingIterations; ++i) {
|
|
||||||
if (isLong && stock.price < order.price) {
|
|
||||||
break;
|
|
||||||
} else if (!isLong && stock.price > order.price) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const shares = Math.min(remainingShares, stock.shareTxForMovement);
|
|
||||||
if (isLong ? sellStock(stock, shares, null, opts) : sellShort(stock, shares, null, opts)) {
|
|
||||||
sharesTransacted += shares;
|
|
||||||
remainingShares -= shares;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case OrderTypes.StopSell: {
|
|
||||||
if (order.pos === PositionTypes.Long) {
|
if (order.pos === PositionTypes.Long) {
|
||||||
sharesTransacted = Math.min(stock.playerShares, order.shares);
|
res = sellStock(stock, order.shares, null, opts) && res;
|
||||||
if (sharesTransacted <= 0) { return; }
|
|
||||||
res = sellStock(stock, sharesTransacted, null, opts) && res;
|
|
||||||
} else if (order.pos === PositionTypes.Short) {
|
} else if (order.pos === PositionTypes.Short) {
|
||||||
sharesTransacted = Math.min(stock.playerShortShares, order.shares);
|
res = sellShort(stock, order.shares, null, opts) && res;
|
||||||
if (sharesTransacted <= 0) { return; }
|
|
||||||
res = sellShort(stock, sharesTransacted, null, opts) && res;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
console.warn(`Invalid order type: ${order.type}`);
|
console.warn(`Invalid order type: ${order.type}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLimit) {
|
|
||||||
res = (sharesTransacted > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Position type, for logging/message purposes
|
// Position type, for logging/message purposes
|
||||||
const pos = order.pos === PositionTypes.Long ? "Long" : "Short";
|
const pos = order.pos === PositionTypes.Long ? "Long" : "Short";
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
for (let i = 0; i < stockOrders.length; ++i) {
|
for (let i = 0; i < stockOrders.length; ++i) {
|
||||||
if (order == stockOrders[i]) {
|
if (order == stockOrders[i]) {
|
||||||
// Limit orders might only transact a certain # of shares, so we have the adjust the order qty.
|
stockOrders.splice(i, 1);
|
||||||
stockOrders[i].shares -= sharesTransacted;
|
dialogBoxCreate(`${order.type} for ${stock.symbol} @ ${numeralWrapper.formatMoney(order.price)} (${pos}) was filled ` +
|
||||||
if (stockOrders[i].shares <= 0) {
|
`(${numeralWrapper.formatBigNumber(Math.round(order.shares))} shares)`);
|
||||||
stockOrders.splice(i, 1);
|
|
||||||
dialogBoxCreate(`${order.type} for ${stock.symbol} @ ${numeralWrapper.formatMoney(order.price)} (${pos}) was filled ` +
|
|
||||||
`(${numeralWrapper.formatBigNumber(Math.round(sharesTransacted))} share)`);
|
|
||||||
} else {
|
|
||||||
dialogBoxCreate(`${order.type} for ${stock.symbol} @ ${numeralWrapper.formatMoney(order.price)} (${pos}) was partially filled ` +
|
|
||||||
`(${numeralWrapper.formatBigNumber(Math.round(sharesTransacted))} shares transacted, ${stockOrders[i].shares} shares remaining`);
|
|
||||||
}
|
|
||||||
refs.rerenderFn();
|
refs.rerenderFn();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -131,12 +131,6 @@ export class Stock {
|
|||||||
*/
|
*/
|
||||||
price: number;
|
price: number;
|
||||||
|
|
||||||
/**
|
|
||||||
* Percentage by which the stock's price changes for a transaction-induced
|
|
||||||
* price movement.
|
|
||||||
*/
|
|
||||||
readonly priceMovementPerc: number;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* How many shares need to be transacted in order to trigger a price movement
|
* How many shares need to be transacted in order to trigger a price movement
|
||||||
*/
|
*/
|
||||||
@ -146,8 +140,7 @@ export class Stock {
|
|||||||
* How many share transactions remaining until a price movement occurs
|
* How many share transactions remaining until a price movement occurs
|
||||||
* (separately tracked for upward and downward movements)
|
* (separately tracked for upward and downward movements)
|
||||||
*/
|
*/
|
||||||
shareTxUntilMovementDown: number;
|
shareTxUntilMovement: number;
|
||||||
shareTxUntilMovementUp: number;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Spread percentage. The bid/ask prices for this stock are N% above or below
|
* Spread percentage. The bid/ask prices for this stock are N% above or below
|
||||||
@ -182,10 +175,8 @@ export class Stock {
|
|||||||
this.otlkMagForecast = this.getAbsoluteForecast();
|
this.otlkMagForecast = this.getAbsoluteForecast();
|
||||||
this.cap = getRandomInt(this.price * 1e3, this.price * 25e3);
|
this.cap = getRandomInt(this.price * 1e3, this.price * 25e3);
|
||||||
this.spreadPerc = toNumber(p.spreadPerc);
|
this.spreadPerc = toNumber(p.spreadPerc);
|
||||||
this.priceMovementPerc = this.spreadPerc / (getRandomInt(10, 30) / 10);
|
|
||||||
this.shareTxForMovement = toNumber(p.shareTxForMovement);
|
this.shareTxForMovement = toNumber(p.shareTxForMovement);
|
||||||
this.shareTxUntilMovementDown = this.shareTxForMovement;
|
this.shareTxUntilMovement = this.shareTxForMovement;
|
||||||
this.shareTxUntilMovementUp = this.shareTxForMovement;
|
|
||||||
|
|
||||||
// Total shares is determined by market cap, and is rounded to nearest 100k
|
// Total shares is determined by market cap, and is rounded to nearest 100k
|
||||||
let totalSharesUnrounded: number = (p.marketCap / this.price);
|
let totalSharesUnrounded: number = (p.marketCap / this.price);
|
||||||
|
@ -7,12 +7,6 @@ import {
|
|||||||
import { Order } from "./Order";
|
import { Order } from "./Order";
|
||||||
import { processOrders } from "./OrderProcessing";
|
import { processOrders } from "./OrderProcessing";
|
||||||
import { Stock } from "./Stock";
|
import { Stock } from "./Stock";
|
||||||
import {
|
|
||||||
getBuyTransactionCost,
|
|
||||||
getSellTransactionGain,
|
|
||||||
processBuyTransactionPriceMovement,
|
|
||||||
processSellTransactionPriceMovement
|
|
||||||
} from "./StockMarketHelpers";
|
|
||||||
import {
|
import {
|
||||||
getStockMarket4SDataCost,
|
getStockMarket4SDataCost,
|
||||||
getStockMarket4STixApiCost
|
getStockMarket4STixApiCost
|
||||||
@ -205,10 +199,10 @@ export function stockMarketCycle() {
|
|||||||
stock.flipForecastForecast();
|
stock.flipForecastForecast();
|
||||||
} else if (roll < 0.6) {
|
} else if (roll < 0.6) {
|
||||||
stock.otlkMagForecast += 0.5;
|
stock.otlkMagForecast += 0.5;
|
||||||
stock.otlkMagForecast = Math.min(stock.otlkMagForecast * 1.02, 50);
|
stock.otlkMagForecast = Math.min(stock.otlkMagForecast * 1.02, 100);
|
||||||
} else if (roll < 0.8) {
|
} else if (roll < 0.8) {
|
||||||
stock.otlkMagForecast -= 0.5;
|
stock.otlkMagForecast -= 0.5;
|
||||||
stock.otlkMagForecast = otlkMagForecast * (1 / 1.02);
|
stock.otlkMagForecast = stock.otlkMagForecast * (1 / 1.02);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -276,8 +270,7 @@ export function processStockPrices(numCycles=1) {
|
|||||||
stock.cycleForecast(otlkMagChange);
|
stock.cycleForecast(otlkMagChange);
|
||||||
|
|
||||||
// Shares required for price movement gradually approaches max over time
|
// Shares required for price movement gradually approaches max over time
|
||||||
stock.shareTxUntilMovementUp = Math.min(stock.shareTxUntilMovementUp + 5, stock.shareTxForMovement);
|
stock.shareTxUntilMovement = Math.min(stock.shareTxUntilMovement + 10, stock.shareTxForMovement);
|
||||||
stock.shareTxUntilMovementDown = Math.min(stock.shareTxUntilMovementDown + 5, stock.shareTxForMovement);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
displayStockMarketContent();
|
displayStockMarketContent();
|
||||||
|
@ -6,34 +6,7 @@ import { CONSTANTS } from "../Constants";
|
|||||||
export const forecastChangePerPriceMovement = 0.005;
|
export const forecastChangePerPriceMovement = 0.005;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a stock, calculates the amount by which the stock price is multiplied
|
* Calculate the total cost of a "buy" transaction. This accounts for spread and commission.
|
||||||
* for an 'upward' price movement. This does not actually increase the stock's price,
|
|
||||||
* just calculates the multiplier
|
|
||||||
* @param {Stock} stock - Stock for price movement
|
|
||||||
* @returns {number | null} Number by which stock's price should be multiplied. Null for invalid args
|
|
||||||
*/
|
|
||||||
export function calculateIncreasingPriceMovement(stock: Stock): number | null {
|
|
||||||
if (!(stock instanceof Stock)) { return null; }
|
|
||||||
|
|
||||||
return (1 + (stock.priceMovementPerc / 100));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given a stock, calculates the amount by which the stock price is multiplied
|
|
||||||
* for a "downward" price movement. This does not actually increase the stock's price,
|
|
||||||
* just calculates the multiplier
|
|
||||||
* @param {Stock} stock - Stock for price movement
|
|
||||||
* @returns {number | null} Number by which stock's price should be multiplied. Null for invalid args
|
|
||||||
*/
|
|
||||||
export function calculateDecreasingPriceMovement(stock: Stock): number | null {
|
|
||||||
if (!(stock instanceof Stock)) { return null; }
|
|
||||||
|
|
||||||
return (1 - (stock.priceMovementPerc / 100));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the total cost of a "buy" transaction. This accounts for spread,
|
|
||||||
* price movements, and commission.
|
|
||||||
* @param {Stock} stock - Stock being purchased
|
* @param {Stock} stock - Stock being purchased
|
||||||
* @param {number} shares - Number of shares being transacted
|
* @param {number} shares - Number of shares being transacted
|
||||||
* @param {PositionTypes} posType - Long or short position
|
* @param {PositionTypes} posType - Long or short position
|
||||||
@ -50,135 +23,15 @@ export function getBuyTransactionCost(stock: Stock, shares: number, posType: Pos
|
|||||||
|
|
||||||
// If the number of shares doesn't trigger a price movement, its a simple calculation
|
// If the number of shares doesn't trigger a price movement, its a simple calculation
|
||||||
if (isLong) {
|
if (isLong) {
|
||||||
if (shares <= stock.shareTxUntilMovementUp) {
|
return (shares * stock.getAskPrice()) + CONSTANTS.StockMarketCommission;
|
||||||
return (shares * stock.getAskPrice()) + CONSTANTS.StockMarketCommission;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (shares <= stock.shareTxUntilMovementDown) {
|
return (shares * stock.getBidPrice()) + CONSTANTS.StockMarketCommission;
|
||||||
return (shares * stock.getBidPrice()) + CONSTANTS.StockMarketCommission;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate how many iterations of price changes we need to account for
|
|
||||||
const firstShares = isLong ? stock.shareTxUntilMovementUp : stock.shareTxUntilMovementDown;
|
|
||||||
let remainingShares = shares - firstShares;
|
|
||||||
let numIterations = 1 + Math.ceil(remainingShares / stock.shareTxForMovement);
|
|
||||||
|
|
||||||
// The initial cost calculation takes care of the first "iteration"
|
|
||||||
let currPrice = isLong ? stock.getAskPrice() : stock.getBidPrice();
|
|
||||||
let totalCost = (firstShares * currPrice);
|
|
||||||
|
|
||||||
const increasingMvmt = calculateIncreasingPriceMovement(stock)!;
|
|
||||||
const decreasingMvmt = calculateDecreasingPriceMovement(stock)!;
|
|
||||||
|
|
||||||
function processPriceMovement() {
|
|
||||||
if (isLong) {
|
|
||||||
currPrice *= increasingMvmt;
|
|
||||||
} else {
|
|
||||||
currPrice *= decreasingMvmt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 1; i < numIterations; ++i) {
|
|
||||||
processPriceMovement();
|
|
||||||
|
|
||||||
const amt = Math.min(stock.shareTxForMovement, remainingShares);
|
|
||||||
totalCost += (amt * currPrice);
|
|
||||||
remainingShares -= amt;
|
|
||||||
}
|
|
||||||
|
|
||||||
return totalCost + CONSTANTS.StockMarketCommission;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Processes a buy transaction's resulting price AND forecast movement.
|
|
||||||
* @param {Stock} stock - Stock being purchased
|
|
||||||
* @param {number} shares - Number of shares being transacted
|
|
||||||
* @param {PositionTypes} posType - Long or short position
|
|
||||||
*/
|
|
||||||
export function processBuyTransactionPriceMovement(stock: Stock, shares: number, posType: PositionTypes): void {
|
|
||||||
if (isNaN(shares) || shares <= 0 || !(stock instanceof Stock)) { return; }
|
|
||||||
|
|
||||||
// Cap the 'shares' arg at the stock's maximum shares. This'll prevent
|
|
||||||
// hanging in the case when a really big number is passed in
|
|
||||||
shares = Math.min(shares, stock.maxShares);
|
|
||||||
|
|
||||||
const isLong = (posType === PositionTypes.Long);
|
|
||||||
|
|
||||||
let currPrice = stock.price;
|
|
||||||
function processPriceMovement() {
|
|
||||||
if (isLong) {
|
|
||||||
currPrice *= calculateIncreasingPriceMovement(stock)!;
|
|
||||||
} else {
|
|
||||||
currPrice *= calculateDecreasingPriceMovement(stock)!;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there's only going to be one iteration
|
|
||||||
const firstShares = isLong ? stock.shareTxUntilMovementUp : stock.shareTxUntilMovementDown;
|
|
||||||
if (shares <= firstShares) {
|
|
||||||
function triggerMovement() {
|
|
||||||
processPriceMovement();
|
|
||||||
stock.changePrice(currPrice);
|
|
||||||
stock.otlkMag -= (forecastChangePerPriceMovement);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isLong) {
|
|
||||||
stock.shareTxUntilMovementUp -= shares;
|
|
||||||
if (stock.shareTxUntilMovementUp <= 0) {
|
|
||||||
stock.shareTxUntilMovementUp = stock.shareTxForMovement;
|
|
||||||
triggerMovement();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
stock.shareTxUntilMovementDown -= shares;
|
|
||||||
if (stock.shareTxUntilMovementDown <= 0) {
|
|
||||||
stock.shareTxUntilMovementDown = stock.shareTxForMovement;
|
|
||||||
triggerMovement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate how many iterations of price changes we need to account for
|
|
||||||
let remainingShares = shares - firstShares;
|
|
||||||
let numIterations = 1 + Math.ceil(remainingShares / stock.shareTxForMovement);
|
|
||||||
|
|
||||||
for (let i = 1; i < numIterations; ++i) {
|
|
||||||
processPriceMovement();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If on the offchance we end up perfectly at the next price movement
|
|
||||||
if (isLong) {
|
|
||||||
stock.shareTxUntilMovementUp = stock.shareTxForMovement - ((shares - stock.shareTxUntilMovementUp) % stock.shareTxForMovement);
|
|
||||||
if (stock.shareTxUntilMovementUp === stock.shareTxForMovement || stock.shareTxUntilMovementUp <= 0) {
|
|
||||||
// The shareTxUntilMovementUp ended up at 0 at the end of the "processing"
|
|
||||||
++numIterations;
|
|
||||||
stock.shareTxUntilMovementUp = stock.shareTxForMovement;
|
|
||||||
processPriceMovement();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
stock.shareTxUntilMovementDown = stock.shareTxForMovement - ((shares - stock.shareTxUntilMovementDown) % stock.shareTxForMovement);
|
|
||||||
if (stock.shareTxUntilMovementDown === stock.shareTxForMovement || stock.shareTxUntilMovementDown <= 0) {
|
|
||||||
// The shareTxUntilMovementDown ended up at 0 at the end of the "processing"
|
|
||||||
++numIterations;
|
|
||||||
stock.shareTxUntilMovementDown = stock.shareTxForMovement;
|
|
||||||
processPriceMovement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stock.changePrice(currPrice);
|
|
||||||
|
|
||||||
// Forecast always decreases in magnitude
|
|
||||||
const forecastChange = Math.min(5, forecastChangePerPriceMovement * (numIterations - 1));
|
|
||||||
if (stock.otlkMag > 10) {
|
|
||||||
stock.otlkMag -= forecastChange;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate the TOTAL amount of money gained from a sale (NOT net profit). This accounts
|
* Calculate the TOTAL amount of money gained from a sale (NOT net profit). This accounts
|
||||||
* for spread, price movements, and commission.
|
* for spread and commission.
|
||||||
* @param {Stock} stock - Stock being sold
|
* @param {Stock} stock - Stock being sold
|
||||||
* @param {number} shares - Number of sharse being transacted
|
* @param {number} shares - Number of sharse being transacted
|
||||||
* @param {PositionTypes} posType - Long or short position
|
* @param {PositionTypes} posType - Long or short position
|
||||||
@ -192,104 +45,39 @@ export function getSellTransactionGain(stock: Stock, shares: number, posType: Po
|
|||||||
shares = Math.min(shares, stock.maxShares);
|
shares = Math.min(shares, stock.maxShares);
|
||||||
|
|
||||||
const isLong = (posType === PositionTypes.Long);
|
const isLong = (posType === PositionTypes.Long);
|
||||||
const firstShares = isLong ? stock.shareTxUntilMovementDown : stock.shareTxUntilMovementUp;
|
if (isLong) {
|
||||||
|
return (shares * stock.getBidPrice()) - CONSTANTS.StockMarketCommission;
|
||||||
|
} else {
|
||||||
|
// Calculating gains for a short position requires calculating the profit made
|
||||||
|
const origCost = shares * stock.playerAvgShortPx;
|
||||||
|
const profit = ((stock.playerAvgShortPx - stock.getAskPrice()) * shares) - CONSTANTS.StockMarketCommission;
|
||||||
|
|
||||||
// If the number of shares doesn't trigger a price mvoement, its a simple calculation
|
return origCost + profit;
|
||||||
if (shares <= firstShares) {
|
|
||||||
if (isLong) {
|
|
||||||
return (shares * stock.getBidPrice()) - CONSTANTS.StockMarketCommission;
|
|
||||||
} else {
|
|
||||||
// Calculating gains for a short position requires calculating the profit made
|
|
||||||
const origCost = shares * stock.playerAvgShortPx;
|
|
||||||
const profit = ((stock.playerAvgShortPx - stock.getAskPrice()) * shares) - CONSTANTS.StockMarketCommission;
|
|
||||||
|
|
||||||
return origCost + profit;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate how many iterations of price changes we need to account for
|
|
||||||
let remainingShares = shares - firstShares;
|
|
||||||
let numIterations = 1 + Math.ceil(remainingShares / stock.shareTxForMovement);
|
|
||||||
|
|
||||||
// Helper function to calculate gain for a single iteration
|
|
||||||
function calculateGain(thisPrice: number, thisShares: number) {
|
|
||||||
if (isLong) {
|
|
||||||
return thisShares * thisPrice;
|
|
||||||
} else {
|
|
||||||
const origCost = thisShares * stock.playerAvgShortPx;
|
|
||||||
const profit = ((stock.playerAvgShortPx - thisPrice) * thisShares);
|
|
||||||
|
|
||||||
return origCost + profit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The initial cost calculation takes care of the first "iteration"
|
|
||||||
let currPrice = isLong ? stock.getBidPrice() : stock.getAskPrice();
|
|
||||||
let totalGain = calculateGain(currPrice, firstShares);
|
|
||||||
for (let i = 1; i < numIterations; ++i) {
|
|
||||||
// Price movement
|
|
||||||
if (isLong) {
|
|
||||||
currPrice *= calculateDecreasingPriceMovement(stock)!;
|
|
||||||
} else {
|
|
||||||
currPrice *= calculateIncreasingPriceMovement(stock)!;
|
|
||||||
}
|
|
||||||
|
|
||||||
const amt = Math.min(stock.shareTxForMovement, remainingShares);
|
|
||||||
totalGain += calculateGain(currPrice, amt);
|
|
||||||
remainingShares -= amt;
|
|
||||||
}
|
|
||||||
|
|
||||||
return totalGain - CONSTANTS.StockMarketCommission;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes a sell transaction's resulting price movement
|
* Processes a stock's change in forecast whenever it is transacted
|
||||||
* @param {Stock} stock - Stock being sold
|
* @param {Stock} stock - Stock being sold
|
||||||
* @param {number} shares - Number of sharse being transacted
|
* @param {number} shares - Number of sharse being transacted
|
||||||
* @param {PositionTypes} posType - Long or short position
|
* @param {PositionTypes} posType - Long or short position
|
||||||
*/
|
*/
|
||||||
export function processSellTransactionPriceMovement(stock: Stock, shares: number, posType: PositionTypes): void {
|
export function processTransactionForecastMovement(stock: Stock, shares: number): void {
|
||||||
if (isNaN(shares) || shares <= 0 || !(stock instanceof Stock)) { return; }
|
if (isNaN(shares) || shares <= 0 || !(stock instanceof Stock)) { return; }
|
||||||
|
|
||||||
// Cap the 'shares' arg at the stock's maximum shares. This'll prevent
|
// Cap the 'shares' arg at the stock's maximum shares. This'll prevent
|
||||||
// hanging in the case when a really big number is passed in
|
// hanging in the case when a really big number is passed in
|
||||||
shares = Math.min(shares, stock.maxShares);
|
shares = Math.min(shares, stock.maxShares);
|
||||||
|
|
||||||
const isLong = (posType === PositionTypes.Long);
|
|
||||||
const firstShares = isLong ? stock.shareTxUntilMovementDown : stock.shareTxUntilMovementUp;
|
|
||||||
|
|
||||||
let currPrice = stock.price;
|
|
||||||
function processPriceMovement() {
|
|
||||||
if (isLong) {
|
|
||||||
currPrice *= calculateDecreasingPriceMovement(stock)!;
|
|
||||||
} else {
|
|
||||||
currPrice *= calculateIncreasingPriceMovement(stock)!;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there's only going to be one iteration at most
|
// If there's only going to be one iteration at most
|
||||||
|
const firstShares = stock.shareTxUntilMovement;
|
||||||
if (shares <= firstShares) {
|
if (shares <= firstShares) {
|
||||||
function triggerPriceMovement() {
|
stock.shareTxUntilMovement -= shares;
|
||||||
processPriceMovement();
|
if (stock.shareTxUntilMovement <= 0) {
|
||||||
stock.changePrice(currPrice);
|
stock.shareTxUntilMovement = stock.shareTxForMovement;
|
||||||
stock.otlkMag -= (forecastChangePerPriceMovement);
|
stock.otlkMag -= (forecastChangePerPriceMovement);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLong) {
|
|
||||||
stock.shareTxUntilMovementDown -= shares;
|
|
||||||
if (stock.shareTxUntilMovementDown <= 0) {
|
|
||||||
stock.shareTxUntilMovementDown = stock.shareTxForMovement;
|
|
||||||
triggerPriceMovement();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
stock.shareTxUntilMovementUp -= shares;
|
|
||||||
if (stock.shareTxUntilMovementUp <= 0) {
|
|
||||||
stock.shareTxUntilMovementUp = stock.shareTxForMovement;
|
|
||||||
triggerPriceMovement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -297,28 +85,13 @@ export function processSellTransactionPriceMovement(stock: Stock, shares: number
|
|||||||
let remainingShares = shares - firstShares;
|
let remainingShares = shares - firstShares;
|
||||||
let numIterations = 1 + Math.ceil(remainingShares / stock.shareTxForMovement);
|
let numIterations = 1 + Math.ceil(remainingShares / stock.shareTxForMovement);
|
||||||
|
|
||||||
for (let i = 1; i < numIterations; ++i) {
|
|
||||||
processPriceMovement();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If on the offchance we end up perfectly at the next price movement
|
// If on the offchance we end up perfectly at the next price movement
|
||||||
if (isLong) {
|
stock.shareTxUntilMovement = stock.shareTxForMovement - ((shares - stock.shareTxUntilMovement) % stock.shareTxForMovement);
|
||||||
stock.shareTxUntilMovementDown = stock.shareTxForMovement - ((shares - stock.shareTxUntilMovementDown) % stock.shareTxForMovement);
|
if (stock.shareTxUntilMovement === stock.shareTxForMovement || stock.shareTxUntilMovement <= 0) {
|
||||||
if (stock.shareTxUntilMovementDown === stock.shareTxForMovement || stock.shareTxUntilMovementDown <= 0) {
|
++numIterations;
|
||||||
++numIterations;
|
stock.shareTxUntilMovement = stock.shareTxForMovement;
|
||||||
stock.shareTxUntilMovementDown = stock.shareTxForMovement;
|
|
||||||
processPriceMovement();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
stock.shareTxUntilMovementUp = stock.shareTxForMovement - ((shares - stock.shareTxUntilMovementUp) % stock.shareTxForMovement);
|
|
||||||
if (stock.shareTxUntilMovementUp === stock.shareTxForMovement || stock.shareTxUntilMovementUp <= 0) {
|
|
||||||
++numIterations;
|
|
||||||
stock.shareTxUntilMovementUp = stock.shareTxForMovement;
|
|
||||||
processPriceMovement();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stock.changePrice(currPrice);
|
|
||||||
|
|
||||||
// Forecast always decreases in magnitude
|
// Forecast always decreases in magnitude
|
||||||
const forecastChange = Math.min(5, forecastChangePerPriceMovement * (numIterations - 1));
|
const forecastChange = Math.min(5, forecastChangePerPriceMovement * (numIterations - 1));
|
||||||
@ -341,44 +114,8 @@ export function calculateBuyMaxAmount(stock: Stock, posType: PositionTypes, mone
|
|||||||
|
|
||||||
const isLong = (posType === PositionTypes.Long);
|
const isLong = (posType === PositionTypes.Long);
|
||||||
|
|
||||||
const increasingMvmt = calculateIncreasingPriceMovement(stock);
|
|
||||||
const decreasingMvmt = calculateDecreasingPriceMovement(stock);
|
|
||||||
if (increasingMvmt == null || decreasingMvmt == null) { return 0; }
|
|
||||||
|
|
||||||
let remainingMoney = money - CONSTANTS.StockMarketCommission;
|
let remainingMoney = money - CONSTANTS.StockMarketCommission;
|
||||||
let currPrice = isLong ? stock.getAskPrice() : stock.getBidPrice();
|
let currPrice = isLong ? stock.getAskPrice() : stock.getBidPrice();
|
||||||
|
|
||||||
// No price movement
|
return Math.floor(remainingMoney / currPrice);
|
||||||
const firstShares = isLong ? stock.shareTxUntilMovementUp : stock.shareTxUntilMovementDown;
|
|
||||||
const firstIterationCost = firstShares * currPrice;
|
|
||||||
if (remainingMoney < firstIterationCost) {
|
|
||||||
return Math.floor(remainingMoney / currPrice);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We'll avoid any accidental infinite loops by having a hardcoded maximum number of
|
|
||||||
// iterations
|
|
||||||
let numShares = firstShares;
|
|
||||||
remainingMoney -= firstIterationCost;
|
|
||||||
for (let i = 0; i < 10e3; ++i) {
|
|
||||||
if (isLong) {
|
|
||||||
currPrice *= increasingMvmt;
|
|
||||||
} else {
|
|
||||||
currPrice *= decreasingMvmt;
|
|
||||||
}
|
|
||||||
|
|
||||||
const affordableShares = Math.floor(remainingMoney / currPrice);
|
|
||||||
const actualShares = Math.min(stock.shareTxForMovement, affordableShares);
|
|
||||||
|
|
||||||
// Can't afford any more, so we're done
|
|
||||||
if (actualShares <= 0) { break; }
|
|
||||||
|
|
||||||
numShares += actualShares;
|
|
||||||
|
|
||||||
let cost = actualShares * currPrice;
|
|
||||||
remainingMoney -= cost;
|
|
||||||
|
|
||||||
if (remainingMoney <= 0) { break; }
|
|
||||||
}
|
|
||||||
|
|
||||||
return Math.floor(numShares);
|
|
||||||
}
|
}
|
||||||
|
@ -117,12 +117,6 @@ export class StockTicker extends React.Component<IProps, IState> {
|
|||||||
let costTxt = `Purchasing ${numeralWrapper.formatBigNumber(qty)} shares (${this.state.position === PositionTypes.Long ? "Long" : "Short"}) ` +
|
let costTxt = `Purchasing ${numeralWrapper.formatBigNumber(qty)} shares (${this.state.position === PositionTypes.Long ? "Long" : "Short"}) ` +
|
||||||
`will cost ${numeralWrapper.formatMoney(cost)}. `;
|
`will cost ${numeralWrapper.formatMoney(cost)}. `;
|
||||||
|
|
||||||
const amtNeededForMovement = this.state.position === PositionTypes.Long ? stock.shareTxUntilMovementUp : stock.shareTxUntilMovementDown;
|
|
||||||
const causesMovement = qty > amtNeededForMovement;
|
|
||||||
if (causesMovement) {
|
|
||||||
costTxt += `WARNING: Purchasing this many shares will influence the stock price`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return costTxt;
|
return costTxt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,12 +145,6 @@ export class StockTicker extends React.Component<IProps, IState> {
|
|||||||
let costTxt = `Selling ${numeralWrapper.formatBigNumber(qty)} shares (${this.state.position === PositionTypes.Long ? "Long" : "Short"}) ` +
|
let costTxt = `Selling ${numeralWrapper.formatBigNumber(qty)} shares (${this.state.position === PositionTypes.Long ? "Long" : "Short"}) ` +
|
||||||
`will result in a gain of ${numeralWrapper.formatMoney(cost)}. `;
|
`will result in a gain of ${numeralWrapper.formatMoney(cost)}. `;
|
||||||
|
|
||||||
const amtNeededForMovement = this.state.position === PositionTypes.Long ? stock.shareTxUntilMovementDown : stock.shareTxUntilMovementUp;
|
|
||||||
const causesMovement = qty > amtNeededForMovement;
|
|
||||||
if (causesMovement) {
|
|
||||||
costTxt += `WARNING: Selling this many shares will influence the stock price`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return costTxt;
|
return costTxt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,11 +345,7 @@ export class StockTicker extends React.Component<IProps, IState> {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
// Determine if the player's intended transaction will cause a price movement
|
// Determine if the player's intended transaction will cause a price movement
|
||||||
let causesMovement: boolean = false;
|
|
||||||
const qty = this.getQuantity();
|
const qty = this.getQuantity();
|
||||||
if (!isNaN(qty)) {
|
|
||||||
causesMovement = qty > this.props.stock.shareTxForMovement;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li>
|
<li>
|
||||||
@ -400,14 +384,6 @@ export class StockTicker extends React.Component<IProps, IState> {
|
|||||||
<StockTickerTxButton onClick={this.handleSellButtonClick} text={"Sell"} tooltip={this.getSellTransactionCostText()} />
|
<StockTickerTxButton onClick={this.handleSellButtonClick} text={"Sell"} tooltip={this.getSellTransactionCostText()} />
|
||||||
<StockTickerTxButton onClick={this.handleBuyMaxButtonClick} text={"Buy MAX"} />
|
<StockTickerTxButton onClick={this.handleBuyMaxButtonClick} text={"Buy MAX"} />
|
||||||
<StockTickerTxButton onClick={this.handleSellAllButtonClick} text={"Sell ALL"} />
|
<StockTickerTxButton onClick={this.handleSellAllButtonClick} text={"Sell ALL"} />
|
||||||
{
|
|
||||||
causesMovement &&
|
|
||||||
<p className="stock-market-price-movement-warning">
|
|
||||||
WARNING: Buying/Selling {numeralWrapper.formatBigNumber(qty)} shares may affect
|
|
||||||
the stock's price. This applies during the transaction itself as well. See Investopedia
|
|
||||||
for more details.
|
|
||||||
</p>
|
|
||||||
}
|
|
||||||
<StockTickerPositionText p={this.props.p} stock={this.props.stock} />
|
<StockTickerPositionText p={this.props.p} stock={this.props.stock} />
|
||||||
<StockTickerOrderList
|
<StockTickerOrderList
|
||||||
cancelOrder={this.props.cancelOrder}
|
cancelOrder={this.props.cancelOrder}
|
||||||
|
@ -593,16 +593,6 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function() {
|
|||||||
await testNonzeroDynamicRamCost(f);
|
await testNonzeroDynamicRamCost(f);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("getStockPurchaseCost()", async function() {
|
|
||||||
const f = ["getStockPurchaseCost"];
|
|
||||||
await testNonzeroDynamicRamCost(f);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("getStockSaleGain()", async function() {
|
|
||||||
const f = ["getStockSaleGain"];
|
|
||||||
await testNonzeroDynamicRamCost(f);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("buyStock()", async function() {
|
it("buyStock()", async function() {
|
||||||
const f = ["buyStock"];
|
const f = ["buyStock"];
|
||||||
await testNonzeroDynamicRamCost(f);
|
await testNonzeroDynamicRamCost(f);
|
||||||
|
@ -19,13 +19,10 @@ import {
|
|||||||
SymbolToStockMap,
|
SymbolToStockMap,
|
||||||
} from "../src/StockMarket/StockMarket";
|
} from "../src/StockMarket/StockMarket";
|
||||||
import {
|
import {
|
||||||
calculateIncreasingPriceMovement,
|
|
||||||
calculateDecreasingPriceMovement,
|
|
||||||
forecastChangePerPriceMovement,
|
forecastChangePerPriceMovement,
|
||||||
getBuyTransactionCost,
|
getBuyTransactionCost,
|
||||||
getSellTransactionGain,
|
getSellTransactionGain,
|
||||||
processBuyTransactionPriceMovement,
|
processTransactionForecastMovement,
|
||||||
processSellTransactionPriceMovement,
|
|
||||||
} from "../src/StockMarket/StockMarketHelpers";
|
} from "../src/StockMarket/StockMarketHelpers";
|
||||||
import { OrderTypes } from "../src/StockMarket/data/OrderTypes"
|
import { OrderTypes } from "../src/StockMarket/data/OrderTypes"
|
||||||
import { PositionTypes } from "../src/StockMarket/data/PositionTypes";
|
import { PositionTypes } from "../src/StockMarket/data/PositionTypes";
|
||||||
@ -79,13 +76,12 @@ describe("Stock Market Tests", function() {
|
|||||||
expect(stock.b).to.equal(ctorParams.b);
|
expect(stock.b).to.equal(ctorParams.b);
|
||||||
expect(stock.mv).to.equal(ctorParams.mv);
|
expect(stock.mv).to.equal(ctorParams.mv);
|
||||||
expect(stock.shareTxForMovement).to.equal(ctorParams.shareTxForMovement);
|
expect(stock.shareTxForMovement).to.equal(ctorParams.shareTxForMovement);
|
||||||
expect(stock.shareTxUntilMovementUp).to.equal(ctorParams.shareTxForMovement);
|
expect(stock.shareTxUntilMovement).to.equal(ctorParams.shareTxForMovement);
|
||||||
expect(stock.shareTxUntilMovementDown).to.equal(ctorParams.shareTxForMovement);
|
|
||||||
expect(stock.maxShares).to.be.below(stock.totalShares);
|
expect(stock.maxShares).to.be.below(stock.totalShares);
|
||||||
expect(stock.spreadPerc).to.equal(ctorParams.spreadPerc);
|
expect(stock.spreadPerc).to.equal(ctorParams.spreadPerc);
|
||||||
expect(stock.priceMovementPerc).to.be.a("number");
|
expect(stock.otlkMag).to.be.a("number");
|
||||||
expect(stock.priceMovementPerc).to.be.at.most(stock.spreadPerc);
|
expect(stock.otlkMag).to.equal(ctorParams.otlkMag);
|
||||||
expect(stock.priceMovementPerc).to.be.at.least(0);
|
expect(stock.otlkMagForecast).to.equal(ctorParams.b ? 50 + ctorParams.otlkMag : 50 - ctorParams.otlkMag);
|
||||||
});
|
});
|
||||||
|
|
||||||
it ("should properly initialize props from range-values", function() {
|
it ("should properly initialize props from range-values", function() {
|
||||||
@ -141,6 +137,67 @@ describe("Stock Market Tests", function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("#cycleForecast()", function() {
|
||||||
|
it("should appropriately change the otlkMag by the given amount when b=true", function() {
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#flipForecastForecast()", function() {
|
||||||
|
it("should flip the 'otlkMagForecast' property around 50", function() {
|
||||||
|
stock.otlkMagForecast = 50;
|
||||||
|
stock.flipForecastForecast();
|
||||||
|
expect(stock.otlkMagForecast).to.equal(50);
|
||||||
|
|
||||||
|
stock.otlkMagForecast = 60;
|
||||||
|
stock.flipForecastForecast();
|
||||||
|
expect(stock.otlkMagForecast).to.equal(40);
|
||||||
|
|
||||||
|
stock.otlkMagForecast = 90;
|
||||||
|
stock.flipForecastForecast();
|
||||||
|
expect(stock.otlkMagForecast).to.equal(10);
|
||||||
|
|
||||||
|
stock.otlkMagForecast = 100;
|
||||||
|
stock.flipForecastForecast();
|
||||||
|
expect(stock.otlkMagForecast).to.equal(0);
|
||||||
|
|
||||||
|
stock.otlkMagForecast = 40;
|
||||||
|
stock.flipForecastForecast();
|
||||||
|
expect(stock.otlkMagForecast).to.equal(60);
|
||||||
|
|
||||||
|
stock.otlkMagForecast = 0;
|
||||||
|
stock.flipForecastForecast();
|
||||||
|
expect(stock.otlkMagForecast).to.equal(100);
|
||||||
|
|
||||||
|
stock.otlkMagForecast = 25;
|
||||||
|
stock.flipForecastForecast();
|
||||||
|
expect(stock.otlkMagForecast).to.equal(75);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("#getAbsoluteForecast()", function() {
|
||||||
|
it("should return the absolute forecast on a 1-100 scale", function() {
|
||||||
|
stock.b = true;
|
||||||
|
stock.otlkMag = 10;
|
||||||
|
expect(stock.getAbsoluteForecast()).to.equal(60);
|
||||||
|
|
||||||
|
stock.b = false;
|
||||||
|
expect(stock.getAbsoluteForecast()).to.equal(40);
|
||||||
|
|
||||||
|
stock.otlkMag = 30;
|
||||||
|
expect(stock.getAbsoluteForecast()).to.equal(20);
|
||||||
|
|
||||||
|
stock.b = true;
|
||||||
|
expect(stock.getAbsoluteForecast()).to.equal(80);
|
||||||
|
|
||||||
|
stock.otlkMag = 0;
|
||||||
|
expect(stock.getAbsoluteForecast()).to.equal(50);
|
||||||
|
|
||||||
|
stock.b = false;
|
||||||
|
expect(stock.getAbsoluteForecast()).to.equal(50);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("#getAskPrice()", function() {
|
describe("#getAskPrice()", function() {
|
||||||
it("should return the price increased by spread percentage", function() {
|
it("should return the price increased by spread percentage", function() {
|
||||||
const perc = stock.spreadPerc / 100;
|
const perc = stock.spreadPerc / 100;
|
||||||
@ -162,6 +219,55 @@ describe("Stock Market Tests", function() {
|
|||||||
expect(stock.getBidPrice()).to.equal(expected);
|
expect(stock.getBidPrice()).to.equal(expected);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("#getForecastIncreaseChance()", function() {
|
||||||
|
it("should return the chance that the stock has of increasing in decimal form", function() {
|
||||||
|
stock.b = true;
|
||||||
|
|
||||||
|
stock.otlkMagForecast = 90;
|
||||||
|
stock.otlkMag = 20; // Absolute forecast of 70
|
||||||
|
expect(stock.getForecastIncreaseChance()).to.equal(0.7);
|
||||||
|
|
||||||
|
stock.otlkMag = 25; // Absolute forecast of 75
|
||||||
|
expect(stock.getForecastIncreaseChance()).to.equal(0.65);
|
||||||
|
|
||||||
|
stock.otlkMagForecast = 100;
|
||||||
|
stock.otlkMag = 0; // Absolute forecast of 50
|
||||||
|
expect(stock.getForecastIncreaseChance()).to.equal(0.95);
|
||||||
|
|
||||||
|
stock.otlkMagForecast = 60;
|
||||||
|
stock.otlkMag = 25; // Absolute forecast of 75
|
||||||
|
expect(stock.getForecastIncreaseChance()).to.equal(0.35);
|
||||||
|
|
||||||
|
stock.otlkMagForecast = 10;
|
||||||
|
expect(stock.getForecastIncreaseChance()).to.equal(0.05);
|
||||||
|
|
||||||
|
stock.b = false;
|
||||||
|
|
||||||
|
stock.otlkMagForecast = 90;
|
||||||
|
stock.otlkMag = 20; // Absolute forecast of 30
|
||||||
|
expect(stock.getForecastIncreaseChance()).to.equal(0.95);
|
||||||
|
|
||||||
|
stock.otlkMagForecast = 50;
|
||||||
|
stock.otlkMag = 25; // Absolute forecast of 25
|
||||||
|
expect(stock.getForecastIncreaseChance()).to.equal(0.75);
|
||||||
|
|
||||||
|
stock.otlkMagForecast = 100;
|
||||||
|
stock.otlkMag = 0; // Absolute forecast of 50
|
||||||
|
expect(stock.getForecastIncreaseChance()).to.equal(0.95);
|
||||||
|
|
||||||
|
stock.otlkMagForecast = 5;
|
||||||
|
stock.otlkMag = 25; // Absolute forecast of 25
|
||||||
|
expect(stock.getForecastIncreaseChance()).to.equal(0.3);
|
||||||
|
|
||||||
|
stock.otlkMagForecast = 10;
|
||||||
|
expect(stock.getForecastIncreaseChance()).to.equal(0.35);
|
||||||
|
|
||||||
|
stock.otlkMagForecast = 50;
|
||||||
|
stock.otlkMag = 0;
|
||||||
|
expect(stock.getForecastIncreaseChance()).to.equal(0.5);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("StockMarket object", function() {
|
describe("StockMarket object", function() {
|
||||||
@ -286,44 +392,18 @@ describe("Stock Market Tests", function() {
|
|||||||
expect(res).to.equal(null);
|
expect(res).to.equal(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should properly evaluate LONG transactions that doesn't trigger a price movement", function() {
|
it("should properly evaluate LONG transactions", function() {
|
||||||
const shares = ctorParams.shareTxForMovement / 2;
|
const shares = ctorParams.shareTxForMovement / 2;
|
||||||
const res = getBuyTransactionCost(stock, shares, PositionTypes.Long);
|
const res = getBuyTransactionCost(stock, shares, PositionTypes.Long);
|
||||||
expect(res).to.equal(shares * stock.getAskPrice() + commission);
|
expect(res).to.equal(shares * stock.getAskPrice() + commission);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should properly evaluate SHORT transactions that doesn't trigger a price movement", function() {
|
it("should properly evaluate SHORT transactions", function() {
|
||||||
const shares = ctorParams.shareTxForMovement / 2;
|
const shares = ctorParams.shareTxForMovement / 2;
|
||||||
const res = getBuyTransactionCost(stock, shares, PositionTypes.Short);
|
const res = getBuyTransactionCost(stock, shares, PositionTypes.Short);
|
||||||
expect(res).to.equal(shares * stock.getBidPrice() + commission);
|
expect(res).to.equal(shares * stock.getBidPrice() + commission);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should properly evaluate LONG transactions that trigger price movements", function() {
|
|
||||||
const sharesPerMvmt = ctorParams.shareTxForMovement;
|
|
||||||
const shares = sharesPerMvmt * 3;
|
|
||||||
const res = getBuyTransactionCost(stock, shares, PositionTypes.Long);
|
|
||||||
|
|
||||||
// Calculate expected cost
|
|
||||||
const secondPrice = stock.getAskPrice() * calculateIncreasingPriceMovement(stock);
|
|
||||||
const thirdPrice = secondPrice * calculateIncreasingPriceMovement(stock);
|
|
||||||
let expected = (sharesPerMvmt * stock.getAskPrice()) + (sharesPerMvmt * secondPrice) + (sharesPerMvmt * thirdPrice);
|
|
||||||
|
|
||||||
expect(res).to.equal(expected + commission);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should properly evaluate SHORT transactions that trigger price movements", function() {
|
|
||||||
const sharesPerMvmt = ctorParams.shareTxForMovement;
|
|
||||||
const shares = sharesPerMvmt * 3;
|
|
||||||
const res = getBuyTransactionCost(stock, shares, PositionTypes.Short);
|
|
||||||
|
|
||||||
// Calculate expected cost
|
|
||||||
const secondPrice = stock.getBidPrice() * calculateDecreasingPriceMovement(stock);
|
|
||||||
const thirdPrice = secondPrice * calculateDecreasingPriceMovement(stock);
|
|
||||||
let expected = (sharesPerMvmt * stock.getBidPrice()) + (sharesPerMvmt * secondPrice) + (sharesPerMvmt * thirdPrice);
|
|
||||||
|
|
||||||
expect(res).to.equal(expected + commission);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should cap the 'shares' argument at the stock's maximum number of shares", function() {
|
it("should cap the 'shares' argument at the stock's maximum number of shares", function() {
|
||||||
const maxRes = getBuyTransactionCost(stock, stock.maxShares, PositionTypes.Long);
|
const maxRes = getBuyTransactionCost(stock, stock.maxShares, PositionTypes.Long);
|
||||||
const exceedRes = getBuyTransactionCost(stock, stock.maxShares * 10, PositionTypes.Long);
|
const exceedRes = getBuyTransactionCost(stock, stock.maxShares * 10, PositionTypes.Long);
|
||||||
@ -345,14 +425,14 @@ describe("Stock Market Tests", function() {
|
|||||||
expect(res).to.equal(null);
|
expect(res).to.equal(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should properly evaluate LONG transactions that doesn't trigger a price movement", function() {
|
it("should properly evaluate LONG transactionst", function() {
|
||||||
const shares = ctorParams.shareTxForMovement / 2;
|
const shares = ctorParams.shareTxForMovement / 2;
|
||||||
const res = getSellTransactionGain(stock, shares, PositionTypes.Long);
|
const res = getSellTransactionGain(stock, shares, PositionTypes.Long);
|
||||||
const expected = shares * stock.getBidPrice() - commission;
|
const expected = shares * stock.getBidPrice() - commission;
|
||||||
expect(res).to.equal(expected);
|
expect(res).to.equal(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should properly evaluate SHORT transactions that doesn't trigger a price movement", function() {
|
it("should properly evaluate SHORT transactions", function() {
|
||||||
// We need to set this property in order to calculate gains from short position
|
// We need to set this property in order to calculate gains from short position
|
||||||
stock.playerAvgShortPx = stock.price * 2;
|
stock.playerAvgShortPx = stock.price * 2;
|
||||||
|
|
||||||
@ -362,41 +442,6 @@ describe("Stock Market Tests", function() {
|
|||||||
expect(res).to.equal(expected);
|
expect(res).to.equal(expected);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should properly evaluate LONG transactions that trigger price movements", function() {
|
|
||||||
const sharesPerMvmt = ctorParams.shareTxForMovement;
|
|
||||||
const shares = sharesPerMvmt * 3;
|
|
||||||
const res = getSellTransactionGain(stock, shares, PositionTypes.Long);
|
|
||||||
|
|
||||||
// Calculated expected gain
|
|
||||||
const mvmt = calculateDecreasingPriceMovement(stock);
|
|
||||||
const secondPrice = stock.getBidPrice() * mvmt;
|
|
||||||
const thirdPrice = secondPrice * mvmt;
|
|
||||||
const expected = (sharesPerMvmt * stock.getBidPrice()) + (sharesPerMvmt * secondPrice) + (sharesPerMvmt * thirdPrice);
|
|
||||||
|
|
||||||
expect(res).to.equal(expected - commission);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should properly evaluate SHORT transactions that trigger price movements", function() {
|
|
||||||
// We need to set this property in order to calculate gains from short position
|
|
||||||
stock.playerAvgShortPx = stock.price * 2;
|
|
||||||
|
|
||||||
const sharesPerMvmt = ctorParams.shareTxForMovement;
|
|
||||||
const shares = sharesPerMvmt * 3;
|
|
||||||
const res = getSellTransactionGain(stock, shares, PositionTypes.Short);
|
|
||||||
|
|
||||||
// Calculate expected gain
|
|
||||||
const mvmt = calculateIncreasingPriceMovement(stock);
|
|
||||||
const secondPrice = stock.getAskPrice() * mvmt;
|
|
||||||
const thirdPrice = secondPrice * mvmt;
|
|
||||||
function getGainForPrice(thisPrice) {
|
|
||||||
const origCost = sharesPerMvmt * stock.playerAvgShortPx;
|
|
||||||
return origCost + ((stock.playerAvgShortPx - thisPrice) * sharesPerMvmt);
|
|
||||||
}
|
|
||||||
const expected = getGainForPrice(stock.getAskPrice()) + getGainForPrice(secondPrice) + getGainForPrice(thirdPrice);
|
|
||||||
|
|
||||||
expect(res).to.equal(expected - commission);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should cap the 'shares' argument at the stock's maximum number of shares", function() {
|
it("should cap the 'shares' argument at the stock's maximum number of shares", function() {
|
||||||
const maxRes = getSellTransactionGain(stock, stock.maxShares, PositionTypes.Long);
|
const maxRes = getSellTransactionGain(stock, stock.maxShares, PositionTypes.Long);
|
||||||
const exceedRes = getSellTransactionGain(stock, stock.maxShares * 10, PositionTypes.Long);
|
const exceedRes = getSellTransactionGain(stock, stock.maxShares * 10, PositionTypes.Long);
|
||||||
@ -405,307 +450,221 @@ describe("Stock Market Tests", function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Price Movement Processor Functions", function() {
|
describe("Forecast Movement Processor Function", function() {
|
||||||
// N = 1 is the original price
|
|
||||||
function getNthPriceIncreasing(origPrice, n) {
|
|
||||||
let price = origPrice;
|
|
||||||
for (let i = 1; i < n; ++i) {
|
|
||||||
price *= calculateIncreasingPriceMovement(stock);
|
|
||||||
}
|
|
||||||
|
|
||||||
return price;
|
|
||||||
}
|
|
||||||
|
|
||||||
// N = 1 is the original price
|
|
||||||
function getNthPriceDecreasing(origPrice, n) {
|
|
||||||
let price = origPrice;
|
|
||||||
for (let i = 1; i < n; ++i) {
|
|
||||||
price *= calculateDecreasingPriceMovement(stock);
|
|
||||||
}
|
|
||||||
|
|
||||||
return price;
|
|
||||||
}
|
|
||||||
|
|
||||||
// N = 1 is the original forecast
|
// N = 1 is the original forecast
|
||||||
function getNthForecast(origForecast, n) {
|
function getNthForecast(origForecast, n) {
|
||||||
return origForecast - forecastChangePerPriceMovement * (n - 1);
|
return origForecast - forecastChangePerPriceMovement * (n - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("processBuyTransactionPriceMovement()", function() {
|
describe("processTransactionForecastMovement() for buy transactions", function() {
|
||||||
const noMvmtShares = Math.round(ctorParams.shareTxForMovement / 2.2);
|
const noMvmtShares = Math.round(ctorParams.shareTxForMovement / 2.2);
|
||||||
const mvmtShares = ctorParams.shareTxForMovement * 3 + noMvmtShares;
|
const mvmtShares = ctorParams.shareTxForMovement * 3 + noMvmtShares;
|
||||||
|
|
||||||
it("should do nothing on invalid 'stock' argument", function() {
|
it("should do nothing on invalid 'stock' argument", function() {
|
||||||
const oldPrice = stock.price;
|
const oldTracker = stock.shareTxUntilMovement;
|
||||||
const oldTracker = stock.shareTxUntilMovementUp;
|
|
||||||
|
|
||||||
processBuyTransactionPriceMovement({}, mvmtShares, PositionTypes.Long);
|
processTransactionForecastMovement({}, mvmtShares, PositionTypes.Long);
|
||||||
expect(stock.price).to.equal(oldPrice);
|
expect(stock.shareTxUntilMovement).to.equal(oldTracker);
|
||||||
expect(stock.shareTxUntilMovementUp).to.equal(oldTracker);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should do nothing on invalid 'shares' arg", function() {
|
it("should do nothing on invalid 'shares' arg", function() {
|
||||||
const oldPrice = stock.price;
|
const oldTracker = stock.shareTxUntilMovement;
|
||||||
const oldTracker = stock.shareTxUntilMovementUp;
|
|
||||||
|
|
||||||
processBuyTransactionPriceMovement(stock, NaN, PositionTypes.Long);
|
processTransactionForecastMovement(stock, NaN, PositionTypes.Long);
|
||||||
expect(stock.price).to.equal(oldPrice);
|
expect(stock.shareTxUntilMovement).to.equal(oldTracker);
|
||||||
expect(stock.shareTxUntilMovementUp).to.equal(oldTracker);
|
|
||||||
|
|
||||||
processBuyTransactionPriceMovement(stock, -1, PositionTypes.Long);
|
processTransactionForecastMovement(stock, -1, PositionTypes.Long);
|
||||||
expect(stock.price).to.equal(oldPrice);
|
expect(stock.shareTxUntilMovement).to.equal(oldTracker);
|
||||||
expect(stock.shareTxUntilMovementUp).to.equal(oldTracker);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should properly evaluate a LONG transaction that doesn't trigger a price movement", function() {
|
it("should properly evaluate a LONG transaction that doesn't trigger a forecast movement", function() {
|
||||||
const oldPrice = stock.price;
|
|
||||||
const oldForecast = stock.otlkMag;
|
const oldForecast = stock.otlkMag;
|
||||||
|
|
||||||
processBuyTransactionPriceMovement(stock, noMvmtShares, PositionTypes.Long);
|
processTransactionForecastMovement(stock, noMvmtShares, PositionTypes.Long);
|
||||||
expect(stock.price).to.equal(oldPrice);
|
|
||||||
expect(stock.otlkMag).to.equal(oldForecast);
|
expect(stock.otlkMag).to.equal(oldForecast);
|
||||||
expect(stock.shareTxUntilMovementUp).to.equal(stock.shareTxForMovement - noMvmtShares);
|
expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement - noMvmtShares);
|
||||||
expect(stock.shareTxUntilMovementDown).to.equal(stock.shareTxForMovement);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should properly evaluate a SHORT transaction that doesn't trigger a price movement", function() {
|
it("should properly evaluate a SHORT transaction that doesn't trigger a forecast movement", function() {
|
||||||
const oldPrice = stock.price;
|
|
||||||
const oldForecast = stock.otlkMag;
|
const oldForecast = stock.otlkMag;
|
||||||
|
|
||||||
processBuyTransactionPriceMovement(stock, noMvmtShares, PositionTypes.Short);
|
processTransactionForecastMovement(stock, noMvmtShares, PositionTypes.Short);
|
||||||
expect(stock.price).to.equal(oldPrice);
|
|
||||||
expect(stock.otlkMag).to.equal(oldForecast);
|
expect(stock.otlkMag).to.equal(oldForecast);
|
||||||
expect(stock.shareTxUntilMovementDown).to.equal(stock.shareTxForMovement - noMvmtShares);
|
expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement - noMvmtShares);
|
||||||
expect(stock.shareTxUntilMovementUp).to.equal(stock.shareTxForMovement);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should properly evaluate LONG transactions that trigger price movements", function() {
|
it("should properly evaluate LONG transactions that triggers forecast movements", function() {
|
||||||
const oldPrice = stock.price;
|
|
||||||
const oldForecast = stock.otlkMag;
|
const oldForecast = stock.otlkMag;
|
||||||
|
|
||||||
processBuyTransactionPriceMovement(stock, mvmtShares, PositionTypes.Long);
|
processTransactionForecastMovement(stock, mvmtShares, PositionTypes.Long);
|
||||||
expect(stock.price).to.equal(getNthPriceIncreasing(oldPrice, 4));
|
|
||||||
expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4));
|
expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4));
|
||||||
expect(stock.shareTxUntilMovementUp).to.equal(stock.shareTxForMovement - noMvmtShares);
|
expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement - noMvmtShares);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should properly evaluate SHORT transactions that trigger price movements", function() {
|
it("should properly evaluate SHORT transactions that triggers forecast movements", function() {
|
||||||
const oldPrice = stock.price;
|
|
||||||
const oldForecast = stock.otlkMag;
|
const oldForecast = stock.otlkMag;
|
||||||
|
|
||||||
processBuyTransactionPriceMovement(stock, mvmtShares, PositionTypes.Short);
|
processTransactionForecastMovement(stock, mvmtShares, PositionTypes.Short);
|
||||||
expect(stock.price).to.equal(getNthPriceDecreasing(oldPrice, 4));
|
|
||||||
expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4));
|
expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4));
|
||||||
expect(stock.shareTxUntilMovementDown).to.equal(stock.shareTxForMovement - noMvmtShares);
|
expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement - noMvmtShares);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should properly evaluate LONG transactions of exactly 'shareTxForMovement' shares", function() {
|
it("should properly evaluate LONG transactions of exactly 'shareTxForMovement' shares", function() {
|
||||||
const oldPrice = stock.price;
|
|
||||||
const oldForecast = stock.otlkMag;
|
const oldForecast = stock.otlkMag;
|
||||||
|
|
||||||
processBuyTransactionPriceMovement(stock, stock.shareTxForMovement, PositionTypes.Long);
|
processTransactionForecastMovement(stock, stock.shareTxForMovement, PositionTypes.Long);
|
||||||
expect(stock.price).to.equal(getNthPriceIncreasing(oldPrice, 2));
|
|
||||||
expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2));
|
expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2));
|
||||||
expect(stock.shareTxUntilMovementUp).to.equal(stock.shareTxForMovement);
|
expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement);
|
||||||
expect(stock.shareTxUntilMovementDown).to.equal(stock.shareTxForMovement);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should properly evaluate LONG transactions that total to 'shareTxForMovement' shares", function() {
|
it("should properly evaluate LONG transactions that total to 'shareTxForMovement' shares", function() {
|
||||||
const oldPrice = stock.price;
|
|
||||||
const oldForecast = stock.otlkMag;
|
const oldForecast = stock.otlkMag;
|
||||||
|
|
||||||
processBuyTransactionPriceMovement(stock, Math.round(stock.shareTxForMovement / 2), PositionTypes.Long);
|
processTransactionForecastMovement(stock, Math.round(stock.shareTxForMovement / 2), PositionTypes.Long);
|
||||||
processBuyTransactionPriceMovement(stock, stock.shareTxUntilMovementUp, PositionTypes.Long);
|
expect(stock.shareTxUntilMovement).to.be.below(stock.shareTxForMovement);
|
||||||
expect(stock.price).to.equal(getNthPriceIncreasing(oldPrice, 2));
|
processTransactionForecastMovement(stock, stock.shareTxUntilMovement, PositionTypes.Long);
|
||||||
expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2));
|
expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2));
|
||||||
expect(stock.shareTxUntilMovementUp).to.equal(stock.shareTxForMovement);
|
expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement);
|
||||||
expect(stock.shareTxUntilMovementDown).to.equal(stock.shareTxForMovement);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should properly evaluate LONG transactions that are a multiple of 'shareTxForMovement' shares", function() {
|
it("should properly evaluate LONG transactions that are a multiple of 'shareTxForMovement' shares", function() {
|
||||||
const oldPrice = stock.price;
|
|
||||||
const oldForecast = stock.otlkMag;
|
const oldForecast = stock.otlkMag;
|
||||||
|
|
||||||
processBuyTransactionPriceMovement(stock, 3 * stock.shareTxForMovement, PositionTypes.Long);
|
processTransactionForecastMovement(stock, 3 * stock.shareTxForMovement, PositionTypes.Long);
|
||||||
expect(stock.price).to.equal(getNthPriceIncreasing(oldPrice, 4));
|
|
||||||
expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4));
|
expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4));
|
||||||
expect(stock.shareTxUntilMovementUp).to.equal(stock.shareTxForMovement);
|
expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement);
|
||||||
expect(stock.shareTxUntilMovementDown).to.equal(stock.shareTxForMovement);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should properly evaluate SHORT transactions of exactly 'shareTxForMovement' shares", function() {
|
it("should properly evaluate SHORT transactions of exactly 'shareTxForMovement' shares", function() {
|
||||||
const oldPrice = stock.price;
|
|
||||||
const oldForecast = stock.otlkMag;
|
const oldForecast = stock.otlkMag;
|
||||||
|
|
||||||
processBuyTransactionPriceMovement(stock, stock.shareTxForMovement, PositionTypes.Short);
|
processTransactionForecastMovement(stock, stock.shareTxForMovement, PositionTypes.Short);
|
||||||
expect(stock.price).to.equal(getNthPriceDecreasing(oldPrice, 2));
|
|
||||||
expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2));
|
expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2));
|
||||||
expect(stock.shareTxUntilMovementDown).to.equal(stock.shareTxForMovement);
|
expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement);
|
||||||
expect(stock.shareTxUntilMovementUp).to.equal(stock.shareTxForMovement);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should properly evaluate SHORT transactions that total to 'shareTxForMovement' shares", function() {
|
it("should properly evaluate SHORT transactions that total to 'shareTxForMovement' shares", function() {
|
||||||
const oldPrice = stock.price;
|
|
||||||
const oldForecast = stock.otlkMag;
|
const oldForecast = stock.otlkMag;
|
||||||
|
|
||||||
processBuyTransactionPriceMovement(stock, Math.round(stock.shareTxForMovement / 2), PositionTypes.Short);
|
processTransactionForecastMovement(stock, Math.round(stock.shareTxForMovement / 2), PositionTypes.Short);
|
||||||
expect(stock.shareTxUntilMovementDown).to.be.below(stock.shareTxForMovement);
|
expect(stock.shareTxUntilMovement).to.be.below(stock.shareTxForMovement);
|
||||||
processBuyTransactionPriceMovement(stock, stock.shareTxUntilMovementDown, PositionTypes.Short);
|
processTransactionForecastMovement(stock, stock.shareTxUntilMovement, PositionTypes.Short);
|
||||||
expect(stock.price).to.equal(getNthPriceDecreasing(oldPrice, 2));
|
|
||||||
expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2));
|
expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2));
|
||||||
expect(stock.shareTxUntilMovementDown).to.equal(stock.shareTxForMovement);
|
expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should properly evaluate SHORT transactions that are a multiple of 'shareTxForMovement' shares", function() {
|
it("should properly evaluate SHORT transactions that are a multiple of 'shareTxForMovement' shares", function() {
|
||||||
const oldPrice = stock.price;
|
|
||||||
const oldForecast = stock.otlkMag;
|
const oldForecast = stock.otlkMag;
|
||||||
|
|
||||||
processBuyTransactionPriceMovement(stock, 3 * stock.shareTxForMovement, PositionTypes.Short);
|
processTransactionForecastMovement(stock, 3 * stock.shareTxForMovement, PositionTypes.Short);
|
||||||
expect(stock.price).to.equal(getNthPriceDecreasing(oldPrice, 4));
|
|
||||||
expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4));
|
expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4));
|
||||||
expect(stock.shareTxUntilMovementDown).to.equal(stock.shareTxForMovement);
|
expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("processSellTransactionPriceMovement()", function() {
|
describe("processTransactionForecastMovement() for sell transactions", function() {
|
||||||
const noMvmtShares = Math.round(ctorParams.shareTxForMovement / 2.2);
|
const noMvmtShares = Math.round(ctorParams.shareTxForMovement / 2.2);
|
||||||
const mvmtShares = ctorParams.shareTxForMovement * 3 + noMvmtShares;
|
const mvmtShares = ctorParams.shareTxForMovement * 3 + noMvmtShares;
|
||||||
|
|
||||||
it("should do nothing on invalid 'stock' argument", function() {
|
it("should do nothing on invalid 'stock' argument", function() {
|
||||||
const oldPrice = stock.price;
|
const oldTracker = stock.shareTxUntilMovement;
|
||||||
const oldTracker = stock.shareTxUntilMovementDown;
|
|
||||||
|
|
||||||
processSellTransactionPriceMovement({}, mvmtShares, PositionTypes.Long);
|
processTransactionForecastMovement({}, mvmtShares, PositionTypes.Long);
|
||||||
expect(stock.price).to.equal(oldPrice);
|
expect(stock.shareTxUntilMovement).to.equal(oldTracker);
|
||||||
expect(stock.shareTxUntilMovementDown).to.equal(oldTracker);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should do nothing on invalid 'shares' arg", function() {
|
it("should do nothing on invalid 'shares' arg", function() {
|
||||||
const oldPrice = stock.price;
|
const oldTracker = stock.shareTxUntilMovement;
|
||||||
const oldTracker = stock.shareTxUntilMovementDown;
|
|
||||||
|
|
||||||
processSellTransactionPriceMovement(stock, NaN, PositionTypes.Long);
|
processTransactionForecastMovement(stock, NaN, PositionTypes.Long);
|
||||||
expect(stock.price).to.equal(oldPrice);
|
expect(stock.shareTxUntilMovement).to.equal(oldTracker);
|
||||||
expect(stock.shareTxUntilMovementDown).to.equal(oldTracker);
|
|
||||||
|
|
||||||
processSellTransactionPriceMovement(stock, -1, PositionTypes.Long);
|
processTransactionForecastMovement(stock, -1, PositionTypes.Long);
|
||||||
expect(stock.price).to.equal(oldPrice);
|
expect(stock.shareTxUntilMovement).to.equal(oldTracker);
|
||||||
expect(stock.shareTxUntilMovementDown).to.equal(oldTracker);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should properly evaluate a LONG transaction that doesn't trigger a price movement", function() {
|
it("should properly evaluate a LONG transaction that doesn't trigger a price movement", function() {
|
||||||
const oldPrice = stock.price;
|
|
||||||
const oldForecast = stock.otlkMag;
|
const oldForecast = stock.otlkMag;
|
||||||
|
|
||||||
processSellTransactionPriceMovement(stock, noMvmtShares, PositionTypes.Long);
|
processTransactionForecastMovement(stock, noMvmtShares, PositionTypes.Long);
|
||||||
expect(stock.price).to.equal(oldPrice);
|
|
||||||
expect(stock.otlkMag).to.equal(oldForecast);
|
expect(stock.otlkMag).to.equal(oldForecast);
|
||||||
expect(stock.shareTxUntilMovementDown).to.equal(stock.shareTxForMovement - noMvmtShares);
|
expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement - noMvmtShares);
|
||||||
expect(stock.shareTxUntilMovementUp).to.equal(stock.shareTxForMovement);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should properly evaluate a SHORT transaction that doesn't trigger a price movement", function() {
|
it("should properly evaluate a SHORT transaction that doesn't trigger a price movement", function() {
|
||||||
const oldPrice = stock.price;
|
|
||||||
const oldForecast = stock.otlkMag;
|
const oldForecast = stock.otlkMag;
|
||||||
|
|
||||||
processSellTransactionPriceMovement(stock, noMvmtShares, PositionTypes.Short);
|
processTransactionForecastMovement(stock, noMvmtShares, PositionTypes.Short);
|
||||||
expect(stock.price).to.equal(oldPrice);
|
|
||||||
expect(stock.otlkMag).to.equal(oldForecast);
|
expect(stock.otlkMag).to.equal(oldForecast);
|
||||||
expect(stock.shareTxUntilMovementUp).to.equal(stock.shareTxForMovement - noMvmtShares);
|
expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement - noMvmtShares);
|
||||||
expect(stock.shareTxUntilMovementDown).to.equal(stock.shareTxForMovement);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should properly evaluate LONG transactions that trigger price movements", function() {
|
it("should properly evaluate LONG transactions that trigger price movements", function() {
|
||||||
const oldPrice = stock.price;
|
|
||||||
const oldForecast = stock.otlkMag;
|
const oldForecast = stock.otlkMag;
|
||||||
|
|
||||||
processSellTransactionPriceMovement(stock, mvmtShares, PositionTypes.Long);
|
processTransactionForecastMovement(stock, mvmtShares, PositionTypes.Long);
|
||||||
expect(stock.price).to.equal(getNthPriceDecreasing(oldPrice, 4));
|
|
||||||
expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4));
|
expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4));
|
||||||
expect(stock.shareTxUntilMovementDown).to.equal(stock.shareTxForMovement - noMvmtShares);
|
expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement - noMvmtShares);
|
||||||
expect(stock.shareTxUntilMovementUp).to.equal(stock.shareTxForMovement);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should properly evaluate SHORT transactions that trigger price movements", function() {
|
it("should properly evaluate SHORT transactions that trigger price movements", function() {
|
||||||
const oldPrice = stock.price;
|
|
||||||
const oldForecast = stock.otlkMag;
|
const oldForecast = stock.otlkMag;
|
||||||
|
|
||||||
processSellTransactionPriceMovement(stock, mvmtShares, PositionTypes.Short);
|
processTransactionForecastMovement(stock, mvmtShares, PositionTypes.Short);
|
||||||
expect(stock.price).to.equal(getNthPriceIncreasing(oldPrice, 4));
|
|
||||||
expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4));
|
expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4));
|
||||||
expect(stock.shareTxUntilMovementUp).to.equal(stock.shareTxForMovement - noMvmtShares);
|
expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement - noMvmtShares);
|
||||||
expect(stock.shareTxUntilMovementDown).to.equal(stock.shareTxForMovement);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should properly evaluate LONG transactions of exactly 'shareTxForMovement' shares", function() {
|
it("should properly evaluate LONG transactions of exactly 'shareTxForMovement' shares", function() {
|
||||||
const oldPrice = stock.price;
|
|
||||||
const oldForecast = stock.otlkMag;
|
const oldForecast = stock.otlkMag;
|
||||||
|
|
||||||
processSellTransactionPriceMovement(stock, stock.shareTxForMovement, PositionTypes.Long);
|
processTransactionForecastMovement(stock, stock.shareTxForMovement, PositionTypes.Long);
|
||||||
expect(stock.price).to.equal(getNthPriceDecreasing(oldPrice, 2));
|
|
||||||
expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2));
|
expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2));
|
||||||
expect(stock.shareTxUntilMovementDown).to.equal(stock.shareTxForMovement);
|
expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement);
|
||||||
expect(stock.shareTxUntilMovementUp).to.equal(stock.shareTxForMovement);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should properly evaluate LONG transactions that total to 'shareTxForMovement' shares", function() {
|
it("should properly evaluate LONG transactions that total to 'shareTxForMovement' shares", function() {
|
||||||
const oldPrice = stock.price;
|
|
||||||
const oldForecast = stock.otlkMag;
|
const oldForecast = stock.otlkMag;
|
||||||
|
|
||||||
processSellTransactionPriceMovement(stock, Math.round(stock.shareTxForMovement / 2), PositionTypes.Long);
|
processTransactionForecastMovement(stock, Math.round(stock.shareTxForMovement / 2), PositionTypes.Long);
|
||||||
expect(stock.shareTxUntilMovementDown).to.be.below(stock.shareTxForMovement);
|
expect(stock.shareTxUntilMovement).to.be.below(stock.shareTxForMovement);
|
||||||
processSellTransactionPriceMovement(stock, stock.shareTxUntilMovementDown, PositionTypes.Long);
|
processTransactionForecastMovement(stock, stock.shareTxUntilMovement, PositionTypes.Long);
|
||||||
expect(stock.price).to.equal(getNthPriceDecreasing(oldPrice, 2));
|
|
||||||
expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2));
|
expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2));
|
||||||
expect(stock.shareTxUntilMovementDown).to.equal(stock.shareTxForMovement);
|
expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement);
|
||||||
expect(stock.shareTxUntilMovementUp).to.equal(stock.shareTxForMovement);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should properly evaluate LONG transactions that are a multiple of 'shareTxForMovement' shares", function() {
|
it("should properly evaluate LONG transactions that are a multiple of 'shareTxForMovement' shares", function() {
|
||||||
const oldPrice = stock.price;
|
|
||||||
const oldForecast = stock.otlkMag;
|
const oldForecast = stock.otlkMag;
|
||||||
|
|
||||||
processSellTransactionPriceMovement(stock, 3 * stock.shareTxForMovement, PositionTypes.Long);
|
processTransactionForecastMovement(stock, 3 * stock.shareTxForMovement, PositionTypes.Long);
|
||||||
expect(stock.price).to.equal(getNthPriceDecreasing(oldPrice, 4));
|
|
||||||
expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4));
|
expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4));
|
||||||
expect(stock.shareTxUntilMovementDown).to.equal(stock.shareTxForMovement);
|
expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement);
|
||||||
expect(stock.shareTxUntilMovementUp).to.equal(stock.shareTxForMovement);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should properly evaluate SHORT transactions of exactly 'shareTxForMovement' shares", function() {
|
it("should properly evaluate SHORT transactions of exactly 'shareTxForMovement' shares", function() {
|
||||||
const oldPrice = stock.price;
|
|
||||||
const oldForecast = stock.otlkMag;
|
const oldForecast = stock.otlkMag;
|
||||||
|
|
||||||
processSellTransactionPriceMovement(stock, stock.shareTxForMovement, PositionTypes.Short);
|
processTransactionForecastMovement(stock, stock.shareTxForMovement, PositionTypes.Short);
|
||||||
expect(stock.price).to.equal(getNthPriceIncreasing(oldPrice, 2));
|
|
||||||
expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2));
|
expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2));
|
||||||
expect(stock.shareTxUntilMovementUp).to.equal(stock.shareTxForMovement);
|
expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement);
|
||||||
expect(stock.shareTxUntilMovementDown).to.equal(stock.shareTxForMovement);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should properly evaluate SHORT transactions that total to 'shareTxForMovement' shares", function() {
|
it("should properly evaluate SHORT transactions that total to 'shareTxForMovement' shares", function() {
|
||||||
const oldPrice = stock.price;
|
|
||||||
const oldForecast = stock.otlkMag;
|
const oldForecast = stock.otlkMag;
|
||||||
|
|
||||||
processSellTransactionPriceMovement(stock, Math.round(stock.shareTxForMovement / 2), PositionTypes.Short);
|
processTransactionForecastMovement(stock, Math.round(stock.shareTxForMovement / 2), PositionTypes.Short);
|
||||||
expect(stock.shareTxUntilMovementUp).to.be.below(stock.shareTxForMovement);
|
expect(stock.shareTxUntilMovement).to.be.below(stock.shareTxForMovement);
|
||||||
expect(stock.shareTxUntilMovementDown).to.equal(stock.shareTxForMovement);
|
processTransactionForecastMovement(stock, stock.shareTxUntilMovement, PositionTypes.Short);
|
||||||
processSellTransactionPriceMovement(stock, stock.shareTxUntilMovementUp, PositionTypes.Short);
|
|
||||||
expect(stock.price).to.equal(getNthPriceIncreasing(oldPrice, 2));
|
|
||||||
expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2));
|
expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 2));
|
||||||
expect(stock.shareTxUntilMovementUp).to.equal(stock.shareTxForMovement);
|
expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement);
|
||||||
expect(stock.shareTxUntilMovementDown).to.equal(stock.shareTxForMovement);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should properly evaluate SHORT transactions that are a multiple of 'shareTxForMovement' shares", function() {
|
it("should properly evaluate SHORT transactions that are a multiple of 'shareTxForMovement' shares", function() {
|
||||||
const oldPrice = stock.price;
|
|
||||||
const oldForecast = stock.otlkMag;
|
const oldForecast = stock.otlkMag;
|
||||||
|
|
||||||
processSellTransactionPriceMovement(stock, 3 * stock.shareTxForMovement, PositionTypes.Short);
|
processTransactionForecastMovement(stock, 3 * stock.shareTxForMovement, PositionTypes.Short);
|
||||||
expect(stock.price).to.equal(getNthPriceIncreasing(oldPrice, 4));
|
|
||||||
expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4));
|
expect(stock.otlkMag).to.equal(getNthForecast(oldForecast, 4));
|
||||||
expect(stock.shareTxUntilMovementUp).to.equal(stock.shareTxForMovement);
|
expect(stock.shareTxUntilMovement).to.equal(stock.shareTxForMovement);
|
||||||
expect(stock.shareTxUntilMovementDown).to.equal(stock.shareTxForMovement);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user