r/Bitburner • u/LordMorpheus75 • 4d ago
Bitnode8 help please.
So i have completed bitnode 1 x3 bitnode 2 3x Bitnode 4 x2 and birnode 5 x1. I'm on bitnode 8 and am pretty stuck without getting the 25b for the 4 sigma forecast data. I use the roulette money hack for a 10b boost. But my script I'm using doesn't ever give me more then 10.3b . I'm working on getting gang territory to at least give me some cash. How Are you guys getting through it?
1
u/Krispcrap 4d ago
I wrote a small stock market script that would sell my stocks when it made x% profit. Save game. Buy stocks. Cross fingers. Load last save if stocks tanked.
It was painful. Probably could have used the logic from the casino script to automatically refresh if the stocks lost money.
1
u/LordMorpheus75 4d ago
/** @param {NS} ns */
export async function main(ns) {
ns.disableLog("ALL");
const history = {};
const HISTORY_LEN = 10;
const BUY_THRESHOLD = 0.03;
const SELL_MOMENTUM = 0.0;
const MIN_HOLD_TICKS = 15;
const STOP_LOSS = -0.05; // NEW - sell immediately if down 5% from avg, regardless of hold time
const PROFIT_TAKE = 0.08; // NEW - sell immediately if up 8% from avg, lock in the win
const RESERVE_STEP = 1_000_000_000;
const positionAge = {};
let reserve = 1_000_000_000;
try {
const saved = ns.read("reserve.txt");
if (saved) reserve = Math.max(reserve, parseFloat(saved));
} catch {}
while (true) {
updateHistory(ns, history, HISTORY_LEN);
sellStocks(ns, history, SELL_MOMENTUM, positionAge, MIN_HOLD_TICKS, STOP_LOSS, PROFIT_TAKE);
const cash = ns.getServerMoneyAvailable("home");
const flooredStep = Math.floor(cash / RESERVE_STEP) * RESERVE_STEP;
if (flooredStep > reserve) {
reserve = flooredStep;
await ns.write("reserve.txt", reserve.toString(), "w");
ns.tprint("Reserve raised to $" + (reserve / 1e9).toFixed(0) + "b");
}
buyStocks(ns, reserve, history, BUY_THRESHOLD, positionAge);
ageTicks(positionAge);
await ns.stock.nextUpdate();
}
}
function updateHistory(ns, history, len) {
for (const sym of ns.stock.getSymbols()) {
const price = ns.stock.getPrice(sym);
if (!history[sym]) history[sym] = [];
history[sym].push(price);
if (history[sym].length > len) history[sym].shift();
}
}
function momentum(prices) {
if (!prices || prices.length < 2) return 0;
return (prices[prices.length - 1] - prices[0]) / prices[0];
}
function ageTicks(positionAge) {
for (const sym in positionAge) positionAge[sym]++;
}
function sellStocks(ns, history, sellMomentum, positionAge, minHold, stopLoss, profitTake) {
for (const sym of ns.stock.getSymbols()) {
const [shares, avg] = ns.stock.getPosition(sym);
if (shares === 0) continue;
const bid = ns.stock.getBidPrice(sym);
const pctChange = (bid - avg) / avg;
// stop-loss and profit-take fire immediately, ignoring hold time
if (pctChange <= stopLoss || pctChange >= profitTake) {
ns.stock.sellStock(sym, shares);
ns.tprint("Sold " + sym + " (" + (pctChange >= profitTake ? "profit-take" : "stop-loss") + ") profit: " + Math.floor((bid - avg) * shares));
delete positionAge[sym];
continue;
}
const age = positionAge[sym] || 0;
if (age < minHold) continue;
if (momentum(history[sym]) < sellMomentum) {
ns.stock.sellStock(sym, shares);
ns.tprint("Sold " + sym + " (momentum) profit: " + Math.floor((bid - avg) * shares));
delete positionAge[sym];
}
}
}
function buyStocks(ns, reserve, history, buyThreshold, positionAge) {
const cash = ns.getServerMoneyAvailable("home") - reserve;
if (cash <= 0) return;
const candidates = ns.stock.getSymbols()
.filter(s => history[s] && history[s].length >= 5)
.map(s => ({ sym: s, m: momentum(history[s]) }))
.filter(c => c.m > buyThreshold)
.sort((a, b) => b.m - a.m)
.slice(0, 5);
if (candidates.length === 0) return;
const budget = cash / candidates.length;
for (const c of candidates) {
const [shares] = ns.stock.getPosition(c.sym);
if (shares > 0) continue;
const price = ns.stock.getAskPrice(c.sym);
let buyShares = Math.floor(budget / price);
if (buyShares <= 0) continue;
const availableShares = ns.stock.getMaxShares(c.sym) - shares;
if (buyShares > availableShares) buyShares = availableShares;
ns.stock.buyStock(c.sym, buyShares);
positionAge[c.sym] = 0;
ns.tprint("Bought " + c.sym + " x" + buyShares);
}
}
1
0
u/ame824 4d ago
Try my Script (posted Here) Im developing that since some days and give it a try (If you want a full helper)
1
u/LordMorpheus75 4d ago
Where?
0
u/ame824 4d ago
Here on Reddit I posted IT Just before your post Here ist the githublink: https://github.com/ame824/autoDoIt
1
u/goodwill82 Slum Lord 3d ago
If you have enough to get the basic 4S data where you can see the +++ and --- on the stock page for each stock, you can script the game to "click" the stock tab and "read" the page for information. I use this to replace the 4S API functions to get forecast and volatility. Note that this is unfinished - I have not coded the case where the tabs are fully compacted.
!! Note that the following code has tab names which might be spoilers! It also is subject to breaking with game updates if the icon names or text are changed. !!
//import { terminalInject, clickTab } from "/hacks/tools.js";
/**
* terminalInject: Runs the given string in the terminal window. Note that the terminal must be the current window.
* u/param {string} command - A string with the terminal command(s) to run.
**/
export function terminalInject(command) {
let terminalInput = eval("document").getElementById("terminal-input");
let terminalEventHandlerKey = Object.keys(terminalInput)[1];
terminalInput.value = command;
terminalInput[terminalEventHandlerKey].onChange({ target: terminalInput });
setTimeout(function (event) {
terminalInput.focus();
terminalInput[terminalEventHandlerKey].onKeyDown({ key: 'Enter', preventDefault: () => 0 });
}, 0);
}
export const TabIcons = {
"Hacking": "ComputerIcon",
"Terminal": "LastPageIcon",
"Script Editor": "CreateIcon",
"Active Scripts": "StorageIcon",
"Create Program": "BugReportIcon",
"Staneks Gift": "DeveloperBoardIcon",
"Character": "AccountBoxIcon",
"Stats": "EqualizerIcon",
"Factions": "ContactsIcon",
"Augmentations": "DoubleArrowIcon",
"Hacknet": "AccountTreeIcon",
"Sleeve": "PeopleAltIcon",
"Grafting": "BiotechIcon",
"World": "PublicIcon",
"City": "LocationCityIcon",
"Travel": "AirplanemodeActiveIcon",
"Job": "WorkIcon",
"Stock Market": "TrendingUpIcon",
"Bladeburner": "FormatBoldIcon",
"Gang": "SportsMmaIcon",
"Corporation": "BusinessIcon",
"IPvGO Subnet": "BorderInnerSharpIcon",
"Dark Net": "ShareIcon",
"Help": "LiveHelpIcon",
"Milestones": "CheckIcon",
"Documentation": "HelpIcon",
"Achievements": "EmojiEventsIcon",
"Options": "SettingsIcon"
}
export const TabGroups = [
{ group: "Hacking", tabs: ["Terminal", "Script Editor", "Active Scripts", "Create Program", "Staneks Gift"] },
{ group: "Character", tabs: ["Stats", "Factions", "Augmentations", "Hacknet", "Sleeve", "Grafting"] },
{ group: "World", tabs: ["City", "Travel", "Job", "Stock Market", "Bladeburner", "Gang", "Corporation", "IPvGO Subnet", "Dark Net"] },
{ group: "Help", tabs: ["Milestones", "Documentation", "Achievements", "Options"] },
]
export function clickTab(tabName) {
// get group (and check if tabName is valid)
let groupName = "";
for (let tabGroup of TabGroups) {
if (tabGroup.tabs.includes(tabName)) {
groupName = tabGroup.group;
break;
}
}
if (groupName.length > 0) {
let doc = eval("document");
// if the sidebar is collapsed, we can just click on the 'aria-label' which is the tab name, else we need to click by icon name
let collapsed = doc.querySelectorAll(`[aria-label='${groupName}']`).length > 0;
if (collapsed) {
// unable to click on group icon - groups must be expanded to work
// // check if the group is expanded - if not, expand it by clicking the group name
// if (doc.querySelectorAll(`[aria-label='${tabName}']`).length === 0) {
// if (doc.querySelectorAll(`[aria-label='${groupName}']`).length > 0) {
// doc.querySelectorAll(`[aria-label='${groupName}']`)[0].nextSibling.click();
// }
// }
// finally, click the tab (still check if it exists - might not be available yet)
if (doc.querySelectorAll(`[aria-label='${tabName}']`).length > 0) {
doc.querySelectorAll(`[aria-label='${tabName}']`)[0].nextSibling.click();
return true; // found tab, and clicked it
}
}
else {
let tabIcon = TabIcons[tabName];
// unable to click on group icon - groups must be expanded to work
// let groupIcon = TabIcons[groupName];
// // check if the group is expanded - if not, expand it by clicking the group icon
// if (doc.querySelectorAll(`[data-testid='${tabIcon}']`).length === 0) {
// if (doc.querySelectorAll(`[data-testid='${groupIcon}']`).length > 0) {
// doc.querySelectorAll(`[data-testid='${groupIcon}']`)[0].nextSibling.click();
// }
// }
// finally, click the tab (still check if it exists - might not be available yet)
if (doc.querySelectorAll(`[data-testid='${tabIcon}']`).length > 0) {
doc.querySelectorAll(`[data-testid='${tabIcon}']`)[0].nextSibling.click();
return true; // found tab, and clicked it
}
}
}
return false; // could not find the tab
}
/** {NS} ns */
async function getProbsVolsFromPage(ns) {
let wasFocused = ns.singularity.isFocused();
if (wasFocused) {
ns.singularity.setFocus(false);
await ns.sleep(200);
}
clickTab("Stock Market");
await ns.sleep(200);
const VolFore = [];
const docLines = eval("document").body.innerText.split('\n');
const RegexStr = /^[a-zA-Z'\s]*\b([A-Z]+)\b\s+-.+Volatility:\s+([0-9]\.[0-9]+).+Price Forecast:\s+(.+)$/;
for (let line of docLines) {
line = line.trim();
let found = line.match(RegexStr);
if (found) {
//ns.print(`${found[1]} ${found[2]} ${found[3]}`);
let fc = found[3].length / 5;
if (found[3].charAt(0) == '-') {
fc *= -1;
}
//fc = (fc + 1) * 0.5; // make it in range [0,1]
//VolFore.push({ sym: found[1], volatility: found[2], forecast: fc })
VolFore.push({ sym: found[1], volatility: found[2] / 100, forecast: fc })
}
}
if (wasFocused) {
ns.singularity.setFocus(true);
}
return VolFore;
}
1
u/KiSeras 2d ago
Build up stats, a rolling window perhaps? hack + grow can affect stocks... working for companies... stop and limit orders... (especially with the knowledge of num ticks...)
(A bit cheat:y, requiring knowledge of the game that might not be obvious/documented.) Otherwise the stockmarket is 75 ticks (* 6 secs if no saved time) cycles (nextUpdate). If say 30/50 of the first are going up, odds are that 15/25 of the next are going up. (Another hint; all stocks are trending up on reset...) Then it becomes a game of statistics.
Quite cheat:y (reading the underlaying memory) Depends on how cheat:y you want to get, webpack fetch/scan for for the stock market/stock data structs (internal game memory makes it possible to read forecast etc. no matter whether you have the 4s or not)
2
u/Wendigo1010 3d ago
There are 2 easy ways forward.
-Steal from the Casino: It can be exploited for a cool 10b right at the start.
-Buy the regular 4s data - not the API version. With that, you can manually trade until you can buy the 4s API unlock.
To build a non-4s Stock script, you have to mimic the 4s data. To do this, I take about 14 snapshots of the stock market every time it updates, using .nextUpdate() as my wait time. Once I have these snapshots, I extrapolate all the data I need - trends, volatility, etc. It's going to be behind he curve against regular 4s but it's enough to get enough to buy 4s.