Advertisement
Guest User

StockMaster.ns

a guest
Jan 5th, 2022
1,422
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. //Requires access to the TIX API and the 4S Mkt Data API
  2.  
  3. let fracL = 0.1;     //Fraction of assets to keep as cash in hand
  4. let fracH = 0.15; //
  5. let commission = 100000; //Buy or sell commission
  6. let numCycles = 2;   //Each cycle is 5 seconds
  7. let totalProfit = 0.0;
  8. let totalLosses = 0.0
  9.  
  10. /**
  11.  * Refreshes stock data
  12.  * @param {NS} ns Game namespace
  13.  * @param {[]} stocks Stocks to be analyzed
  14.  * @param {[]} myStocks Owned stocks
  15.  */
  16. function refresh(ns, stocks, myStocks) {
  17.     let cashToSpend = ns.getServerMoneyAvailable("home");
  18.     //corpus stores equity (cash + value of all stocks)
  19.     let corpus = cashToSpend;
  20.     myStocks.length = 0;
  21.  
  22.     //refresh stock data
  23.     for (let i = 0; i < stocks.length; i++) {
  24.         let sym = stocks[i].sym;
  25.         stocks[i].price = ns.stock.getAskPrice(sym);
  26.         stocks[i].maxShares = ns.stock.getMaxShares(sym);
  27.         stocks[i].shares = ns.stock.getPosition(sym)[0];
  28.         stocks[i].buyPrice = ns.stock.getPosition(sym)[1];
  29.         stocks[i].vol = ns.stock.getVolatility(sym);
  30.         //calculate probability of stock price gain/loss
  31.         stocks[i].prob = 2 * (ns.stock.getForecast(sym) - 0.5);
  32.         //calculate expected return
  33.         stocks[i].expRet = stocks[i].vol * stocks[i].prob / 2;
  34.         //add stock equity to corpus
  35.         corpus += stocks[i].price * stocks[i].shares;
  36.  
  37.         //store owned stocks
  38.         if (stocks[i].shares > 0) {
  39.             myStocks.push(stocks[i]);
  40.         }
  41.     }
  42.  
  43.     //prunes low-cost stocks that are inefficient
  44.     stocks.forEach(function (_, i) {
  45.         //calculate max purchase cost
  46.         let cost = stocks[i].price * (stocks[i].maxShares - stocks[i].shares);
  47.  
  48.         //if cost < 1.5% of cash and is unowned
  49.         //then the max purchase for this stock will
  50.         //yield very little profit due to its low
  51.         //stock cost or max shares
  52.         if (cost < 0.015 * cashToSpend && stocks[i].shares == 0) {
  53.             ns.print(`Removed ${stocks[i].sym} from stocks  Max cost: ${format(cost, true)}`);
  54.             stocks.splice(i, 1);
  55.             //remove added equity if stock is pruned
  56.             corpus -= cost;
  57.         }
  58.     });
  59.  
  60.     //order stocks by profitability %
  61.     stocks.sort(function (a, b) { return b.expRet - a.expRet });
  62.  
  63.     return corpus;
  64. }
  65.  
  66. /**
  67.  * Buys stock
  68.  * @param {NS} ns Game namespace
  69.  * @param {[]} stock Stock to buy
  70.  * @param {number} numShares Number of shares to buy
  71. */
  72. function buy(ns, stock, numShares) {
  73.     let actualPrice = ns.stock.buy(stock.sym, numShares);
  74.     ns.print(`${actualPrice > 0 ? "Bought" : "Failed to buy"} ${format(numShares, false)} shares of ${stock.sym} for ${format(numShares * stock.price, true)}`);
  75. }
  76.  
  77. /**
  78.  * Sells stock
  79.  * @param {NS} ns Game namespace
  80.  * @param {[]} stock Stock to sell
  81.  * @param {number} numShares Number of shares to sell
  82. */
  83. function sell(ns, stock, numShares) {
  84.     let profit = numShares * (stock.price - stock.buyPrice) - 2 * commission;
  85.  
  86.     if (ns.stock.sell(stock.sym, numShares) > 0) {
  87.         let message = `Sold   ${format(numShares, false)} shares of ${stock.sym} for profit of ${format(profit, true)}`;
  88.         ns.print(message);
  89.  
  90.         //tally gains/losses for efficiency calculation
  91.         if (profit > 0) {
  92.             totalProfit += profit;
  93.             ns.toast(message);
  94.         }
  95.         else { totalLosses += profit; }
  96.     }
  97.     else {
  98.         ns.print(`Failed to sell ${format(numShares, false)} shares of ${stock.sym} for profit of ${format(profit, true)}`);
  99.     }
  100.  
  101. }
  102.  
  103. /**
  104.  * Formats big numbers into abbreviated versions
  105.  * @param {number} num Number to format
  106.  * @param {boolean} isMonetary Boolean representation
  107.  * if `num` represents a monetary numerical value or not
  108. */
  109. function format(num, isMonetary) {
  110.     //define suffixes
  111.     let symbols = ["", "K", "M", "B", "T", "Qa", "Qi", "Sx", "Sp", "Oc"];
  112.     let i = 0;
  113.     //determine correct suffix
  114.     for (; (Math.abs(num) >= 1000) && (i < symbols.length); i++) num /= 1000;
  115.     //return formatted number
  116.     return ((Math.sign(num) < 0) ? "-" : "") + (isMonetary ? "$" : "") + Math.abs(num).toFixed(3) + symbols[i];
  117. }
  118.  
  119. /**
  120.  * Program entry point
  121.  * @param {NS} ns Game namespace
  122.  * */
  123. export async function main(ns) {
  124.     //Initialise
  125.     ns.disableLog("ALL");
  126.     let stocks = [];
  127.     let myStocks = [];
  128.     let corpus = 0;
  129.     let symbols = ns.stock.getSymbols();
  130.  
  131.     //populate stock list and add stock symbols
  132.     for (let i = 0; i < symbols.length; i++) {
  133.         let sym = symbols[i];
  134.         stocks.push({ sym: sym });
  135.     }
  136.  
  137.     while (true) {
  138.         //refresh equity at beginning of loop
  139.         corpus = refresh(ns, stocks, myStocks);
  140.  
  141.         //Sell underperforming shares
  142.         for (let i = 0; i < myStocks.length; i++) {
  143.             //if a stock with a higher expected return is found,
  144.             //sell shares of stocks with lower expected returns
  145.             if (stocks[0].expRet > myStocks[i].expRet) {
  146.                 sell(ns, myStocks[i], myStocks[i].shares);
  147.                 corpus -= commission;
  148.             }
  149.         }
  150.  
  151.         //Sell shares if not enough cash in hand
  152.         for (let i = 0; i < myStocks.length; i++) {
  153.             if (ns.getServerMoneyAvailable("home") < (fracL * corpus)) {
  154.                 //calculate cash needed to replenish cash in hand
  155.                 let cashNeeded = (corpus * fracH - ns.getServerMoneyAvailable("home") + commission);
  156.                 //calculate number of shares that need to be sold
  157.                 let numShares = Math.floor(cashNeeded / myStocks[i].price);
  158.                 //sell and remove commission from equity
  159.                 sell(ns, myStocks[i], numShares);
  160.                 corpus -= commission;
  161.             }
  162.         }
  163.  
  164.         //Buy shares with cash remaining in hand
  165.  
  166.         let cashToSpend = ns.getServerMoneyAvailable("home") - (fracH * corpus);
  167.         //calculate max number of shares available
  168.         let maxSharesAvailable = stocks[0].maxShares - stocks[0].shares;
  169.         //calculate max number of shares to buy
  170.         let numShares = Math.min(Math.floor((cashToSpend - commission) / stocks[0].price), maxSharesAvailable);
  171.         //buy additional shares if deemed profitable
  172.         if ((numShares * stocks[0].expRet * stocks[0].price * numCycles) > commission) {
  173.             buy(ns, stocks[0], numShares);
  174.         }
  175.  
  176.         //calculate and display efficiency and profits/losses
  177.         let efficiency = totalProfit / (totalProfit - totalLosses);
  178.         if(isNaN(efficiency)) { efficiency = 0.0; }
  179.         ns.print(`Efficiency: ${format(efficiency * 100)}%`);
  180.         ns.print(`Profits: ${format(totalProfit, true)} Losses: ${format(totalLosses, true)}`)
  181.  
  182.         await ns.sleep(5 * 1000 * numCycles + 200);
  183.     }
  184. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement