// Investments: nine societies in three risk tiers. Every `intervalSeconds` each // price moves by a random percentage within the tier's swing (low ±5 %, mid // ±15 %, high ±50 %), then drifts back towards its fair value (more weakly for the wilder tiers), and // holdings pay a small dividend. Buying and selling carries a small fee. import { clamp } from './formulas.js'; export function holdingValue(state, id) { return state.invest.holdings[id] * state.invest.prices[id]; } export function portfolio(state, ctx) { let value = 0; let basis = 0; for (const s of ctx.investments.societies) { value += holdingValue(state, s.id); basis += state.invest.basis[s.id]; } return { value, basis, profit: value - basis }; } function repriceAll(state, ctx, rng, dividendMultiplier) { const cfg = ctx.investments; const inv = state.invest; for (const s of cfg.societies) { const tier = cfg.tiers[s.tier]; const before = inv.prices[s.id]; let price = before * (1 + (rng() * 2 - 1) * tier.swing); price += (s.basePrice - price) * tier.reversion; price = clamp(price, s.basePrice * cfg.minRatio, s.basePrice * cfg.maxRatio); inv.prices[s.id] = price; inv.change[s.id] = price / before - 1; state.funds += inv.holdings[s.id] * price * tier.dividend * dividendMultiplier; } } export function stepInvestments(state, ctx, dt, rng, mods) { state.investTimer += dt; while (state.investTimer >= ctx.investments.intervalSeconds) { state.investTimer -= ctx.investments.intervalSeconds; repriceAll(state, ctx, rng, mods.dividend); } } /** Spend `fraction` of the current funds on society `id`. */ export function buyShares(state, ctx, id, fraction) { if (!ctx.societiesById.has(id)) return false; const spend = state.funds * clamp(fraction, 0, 1); if (!(spend >= 0.01)) return false; state.funds -= spend; state.invest.holdings[id] += (spend * (1 - ctx.investments.fee)) / state.invest.prices[id]; state.invest.basis[id] += spend; return true; } /** Sell `fraction` of what is held in society `id`. */ export function sellShares(state, ctx, id, fraction) { if (!ctx.societiesById.has(id)) return false; const inv = state.invest; const share = clamp(fraction, 0, 1); if (!(inv.holdings[id] > 0) || share <= 0) return false; const proceeds = inv.holdings[id] * share * inv.prices[id] * (1 - ctx.investments.fee); const basisPart = inv.basis[id] * share; state.funds += proceeds; state.stats.investProfit += proceeds - basisPart; if (share >= 1) { inv.holdings[id] = 0; inv.basis[id] = 0; } else { inv.holdings[id] -= inv.holdings[id] * share; inv.basis[id] -= basisPart; } return true; }