hood-traders 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/framework/risk.ts","../src/framework/agent.ts","../src/framework/journal.ts","../src/framework/kill.ts","../src/framework/market.ts","../src/framework/fleet.ts","../src/framework/config.ts","../src/strategies/launch-sniper.ts","../src/strategies/momentum.ts","../src/strategies/premium-watch.ts","../src/server/dashboard.ts"],"sourcesContent":["import type { RefusalReason, RiskLimits } from './types.js'\n\n/** Everything the risk engine needs to rule on a single intent. */\nexport interface RiskContext {\n /** `buy` grows exposure and hits spend/position caps; `sell` reduces it and is exempt from those. */\n side: 'buy' | 'sell'\n /** USD notional of this order (agent computes it from live prices; USDG≈USD, WETH×ethUsd). */\n notionalUsd: number\n /** USD value the token position would reach AFTER a buy fills. */\n positionUsdAfter: number\n /** USD this agent has already spent this UTC day. */\n spentTodayUsd: number\n /** USD the whole fleet has spent this UTC day. */\n fleetSpentTodayUsd: number\n /** ms epoch of this agent's last executed trade, or null. */\n lastTradeAt: number | null\n /** Slippage bound (bps) the order would execute with. */\n slippageBps: number\n /** Whether the global kill switch is tripped. */\n killed: boolean\n /** Current time (ms) — injected for deterministic tests. */\n now: number\n /** Fleet-wide daily spend ceiling (USD). */\n fleetMaxDailySpendUsdg: number\n}\n\n/** Result of a risk check. */\nexport interface RiskVerdict {\n ok: boolean\n reason?: RefusalReason\n /** Human explanation, safe to journal and surface on the dashboard. */\n detail: string\n}\n\n/**\n * The risk engine. It is the last gate before any order — paper or live —\n * reaches execution, and it fails CLOSED: any check it cannot satisfy refuses\n * the trade rather than letting it through. Refusals are ordered from\n * cheapest/most-fatal (kill switch) to most-specific (caps) so the journaled\n * reason is the most meaningful one.\n *\n * Sells are intentionally exempt from spend and position caps: those caps exist\n * to bound how much risk you take ON, and refusing a de-risking sell because of\n * a spend cap would trap an agent in a losing position. Sells still honor the\n * kill switch, cooldown, and slippage bound.\n */\nexport class RiskEngine {\n constructor(private readonly limits: RiskLimits) {}\n\n get riskLimits(): RiskLimits {\n return this.limits\n }\n\n check(ctx: RiskContext): RiskVerdict {\n if (ctx.killed) {\n return { ok: false, reason: 'kill_switch', detail: 'kill switch is tripped — no new orders' }\n }\n\n if (ctx.notionalUsd <= 0) {\n return { ok: false, reason: 'zero_amount', detail: 'order notional is zero or negative' }\n }\n\n // Cooldown throttles trade frequency regardless of side.\n if (ctx.lastTradeAt !== null) {\n const elapsed = (ctx.now - ctx.lastTradeAt) / 1000\n if (elapsed < this.limits.cooldownSeconds) {\n const wait = (this.limits.cooldownSeconds - elapsed).toFixed(1)\n return { ok: false, reason: 'cooldown', detail: `cooldown active — ${wait}s until next trade allowed` }\n }\n }\n\n // Slippage bound must never exceed the configured cap.\n if (ctx.slippageBps > this.limits.maxSlippageBps) {\n return {\n ok: false,\n reason: 'slippage_bound',\n detail: `slippage ${ctx.slippageBps}bps exceeds cap ${this.limits.maxSlippageBps}bps`,\n }\n }\n\n // Spend/position caps only constrain buys (exposure-increasing orders).\n if (ctx.side === 'buy') {\n if (ctx.positionUsdAfter > this.limits.maxPositionUsdg) {\n return {\n ok: false,\n reason: 'position_cap',\n detail: `position would reach $${ctx.positionUsdAfter.toFixed(2)}, over cap $${this.limits.maxPositionUsdg}`,\n }\n }\n if (ctx.spentTodayUsd + ctx.notionalUsd > this.limits.maxDailySpendUsdg) {\n return {\n ok: false,\n reason: 'daily_cap',\n detail: `daily spend would reach $${(ctx.spentTodayUsd + ctx.notionalUsd).toFixed(2)}, over cap $${this.limits.maxDailySpendUsdg}`,\n }\n }\n if (ctx.fleetSpentTodayUsd + ctx.notionalUsd > ctx.fleetMaxDailySpendUsdg) {\n return {\n ok: false,\n reason: 'fleet_daily_cap',\n detail: `fleet daily spend would reach $${(ctx.fleetSpentTodayUsd + ctx.notionalUsd).toFixed(2)}, over cap $${ctx.fleetMaxDailySpendUsdg}`,\n }\n }\n }\n\n return { ok: true, detail: 'within all risk limits' }\n }\n}\n\n/** UTC-midnight ms boundary for `now` — the daily-cap accounting window. */\nexport function utcDayStart(now: number): number {\n const d = new Date(now)\n return Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate())\n}\n","import { formatUnits, type Account, type Address, type Hash } from 'viem'\nimport { buildSwapTx, ensureApproval, type SwapQuote } from 'hoodchain'\nimport type { Journal } from './journal.js'\nimport type { Market } from './market.js'\nimport { RiskEngine, utcDayStart } from './risk.js'\nimport type { KillSwitch } from './kill.js'\nimport type { Strategy } from './strategy.js'\nimport type {\n AgentStatus,\n Decision,\n Intent,\n Mode,\n Position,\n RiskLimits,\n TradeRecord,\n} from './types.js'\n\n/** Everything an {@link Agent} is constructed with. */\nexport interface AgentOptions {\n id: string\n strategy: Strategy\n market: Market\n limits: RiskLimits\n journal: Journal\n kill: KillSwitch\n mode: Mode\n /** Account for live execution; null in paper mode. */\n account: Account | null\n fleetMaxDailySpendUsdg: number\n /** Fleet-wide spend accessor + reporter, so the agent respects the global cap. */\n fleetSpentTodayUsd: () => number\n reportFleetSpend: (usd: number) => void\n /** Milliseconds between decision ticks. */\n tickIntervalMs: number\n /** Injected clock, for tests. Defaults to `Date.now`. */\n clock?: () => number\n}\n\nconst DUST = 1_000n // token smallest-units below which a position is considered closed\n\n/**\n * An autonomous trading agent = strategy + wallet + risk budget + journal.\n *\n * Each tick runs the full pipeline: observe (the strategy reads the {@link\n * Market}) → decide (the strategy returns intents) → simulate (a real QuoterV2\n * `eth_call`) → risk-check (the {@link RiskEngine}, fail-closed) → execute\n * (paper: record the simulated fill; live: sign the swap) → journal (every\n * decision, refusal, trade, and equity mark). The strategy proposes; the agent\n * disposes, and never lets an intent skip the risk gate.\n */\nexport class Agent {\n readonly id: string\n readonly strategy: Strategy\n readonly mode: Mode\n private readonly market: Market\n private readonly risk: RiskEngine\n private readonly journal: Journal\n private readonly kill: KillSwitch\n private readonly account: Account | null\n private readonly opts: AgentOptions\n private readonly clock: () => number\n\n private readonly positions = new Map<string, Position>()\n private lastTradeAt: number | null = null\n private realizedUsd = 0\n private spentTodayUsd = 0\n private spentDay = 0\n private ticks = 0\n private trades = 0\n private refusals = 0\n private lastTickAt: number | null = null\n private lastError: string | null = null\n private timer: ReturnType<typeof setTimeout> | null = null\n private running = false\n private ticking = false\n\n constructor(opts: AgentOptions) {\n this.opts = opts\n this.id = opts.id\n this.strategy = opts.strategy\n this.mode = opts.mode\n this.market = opts.market\n this.risk = new RiskEngine(opts.limits)\n this.journal = opts.journal\n this.kill = opts.kill\n this.account = opts.account\n this.clock = opts.clock ?? Date.now\n }\n\n /** Begin the tick loop and wire the strategy's stream subscriptions. */\n async start(): Promise<void> {\n if (this.running) return\n this.running = true\n const log = (message: string, meta: Record<string, unknown> = {}) =>\n this.journal.recordDecision({ agentId: this.id, ts: this.clock(), kind: 'observe', detail: message, meta })\n await this.strategy.start?.({ market: this.market, log })\n this.kill.onKill((reason) => log(`kill switch tripped: ${reason} — halting new orders`))\n this.scheduleTick()\n }\n\n /** Stop the loop and the strategy's subscriptions. */\n stop(): void {\n this.running = false\n if (this.timer) clearTimeout(this.timer)\n this.timer = null\n this.strategy.stop?.()\n }\n\n private scheduleTick(): void {\n if (!this.running) return\n this.timer = setTimeout(async () => {\n await this.tick()\n this.scheduleTick()\n }, this.opts.tickIntervalMs)\n this.timer.unref?.()\n }\n\n /** Run one full pipeline pass. Safe to call directly (used by tests). */\n async tick(): Promise<void> {\n if (this.ticking) return\n this.ticking = true\n const now = this.clock()\n try {\n this.rolloverDay(now)\n await this.markPositions(now)\n\n if (this.kill.isKilled()) {\n // Halted: still mark equity so the curve shows the freeze, but propose nothing.\n this.recordEquity(now)\n this.ticks++\n this.lastTickAt = now\n return\n }\n\n const decision = await this.decide(now)\n for (const alert of decision.alerts) {\n this.journal.recordDecision({\n agentId: this.id,\n ts: now,\n kind: 'alert',\n detail: `[${alert.level}] ${alert.message}`,\n meta: alert.meta ?? {},\n })\n }\n for (const intent of decision.intents) {\n await this.processIntent(intent, now)\n }\n\n this.recordEquity(now)\n this.ticks++\n this.lastTickAt = now\n this.lastError = null\n } catch (err) {\n this.lastError = err instanceof Error ? err.message : String(err)\n this.journal.recordDecision({\n agentId: this.id,\n ts: now,\n kind: 'observe',\n detail: `tick error: ${this.lastError}`,\n meta: {},\n })\n } finally {\n this.ticking = false\n }\n }\n\n private async decide(now: number): Promise<Decision> {\n const { quoteToken, quoteSymbol, quoteDecimals } = this.quoteInfo()\n return this.strategy.tick({\n market: this.market,\n positions: [...this.positions.values()],\n now,\n quoteToken,\n quoteSymbol,\n quoteDecimals,\n log: (message, meta = {}) =>\n this.journal.recordDecision({ agentId: this.id, ts: now, kind: 'observe', detail: message, meta }),\n })\n }\n\n private quoteInfo(): { quoteToken: Address; quoteSymbol: string; quoteDecimals: number } {\n if (this.strategy.quote === 'weth') {\n return { quoteToken: this.market.weth, quoteSymbol: 'WETH', quoteDecimals: 18 }\n }\n return { quoteToken: this.market.usdg, quoteSymbol: 'USDG', quoteDecimals: this.market.usdgDecimals }\n }\n\n private async processIntent(intent: Intent, now: number): Promise<void> {\n const refuse = (reason: string, detail: string, meta: Record<string, unknown> = {}) => {\n this.refusals++\n this.journal.recordDecision({\n agentId: this.id,\n ts: now,\n kind: 'refused',\n detail: `${intent.side} ${intent.tokenSymbol}: ${detail}`,\n meta: { reason, intentReason: intent.reason, ...meta },\n })\n }\n\n const quoteToken = intent.quoteToken\n const quoteDecimals = intent.quoteSymbol === 'USDG' ? this.market.usdgDecimals : 18\n\n // ── simulate (real eth_call against live pools) ────────────────────────────\n let sim: SwapQuote | null\n if (intent.side === 'buy') {\n sim = await this.market.quoteBuy(quoteToken, intent.token, intent.amountIn)\n } else {\n sim = await this.market.quoteSell(intent.token, quoteToken, intent.amountIn)\n }\n if (!sim || sim.amountOut <= 0n) {\n refuse('no_route', 'no liquid route to simulate the fill')\n return\n }\n\n // ── notional in USD ────────────────────────────────────────────────────────\n const ethUsd = intent.quoteSymbol === 'WETH' ? await this.market.ethUsd(30_000, now) : 1\n if (ethUsd === null) {\n refuse('no_route', 'cannot price ETH to enforce USD caps')\n return\n }\n let notionalUsd: number\n if (intent.side === 'buy') {\n notionalUsd = Number(formatUnits(intent.amountIn, quoteDecimals)) * ethUsd\n } else {\n notionalUsd = Number(formatUnits(sim.amountOut, quoteDecimals)) * ethUsd\n }\n\n // ── position accounting for the cap ────────────────────────────────────────\n const existing = this.positions.get(intent.token.toLowerCase())\n if (intent.side === 'sell') {\n if (!existing || existing.amount < intent.amountIn - DUST) {\n refuse('insufficient_balance', 'position too small to sell requested amount')\n return\n }\n }\n const positionUsdAfter = intent.side === 'buy' ? (existing?.investedUsd ?? 0) + notionalUsd : 0\n\n // ── slippage bound ─────────────────────────────────────────────────────────\n const slippageBps = Math.min(intent.maxSlippageBps ?? this.risk.riskLimits.maxSlippageBps, 10_000)\n const minOut = (sim.amountOut * BigInt(10_000 - slippageBps)) / 10_000n\n\n // ── risk gate (fail closed) ────────────────────────────────────────────────\n const verdict = this.risk.check({\n side: intent.side,\n notionalUsd,\n positionUsdAfter,\n spentTodayUsd: this.spentTodayUsd,\n fleetSpentTodayUsd: this.opts.fleetSpentTodayUsd(),\n lastTradeAt: this.lastTradeAt,\n slippageBps,\n killed: this.kill.isKilled(),\n now,\n fleetMaxDailySpendUsdg: this.opts.fleetMaxDailySpendUsdg,\n })\n if (!verdict.ok) {\n refuse(verdict.reason ?? 'refused', verdict.detail, { notionalUsd: round(notionalUsd) })\n return\n }\n\n // ── execute ────────────────────────────────────────────────────────────────\n let txHash: Hash | null = null\n let amountOut = sim.amountOut\n if (this.mode === 'live') {\n const executed = await this.executeLive(intent, sim, slippageBps)\n if (!executed) {\n refuse('no_route', 'live execution failed (see logs)')\n return\n }\n txHash = executed.hash\n amountOut = executed.amountOutMinimum // conservative floor actually received ≥ this\n }\n\n // ── journal + book-keeping ──────────────────────────────────────────────────\n const trade: TradeRecord = {\n agentId: this.id,\n mode: this.mode,\n ts: now,\n side: intent.side,\n token: intent.token,\n tokenSymbol: intent.tokenSymbol,\n quoteToken,\n quoteSymbol: intent.quoteSymbol,\n amountIn: intent.amountIn,\n amountOut,\n txHash,\n reason: intent.reason,\n slippageBps,\n gasEstimate: sim.gasEstimate,\n meta: { ...(intent.meta ?? {}), notionalUsd: round(notionalUsd), minOut: minOut.toString() },\n }\n this.journal.recordTrade(trade)\n this.trades++\n this.lastTradeAt = now\n this.applyFill(intent, amountOut, notionalUsd, now)\n if (intent.side === 'buy') {\n this.spentTodayUsd += notionalUsd\n this.opts.reportFleetSpend(notionalUsd)\n }\n }\n\n private async executeLive(\n intent: Intent,\n sim: SwapQuote,\n slippageBps: number,\n ): Promise<{ hash: Hash; amountOutMinimum: bigint } | null> {\n if (!this.account) return null\n try {\n const tx = buildSwapTx(this.market.client, sim, { slippageBps })\n await ensureApproval(this.market.client, intent.side === 'buy' ? intent.quoteToken : intent.token, intent.amountIn)\n const hash = await this.market.client.wallet!.sendTransaction({\n to: tx.to,\n data: tx.data,\n value: tx.value,\n account: this.account,\n chain: this.market.client.chain,\n })\n await this.market.client.public.waitForTransactionReceipt({ hash })\n return { hash, amountOutMinimum: tx.amountOutMinimum }\n } catch (err) {\n this.lastError = err instanceof Error ? err.message : String(err)\n return null\n }\n }\n\n private applyFill(intent: Intent, amountOut: bigint, notionalUsd: number, now: number): void {\n const key = intent.token.toLowerCase()\n const existing = this.positions.get(key)\n if (intent.side === 'buy') {\n if (existing) {\n existing.amount += amountOut\n existing.costBasis += intent.amountIn\n existing.investedUsd += notionalUsd\n } else {\n this.positions.set(key, {\n token: intent.token,\n tokenSymbol: intent.tokenSymbol,\n amount: amountOut,\n costBasis: intent.amountIn,\n investedUsd: notionalUsd,\n quoteToken: intent.quoteToken,\n quoteSymbol: intent.quoteSymbol,\n openedAt: now,\n markUsd: null,\n meta: intent.meta ?? {},\n })\n }\n return\n }\n // sell: realize PnL on the sold fraction\n if (!existing) return\n const sellAmount = intent.amountIn > existing.amount ? existing.amount : intent.amountIn\n // fraction as a float only touches investedUsd (already a float, USD-scale — safe);\n // costBasis stays bigint-only arithmetic so large token-unit positions (amounts near\n // or past 2^53) don't lose precision through a Number() round-trip.\n const fraction = existing.amount > 0n ? Number(sellAmount) / Number(existing.amount) : 1\n const costFractionUsd = existing.investedUsd * fraction\n const costBasisSold = existing.amount > 0n ? (existing.costBasis * sellAmount) / existing.amount : existing.costBasis\n this.realizedUsd += notionalUsd - costFractionUsd\n existing.amount -= sellAmount\n existing.costBasis -= costBasisSold\n existing.investedUsd -= costFractionUsd\n if (existing.amount <= DUST) this.positions.delete(key)\n }\n\n /** Mark every open position to its live exit value (a real sell-side quote). */\n private async markPositions(now: number): Promise<void> {\n for (const pos of this.positions.values()) {\n const q = await this.market.quoteSell(pos.token, pos.quoteToken, pos.amount)\n if (!q) {\n pos.markUsd = null\n continue\n }\n const quoteDecimals = pos.quoteSymbol === 'USDG' ? this.market.usdgDecimals : 18\n const ethUsd = pos.quoteSymbol === 'WETH' ? await this.market.ethUsd(30_000, now) : 1\n if (ethUsd === null) {\n pos.markUsd = null\n continue\n }\n pos.markUsd = Number(formatUnits(q.amountOut, quoteDecimals)) * ethUsd\n }\n }\n\n private openValueUsd(): number {\n let sum = 0\n for (const pos of this.positions.values()) sum += pos.markUsd ?? pos.investedUsd\n return sum\n }\n\n private recordEquity(now: number): void {\n const openValueUsd = this.openValueUsd()\n this.journal.recordEquity({\n agentId: this.id,\n ts: now,\n realizedUsd: round(this.realizedUsd),\n openValueUsd: round(openValueUsd),\n equityUsd: round(this.realizedUsd + openValueUsd),\n })\n }\n\n private rolloverDay(now: number): void {\n const day = utcDayStart(now)\n if (day !== this.spentDay) {\n this.spentDay = day\n this.spentTodayUsd = 0\n }\n }\n\n /** Live status snapshot for the dashboard API. */\n status(): AgentStatus {\n const openValueUsd = this.openValueUsd()\n return {\n id: this.id,\n strategy: this.strategy.id,\n mode: this.mode,\n running: this.running,\n killed: this.kill.isKilled(),\n limits: this.risk.riskLimits,\n spentTodayUsd: round(this.spentTodayUsd),\n realizedUsd: round(this.realizedUsd),\n openValueUsd: round(openValueUsd),\n equityUsd: round(this.realizedUsd + openValueUsd),\n positions: [...this.positions.values()],\n lastTickAt: this.lastTickAt,\n lastError: this.lastError,\n ticks: this.ticks,\n trades: this.trades,\n refusals: this.refusals,\n }\n }\n}\n\nfunction round(n: number): number {\n return Math.round(n * 1e6) / 1e6\n}\n","import Database from 'better-sqlite3'\nimport { mkdirSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport type { DecisionRecord, EquityPoint, TradeRecord } from './types.js'\n\n/**\n * The decision journal — the agent's black box recorder. Every observe, every\n * refusal, every trade (paper or live), and every equity mark lands here so the\n * dashboard can answer \"why did this trade fire?\" and the whole run is auditable\n * after the fact.\n *\n * SQLite via better-sqlite3 (synchronous, zero-config, embedded). bigints are\n * stored as decimal TEXT — SQLite integers are 64-bit signed and token amounts\n * routinely exceed that, so TEXT is the only lossless option.\n */\nexport class Journal {\n private readonly db: Database.Database\n\n constructor(dbPath: string) {\n if (dbPath !== ':memory:') mkdirSync(dirname(dbPath), { recursive: true })\n this.db = new Database(dbPath)\n this.db.pragma('journal_mode = WAL')\n this.db.pragma('foreign_keys = ON')\n this.migrate()\n }\n\n private migrate(): void {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS trades (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n agent_id TEXT NOT NULL,\n mode TEXT NOT NULL,\n ts INTEGER NOT NULL,\n side TEXT NOT NULL,\n token TEXT NOT NULL,\n token_symbol TEXT NOT NULL,\n quote_token TEXT NOT NULL,\n quote_symbol TEXT NOT NULL,\n amount_in TEXT NOT NULL,\n amount_out TEXT NOT NULL,\n tx_hash TEXT,\n reason TEXT NOT NULL,\n slippage_bps INTEGER NOT NULL,\n gas_estimate TEXT NOT NULL,\n meta TEXT NOT NULL\n );\n CREATE INDEX IF NOT EXISTS idx_trades_agent_ts ON trades(agent_id, ts);\n\n CREATE TABLE IF NOT EXISTS decisions (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n agent_id TEXT NOT NULL,\n ts INTEGER NOT NULL,\n kind TEXT NOT NULL,\n detail TEXT NOT NULL,\n meta TEXT NOT NULL\n );\n CREATE INDEX IF NOT EXISTS idx_decisions_agent_ts ON decisions(agent_id, ts);\n\n CREATE TABLE IF NOT EXISTS equity (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n agent_id TEXT NOT NULL,\n ts INTEGER NOT NULL,\n realized_usd REAL NOT NULL,\n open_value_usd REAL NOT NULL,\n equity_usd REAL NOT NULL\n );\n CREATE INDEX IF NOT EXISTS idx_equity_agent_ts ON equity(agent_id, ts);\n `)\n }\n\n recordTrade(t: TradeRecord): number {\n const info = this.db\n .prepare(\n `INSERT INTO trades\n (agent_id, mode, ts, side, token, token_symbol, quote_token, quote_symbol,\n amount_in, amount_out, tx_hash, reason, slippage_bps, gas_estimate, meta)\n VALUES (@agent_id,@mode,@ts,@side,@token,@token_symbol,@quote_token,@quote_symbol,\n @amount_in,@amount_out,@tx_hash,@reason,@slippage_bps,@gas_estimate,@meta)`,\n )\n .run({\n agent_id: t.agentId,\n mode: t.mode,\n ts: t.ts,\n side: t.side,\n token: t.token,\n token_symbol: t.tokenSymbol,\n quote_token: t.quoteToken,\n quote_symbol: t.quoteSymbol,\n amount_in: t.amountIn.toString(),\n amount_out: t.amountOut.toString(),\n tx_hash: t.txHash,\n reason: t.reason,\n slippage_bps: t.slippageBps,\n gas_estimate: t.gasEstimate.toString(),\n meta: JSON.stringify(t.meta ?? {}),\n })\n return Number(info.lastInsertRowid)\n }\n\n recordDecision(d: DecisionRecord): number {\n const info = this.db\n .prepare(\n `INSERT INTO decisions (agent_id, ts, kind, detail, meta)\n VALUES (@agent_id,@ts,@kind,@detail,@meta)`,\n )\n .run({\n agent_id: d.agentId,\n ts: d.ts,\n kind: d.kind,\n detail: d.detail,\n meta: JSON.stringify(d.meta ?? {}),\n })\n return Number(info.lastInsertRowid)\n }\n\n recordEquity(e: EquityPoint): void {\n this.db\n .prepare(\n `INSERT INTO equity (agent_id, ts, realized_usd, open_value_usd, equity_usd)\n VALUES (?,?,?,?,?)`,\n )\n .run(e.agentId, e.ts, e.realizedUsd, e.openValueUsd, e.equityUsd)\n }\n\n /** Sum of USDG-denominated spend by an agent since a UTC-day boundary (ms). */\n spentSince(agentId: string, sinceMs: number, quoteSymbol = 'USDG'): number {\n const rows = this.db\n .prepare(\n `SELECT amount_in FROM trades\n WHERE agent_id=? AND ts>=? AND side='buy' AND quote_symbol=?`,\n )\n .all(agentId, sinceMs, quoteSymbol) as { amount_in: string }[]\n // amount_in is USDG (6dp) smallest units → dollars\n return rows.reduce((sum, r) => sum + Number(BigInt(r.amount_in)) / 1e6, 0)\n }\n\n recentTrades(agentId: string, limit = 50): TradeRecord[] {\n const rows = this.db\n .prepare(`SELECT * FROM trades WHERE agent_id=? ORDER BY ts DESC LIMIT ?`)\n .all(agentId, limit) as Record<string, unknown>[]\n return rows.map(rowToTrade)\n }\n\n recentDecisions(agentId: string, limit = 100): DecisionRecord[] {\n const rows = this.db\n .prepare(`SELECT * FROM decisions WHERE agent_id=? ORDER BY ts DESC LIMIT ?`)\n .all(agentId, limit) as Record<string, unknown>[]\n return rows.map((r) => ({\n id: r.id as number,\n agentId: r.agent_id as string,\n ts: r.ts as number,\n kind: r.kind as DecisionRecord['kind'],\n detail: r.detail as string,\n meta: JSON.parse((r.meta as string) || '{}'),\n }))\n }\n\n equityCurve(agentId: string, limit = 500): EquityPoint[] {\n const rows = this.db\n .prepare(\n `SELECT * FROM (\n SELECT * FROM equity WHERE agent_id=? ORDER BY ts DESC LIMIT ?\n ) ORDER BY ts ASC`,\n )\n .all(agentId, limit) as Record<string, unknown>[]\n return rows.map((r) => ({\n agentId: r.agent_id as string,\n ts: r.ts as number,\n realizedUsd: r.realized_usd as number,\n openValueUsd: r.open_value_usd as number,\n equityUsd: r.equity_usd as number,\n }))\n }\n\n /** All trades across every agent, newest first — for the fleet-wide feed. */\n allRecentTrades(limit = 100): TradeRecord[] {\n const rows = this.db\n .prepare(`SELECT * FROM trades ORDER BY ts DESC LIMIT ?`)\n .all(limit) as Record<string, unknown>[]\n return rows.map(rowToTrade)\n }\n\n close(): void {\n this.db.close()\n }\n}\n\nfunction rowToTrade(r: Record<string, unknown>): TradeRecord {\n return {\n id: r.id as number,\n agentId: r.agent_id as string,\n mode: r.mode as TradeRecord['mode'],\n ts: r.ts as number,\n side: r.side as 'buy' | 'sell',\n token: r.token as `0x${string}`,\n tokenSymbol: r.token_symbol as string,\n quoteToken: r.quote_token as `0x${string}`,\n quoteSymbol: r.quote_symbol as string,\n amountIn: BigInt(r.amount_in as string),\n amountOut: BigInt(r.amount_out as string),\n txHash: (r.tx_hash as `0x${string}` | null) ?? null,\n reason: r.reason as string,\n slippageBps: r.slippage_bps as number,\n gasEstimate: BigInt(r.gas_estimate as string),\n meta: JSON.parse((r.meta as string) || '{}'),\n }\n}\n","import { existsSync } from 'node:fs'\n\n/**\n * Global kill switch. Once tripped it is irreversible for the process lifetime:\n * every agent checks `isKilled()` before each decide/execute step and refuses to\n * open new positions. Three independent triggers, per the spec:\n *\n * 1. SIGINT / SIGTERM (Ctrl-C, container stop)\n * 2. the presence of a `KILL` file on disk (drop-a-file panic button)\n * 3. an HTTP `POST /kill` on the dashboard server (wired in server/)\n *\n * The switch never sells or unwinds on its own — it HALTS new risk. Unwinding is\n * an explicit operator action, because a forced market-sell into thin liquidity\n * during whatever caused the panic is usually worse than holding.\n */\nexport class KillSwitch {\n private killed = false\n private reason: string | null = null\n private readonly listeners = new Set<(reason: string) => void>()\n private fileTimer: ReturnType<typeof setInterval> | null = null\n\n constructor(private readonly killFilePath: string) {}\n\n /** Install SIGINT/SIGTERM handlers and begin polling the kill file. */\n arm(): void {\n const onSignal = (sig: string) => () => this.trip(`signal:${sig}`)\n process.once('SIGINT', onSignal('SIGINT'))\n process.once('SIGTERM', onSignal('SIGTERM'))\n\n // Poll the kill file. If it already exists at boot, trip immediately.\n const check = () => {\n if (!this.killed && existsSync(this.killFilePath)) this.trip('kill_file')\n }\n check()\n this.fileTimer = setInterval(check, 1000)\n // Do not keep the event loop alive solely for this poll.\n this.fileTimer.unref?.()\n }\n\n /** Trip the switch. Idempotent. */\n trip(reason: string): void {\n if (this.killed) return\n this.killed = true\n this.reason = reason\n for (const l of this.listeners) {\n try {\n l(reason)\n } catch {\n // a listener throwing must not stop the others from being notified\n }\n }\n }\n\n isKilled(): boolean {\n return this.killed\n }\n\n killReason(): string | null {\n return this.reason\n }\n\n /** Subscribe to the trip event. Returns an unsubscribe fn. */\n onKill(listener: (reason: string) => void): () => void {\n this.listeners.add(listener)\n return () => this.listeners.delete(listener)\n }\n\n /** Stop the file poller (used in tests/teardown). */\n dispose(): void {\n if (this.fileTimer) clearInterval(this.fileTimer)\n this.fileTimer = null\n this.listeners.clear()\n }\n}\n","import {\n createHoodClient,\n getQuote,\n quoteSwap,\n swapAddresses,\n listPricedStockTokens,\n MAINNET_ADDRESSES,\n TESTNET_ADDRESSES,\n USDG_DECIMALS,\n type HoodClient,\n type StockQuote,\n type SwapQuote,\n} from 'hoodchain'\nimport { formatUnits, parseUnits, type Account, type Address } from 'viem'\nimport type { FleetConfig } from './config.js'\n\n/** A memecoin/token spot price sourced from live Uniswap v3 liquidity. */\nexport interface SpotPrice {\n token: Address\n /** USD value of one whole token, derived from a probe quote against USDG (direct or via WETH). */\n priceUsd: number\n /** Quote token the probe routed through. */\n via: 'usdg' | 'weth'\n ts: number\n}\n\n/**\n * The market adapter. Everything the observe/simulate steps need, sourced from\n * live Robinhood Chain state through the `hoodchain` SDK — Chainlink stock\n * feeds, Uniswap v3 quotes (which ARE `eth_call` simulations against real\n * pools), and an ETH/USD reference derived from the on-chain USDG/WETH pool.\n *\n * No price here is fabricated: a token with no liquidity resolves to `null`, and\n * strategies must handle that rather than trade a made-up number.\n */\nexport class Market {\n readonly client: HoodClient\n readonly usdg: Address\n readonly weth: Address\n readonly usdgDecimals = USDG_DECIMALS\n private ethUsdCache: { value: number; ts: number } | null = null\n\n constructor(config: FleetConfig, account?: Account) {\n this.client = createHoodClient({\n chain: config.network,\n rpcUrl: config.rpcUrl,\n account,\n acknowledgeStockTokenEligibility: config.stockTokenEligible,\n })\n const addrs = config.network === 'testnet' ? TESTNET_ADDRESSES : MAINNET_ADDRESSES\n this.usdg = addrs.usdg\n this.weth = config.network === 'testnet' ? TESTNET_ADDRESSES.weth : MAINNET_ADDRESSES.weth\n }\n\n /** Latest block number — a cheap liveness/observe heartbeat. */\n blockNumber(): Promise<bigint> {\n return this.client.public.getBlockNumber()\n }\n\n /**\n * ETH price in USD from the on-chain USDG/WETH pool (quote 0.01 WETH → USDG).\n * Cached for 30s — it moves slowly relative to a trading tick and every call\n * is a real RPC round trip. Returns `null` if no USDG/WETH pool answers.\n */\n async ethUsd(maxAgeMs = 30_000, now = Date.now()): Promise<number | null> {\n if (this.ethUsdCache && now - this.ethUsdCache.ts < maxAgeMs) return this.ethUsdCache.value\n try {\n const probe = parseUnits('0.01', 18)\n const q = await quoteSwap(this.client, { tokenIn: this.weth, tokenOut: this.usdg, amountIn: probe })\n const usd = Number(formatUnits(q.amountOut, this.usdgDecimals)) / 0.01\n this.ethUsdCache = { value: usd, ts: now }\n return usd\n } catch {\n return null\n }\n }\n\n /**\n * Spot USD price of one whole `token` from live liquidity. Probes token→USDG\n * directly, then token→WETH→USDG, using a 1-token probe. Returns `null` when\n * neither route has liquidity.\n */\n async spotPrice(token: Address, tokenDecimals = 18, now = Date.now()): Promise<SpotPrice | null> {\n const probe = parseUnits('1', tokenDecimals)\n // direct → USDG\n try {\n const q = await quoteSwap(this.client, { tokenIn: token, tokenOut: this.usdg, amountIn: probe })\n return { token, priceUsd: Number(formatUnits(q.amountOut, this.usdgDecimals)), via: 'usdg', ts: now }\n } catch {\n /* fall through to WETH route */\n }\n try {\n const q = await quoteSwap(this.client, { tokenIn: token, tokenOut: this.weth, amountIn: probe })\n const eth = await this.ethUsd(30_000, now)\n if (eth === null) return null\n const priceEth = Number(formatUnits(q.amountOut, 18))\n return { token, priceUsd: priceEth * eth, via: 'weth', ts: now }\n } catch {\n return null\n }\n }\n\n /**\n * Quote a buy: how much `token` `amountIn` of `quoteToken` acquires, at live\n * liquidity. This is the simulate step — a QuoterV2 `eth_call`, no state\n * change. Returns `null` when no route fills.\n */\n async quoteBuy(quoteToken: Address, token: Address, amountIn: bigint): Promise<SwapQuote | null> {\n try {\n return await quoteSwap(this.client, { tokenIn: quoteToken, tokenOut: token, amountIn })\n } catch {\n return null\n }\n }\n\n /** Quote a sell: proceeds in `quoteToken` from selling `amount` of `token`. */\n async quoteSell(token: Address, quoteToken: Address, amount: bigint): Promise<SwapQuote | null> {\n try {\n return await quoteSwap(this.client, { tokenIn: token, tokenOut: quoteToken, amountIn: amount })\n } catch {\n return null\n }\n }\n\n /** Chainlink price for a Stock Token (already multiplier-adjusted). */\n async stockChainlinkPrice(symbol: string): Promise<StockQuote | null> {\n try {\n return await getQuote(this.client, symbol)\n } catch {\n return null\n }\n }\n\n /**\n * DEX price of a Stock Token in USD from the USDG pool — the number the\n * premium-watch strategy compares against the Chainlink oracle. Probes with a\n * flat 10 USDG order (small enough to keep impact low across the priced\n * registry) and backs out the implied per-token price from the fill.\n */\n async stockDexPrice(tokenAddress: Address, referenceUsd: number): Promise<number | null> {\n if (referenceUsd <= 0) return null\n // Buy ~$10 of the token with USDG and back out the implied price.\n const usdgIn = parseUnits('10', this.usdgDecimals)\n const q = await this.quoteBuy(this.usdg, tokenAddress, usdgIn)\n if (!q) return null\n const tokensOut = Number(formatUnits(q.amountOut, 18))\n if (tokensOut <= 0) return null\n return 10 / tokensOut\n }\n\n /** Priced Stock Tokens from the registry (those with a Chainlink feed). */\n pricedStockTokens() {\n return listPricedStockTokens()\n }\n\n /** Router/quoter/weth/usdg set for the active network. */\n addresses() {\n return swapAddresses(this.client)\n }\n}\n","import { privateKeyToAccount } from 'viem/accounts'\nimport type { Account } from 'viem'\nimport { Agent } from './agent.js'\nimport type { FleetConfig } from './config.js'\nimport { Journal } from './journal.js'\nimport { KillSwitch } from './kill.js'\nimport { Market } from './market.js'\nimport { utcDayStart } from './risk.js'\nimport type { Strategy } from './strategy.js'\nimport type { AgentStatus, Mode, RiskLimits } from './types.js'\n\n/** Definition of one agent within a fleet. */\nexport interface AgentSpec {\n id: string\n strategy: Strategy\n /** Overrides merged over the fleet default limits. */\n limits?: Partial<RiskLimits>\n tickIntervalMs?: number\n}\n\n/** Aggregate fleet numbers for the dashboard header. */\nexport interface FleetSummary {\n network: string\n mode: Mode\n killed: boolean\n killReason: string | null\n fleetSpentTodayUsd: number\n fleetMaxDailySpendUsdg: number\n realizedUsd: number\n openValueUsd: number\n equityUsd: number\n agents: number\n startedAt: number\n}\n\n/**\n * The fleet: owns the shared market client, journal, and kill switch, then runs\n * a set of agents against them and tracks the global daily-spend budget. It is\n * the process-level object the dashboard server reads and the kill switch acts\n * on.\n */\nexport class Fleet {\n readonly config: FleetConfig\n readonly journal: Journal\n readonly kill: KillSwitch\n readonly market: Market\n private readonly account: Account | null\n private readonly agents: Agent[] = []\n private fleetSpentTodayUsd = 0\n private spentDay = 0\n private startedAt = 0\n\n constructor(config: FleetConfig) {\n this.config = config\n this.account = config.privateKey ? privateKeyToAccount(config.privateKey) : null\n this.journal = new Journal(config.dbPath)\n this.kill = new KillSwitch(config.killFile)\n this.market = new Market(config, this.account ?? undefined)\n }\n\n /** Build agents from specs. */\n addAgents(specs: AgentSpec[]): void {\n for (const spec of specs) {\n const limits: RiskLimits = { ...this.config.defaultLimits, ...spec.limits }\n this.agents.push(\n new Agent({\n id: spec.id,\n strategy: spec.strategy,\n market: this.market,\n limits,\n journal: this.journal,\n kill: this.kill,\n mode: this.config.mode,\n account: this.account,\n fleetMaxDailySpendUsdg: this.config.fleetMaxDailySpendUsdg,\n fleetSpentTodayUsd: () => this.currentFleetSpend(),\n reportFleetSpend: (usd) => this.recordFleetSpend(usd),\n tickIntervalMs: spec.tickIntervalMs ?? 5000,\n }),\n )\n }\n }\n\n private currentFleetSpend(now = Date.now()): number {\n const day = utcDayStart(now)\n if (day !== this.spentDay) {\n this.spentDay = day\n this.fleetSpentTodayUsd = 0\n }\n return this.fleetSpentTodayUsd\n }\n\n private recordFleetSpend(usd: number): void {\n this.currentFleetSpend()\n this.fleetSpentTodayUsd += usd\n }\n\n /** Arm the kill switch and start every agent's loop. */\n async start(): Promise<void> {\n this.startedAt = Date.now()\n this.kill.arm()\n await Promise.all(this.agents.map((a) => a.start()))\n }\n\n /** Stop all agents (kill switch stays tripped if it was). */\n stop(): void {\n for (const a of this.agents) a.stop()\n }\n\n /** Run exactly one tick per agent, in parallel, without arming the interval scheduler. Used by E2E tests. */\n async tickAllOnce(): Promise<void> {\n await Promise.all(this.agents.map((a) => a.tick()))\n }\n\n /** Trip the kill switch — halts new orders across the fleet. */\n tripKill(reason: string): void {\n this.kill.trip(reason)\n }\n\n agentStatuses(): AgentStatus[] {\n return this.agents.map((a) => a.status())\n }\n\n summary(): FleetSummary {\n const statuses = this.agentStatuses()\n return {\n network: this.config.network,\n mode: this.config.mode,\n killed: this.kill.isKilled(),\n killReason: this.kill.killReason(),\n fleetSpentTodayUsd: round(this.currentFleetSpend()),\n fleetMaxDailySpendUsdg: this.config.fleetMaxDailySpendUsdg,\n realizedUsd: round(statuses.reduce((s, a) => s + a.realizedUsd, 0)),\n openValueUsd: round(statuses.reduce((s, a) => s + a.openValueUsd, 0)),\n equityUsd: round(statuses.reduce((s, a) => s + a.equityUsd, 0)),\n agents: this.agents.length,\n startedAt: this.startedAt,\n }\n }\n\n close(): void {\n this.stop()\n this.kill.dispose()\n this.journal.close()\n }\n}\n\nfunction round(n: number): number {\n return Math.round(n * 1e6) / 1e6\n}\n","import type { HoodNetwork } from 'hoodchain'\nimport type { Mode, RiskLimits } from './types.js'\n\n/** Fleet-wide configuration resolved from the environment. */\nexport interface FleetConfig {\n network: HoodNetwork\n rpcUrl: string | undefined\n mode: Mode\n /** Set true only when HOOD_TRADERS_LIVE=1 AND a key is present. */\n hasWallet: boolean\n privateKey: `0x${string}` | undefined\n stockTokenEligible: boolean\n fleetMaxDailySpendUsdg: number\n dashboardPort: number\n killFile: string\n /** Path to the SQLite journal. */\n dbPath: string\n defaultLimits: RiskLimits\n}\n\nfunction num(name: string, fallback: number): number {\n const raw = process.env[name]\n if (raw === undefined || raw === '') return fallback\n const n = Number(raw)\n if (!Number.isFinite(n) || n < 0) {\n throw new Error(`Env ${name}=\"${raw}\" is not a non-negative number`)\n }\n return n\n}\n\nfunction bool(name: string, fallback: boolean): boolean {\n const raw = process.env[name]\n if (raw === undefined || raw === '') return fallback\n return raw === '1' || raw.toLowerCase() === 'true' || raw.toLowerCase() === 'yes'\n}\n\n/**\n * Resolve fleet configuration from the environment.\n *\n * Live mode is deliberately hard to enable by accident: it requires BOTH\n * `HOOD_TRADERS_LIVE=1` and a `ROBINHOOD_CHAIN_PRIVATE_KEY`. Missing either one\n * falls back to paper mode rather than erroring, so a mis-set flag can never\n * silently spend real funds.\n */\nexport function loadFleetConfig(env: NodeJS.ProcessEnv = process.env): FleetConfig {\n const network = (env.HOOD_NETWORK === 'testnet' ? 'testnet' : 'mainnet') as HoodNetwork\n const wantLive = bool('HOOD_TRADERS_LIVE', false)\n const privateKey = env.ROBINHOOD_CHAIN_PRIVATE_KEY as `0x${string}` | undefined\n const hasKey = typeof privateKey === 'string' && /^0x[0-9a-fA-F]{64}$/.test(privateKey)\n const mode: Mode = wantLive && hasKey ? 'live' : 'paper'\n\n return {\n network,\n rpcUrl: env.HOOD_RPC_URL || undefined,\n mode,\n hasWallet: hasKey,\n privateKey: hasKey ? privateKey : undefined,\n stockTokenEligible: bool('HOOD_STOCK_TOKEN_ELIGIBLE', false),\n fleetMaxDailySpendUsdg: num('FLEET_MAX_DAILY_SPEND_USDG', 250),\n dashboardPort: num('DASHBOARD_PORT', 4670),\n killFile: env.KILL_FILE || './KILL',\n dbPath: env.HOOD_TRADERS_DB || './data/hood-traders.db',\n defaultLimits: {\n maxPositionUsdg: num('AGENT_MAX_POSITION_USDG', 50),\n maxDailySpendUsdg: num('AGENT_MAX_DAILY_SPEND_USDG', 100),\n maxSlippageBps: num('AGENT_MAX_SLIPPAGE_BPS', 100),\n cooldownSeconds: num('AGENT_COOLDOWN_SECONDS', 60),\n },\n }\n}\n","import { erc20Abi, watchLaunches, type Launch } from 'hoodchain'\nimport { formatUnits, parseEther, type Address } from 'viem'\nimport type {\n Strategy,\n StrategyMeta,\n StrategyStartContext,\n StrategyTickContext,\n} from '../framework/strategy.js'\nimport type { Decision, Intent, Alert } from '../framework/types.js'\n\n/** Tunables for {@link LaunchSniper}. */\nexport interface LaunchSniperParams {\n /** WETH spent per entry. */\n entryWeth: number\n /** Take profit as a fraction (0.5 = +50%). */\n takeProfitPct: number\n /** Stop loss as a fraction (0.3 = -30%). */\n stopLossPct: number\n /** Force-exit a position after this many seconds regardless of PnL. */\n maxHoldSeconds: number\n /** Reject a launch if the deployer still holds more than this fraction of supply. */\n maxDeployerPct: number\n /** Reject if an immediate buy→sell round trip would lose more than this fraction (honeypot/thin-pool guard). */\n maxRoundTripLossPct: number\n /** Only consider launches at most this many seconds old when first seen. */\n maxLaunchAgeSeconds: number\n}\n\nconst DEFAULTS: LaunchSniperParams = {\n entryWeth: 0.01,\n takeProfitPct: 0.6,\n stopLossPct: 0.35,\n maxHoldSeconds: 30 * 60,\n maxDeployerPct: 0.15,\n maxRoundTripLossPct: 0.35,\n maxLaunchAgeSeconds: 5 * 60,\n}\n\ninterface Candidate {\n launch: Launch\n seenAt: number\n}\n\n/**\n * launch-sniper — enter brand-new launchpad coins that clear objective safety\n * filters, then exit on take-profit, stop, or a hard time limit.\n *\n * EDGE HYPOTHESIS: the first minutes after a NOXA instant-listing are the most\n * information-rich and most volatile window a memecoin ever has. A disciplined\n * buyer who (a) only touches tokens that are actually round-trippable (not\n * honeypots) and whose deployer is not sitting on the supply, and (b) exits\n * mechanically instead of falling in love, harvests a slice of that opening\n * volatility. The edge is speed + discipline, not prediction.\n *\n * FAILURE MODES: most new launches go to zero — the stop loss WILL fire often\n * and the strategy is negative-carry unless the winners pay for the losers.\n * Honeypots evolve (sell-tax toggled AFTER you buy); the round-trip check only\n * sees the state at entry. Odyssey tokens on the bonding curve have no Uniswap\n * pool yet, so they are skipped until graduation — this strategy is really a\n * NOXA/graduated-pool sniper. Paper fills assume the QuoterV2 mid; a real\n * sniper competes with faster bots and eats worse fills.\n */\nexport class LaunchSniper implements Strategy {\n readonly id = 'launch-sniper'\n readonly title = 'Launch Sniper'\n readonly quote = 'weth' as const\n private readonly p: LaunchSniperParams\n private readonly queue: Candidate[] = []\n private readonly seen = new Set<string>()\n private unwatch: (() => void) | null = null\n\n constructor(params: Partial<LaunchSniperParams> = {}) {\n this.p = { ...DEFAULTS, ...params }\n }\n\n get meta(): StrategyMeta {\n return {\n edge:\n 'Harvest opening-minutes volatility of freshly launched memecoins by buying only round-trippable, ' +\n 'non-deployer-heavy launches and exiting mechanically on TP/stop/time.',\n failureModes: [\n 'Most launches trend to zero — the stop loss fires frequently; profitability depends on winners covering losers.',\n 'Honeypots can enable a sell tax AFTER entry; the entry-time round-trip check cannot see that.',\n 'Odyssey bonding-curve tokens have no Uniswap pool pre-graduation and are skipped (this is effectively a NOXA sniper).',\n 'Paper fills use the QuoterV2 mid; live, faster bots win the best fills and you eat slippage.',\n ],\n params: { ...this.p },\n }\n }\n\n start(ctx: StrategyStartContext): void {\n // Real-time launch subscription across NOXA + The Odyssey.\n this.unwatch = watchLaunches(\n ctx.market.client,\n (launch) => {\n const key = launch.token.toLowerCase()\n if (this.seen.has(key)) return\n this.seen.add(key)\n this.queue.push({ launch, seenAt: Date.now() })\n ctx.log(`new launch queued: ${launch.launchpad} ${launch.token}`, { creator: launch.creator })\n },\n { onError: (e) => ctx.log(`launch watcher error: ${e.message}`) },\n )\n }\n\n stop(): void {\n this.unwatch?.()\n this.unwatch = null\n }\n\n async tick(ctx: StrategyTickContext): Promise<Decision> {\n const intents: Intent[] = []\n const alerts: Alert[] = []\n\n // ── exits first (protect open risk before taking on more) ──────────────────\n for (const pos of ctx.positions) {\n const ageSec = (ctx.now - pos.openedAt) / 1000\n const pnlPct = pos.markUsd !== null && pos.investedUsd > 0 ? pos.markUsd / pos.investedUsd - 1 : null\n let exitReason: string | null = null\n if (pnlPct !== null && pnlPct >= this.p.takeProfitPct) exitReason = `take-profit ${(pnlPct * 100).toFixed(1)}%`\n else if (pnlPct !== null && pnlPct <= -this.p.stopLossPct) exitReason = `stop-loss ${(pnlPct * 100).toFixed(1)}%`\n else if (ageSec >= this.p.maxHoldSeconds) exitReason = `time-exit ${Math.round(ageSec)}s held`\n if (exitReason) {\n intents.push({\n side: 'sell',\n token: pos.token,\n tokenSymbol: pos.tokenSymbol,\n amountIn: pos.amount,\n quoteToken: pos.quoteToken,\n quoteSymbol: pos.quoteSymbol,\n reason: exitReason,\n meta: { pnlPct },\n })\n }\n }\n\n // ── one new entry per tick (evaluate the oldest queued candidate) ──────────\n const candidate = this.queue.shift()\n if (candidate) {\n const decisionOrReject = await this.evaluate(ctx, candidate)\n if (decisionOrReject.intent) intents.push(decisionOrReject.intent)\n else if (decisionOrReject.alert) alerts.push(decisionOrReject.alert)\n }\n\n return { intents, alerts }\n }\n\n private async evaluate(\n ctx: StrategyTickContext,\n candidate: Candidate,\n ): Promise<{ intent?: Intent; alert?: Alert }> {\n const { launch } = candidate\n const ageSec = (ctx.now - candidate.seenAt) / 1000\n if (ageSec > this.p.maxLaunchAgeSeconds) {\n return { alert: { level: 'info', message: `skip ${launch.token}: stale (${Math.round(ageSec)}s old)`, meta: {} } }\n }\n // already holding it?\n if (ctx.positions.some((p) => p.token.toLowerCase() === launch.token.toLowerCase())) return {}\n\n const amountIn = parseEther(String(this.p.entryWeth))\n\n // Filter 1 — route exists (Odyssey pre-graduation tokens fail here and are skipped).\n const buyQuote = await ctx.market.quoteBuy(ctx.quoteToken, launch.token, amountIn)\n if (!buyQuote || buyQuote.amountOut <= 0n) {\n return { alert: { level: 'info', message: `skip ${launch.token}: no liquid Uniswap route`, meta: { launchpad: launch.launchpad } } }\n }\n\n // Filter 2 — round-trip retention (honeypot / thin-pool guard).\n const sellQuote = await ctx.market.quoteSell(launch.token, ctx.quoteToken, buyQuote.amountOut)\n if (!sellQuote || sellQuote.amountOut <= 0n) {\n return { alert: { level: 'warn', message: `skip ${launch.token}: cannot sell back (honeypot?)`, meta: {} } }\n }\n const retention = Number(sellQuote.amountOut) / Number(amountIn)\n if (1 - retention > this.p.maxRoundTripLossPct) {\n return {\n alert: {\n level: 'warn',\n message: `skip ${launch.token}: round-trip loss ${((1 - retention) * 100).toFixed(1)}% > ${(this.p.maxRoundTripLossPct * 100).toFixed(0)}%`,\n meta: { retention },\n },\n }\n }\n\n // Filter 3 — deployer concentration.\n const deployerPct = await this.deployerConcentration(ctx, launch)\n if (deployerPct !== null && deployerPct > this.p.maxDeployerPct) {\n return {\n alert: {\n level: 'warn',\n message: `skip ${launch.token}: deployer holds ${(deployerPct * 100).toFixed(1)}% > ${(this.p.maxDeployerPct * 100).toFixed(0)}%`,\n meta: { deployerPct },\n },\n }\n }\n\n return {\n intent: {\n side: 'buy',\n token: launch.token,\n tokenSymbol: shortToken(launch.token),\n amountIn,\n quoteToken: ctx.quoteToken,\n quoteSymbol: ctx.quoteSymbol,\n reason: `sniped ${launch.launchpad} launch — retention ${(retention * 100).toFixed(1)}%, deployer ${deployerPct === null ? 'n/a' : (deployerPct * 100).toFixed(1) + '%'}`,\n meta: { launchpad: launch.launchpad, deployerPct, retention },\n },\n }\n }\n\n private async deployerConcentration(ctx: StrategyTickContext, launch: Launch): Promise<number | null> {\n try {\n const [supply, bal] = await ctx.market.client.public.multicall({\n contracts: [\n { address: launch.token, abi: erc20Abi, functionName: 'totalSupply' as const },\n { address: launch.token, abi: erc20Abi, functionName: 'balanceOf' as const, args: [launch.creator] as const },\n ],\n allowFailure: false,\n })\n if ((supply as bigint) === 0n) return null\n return Number(formatUnits((bal as bigint) * 10_000n / (supply as bigint), 4))\n } catch {\n return null\n }\n }\n}\n\nfunction shortToken(addr: Address): string {\n return `${addr.slice(0, 6)}…${addr.slice(-4)}`\n}\n","import { getRecentLaunches, parseUsdg, type Launch } from 'hoodchain'\nimport type { Address } from 'viem'\nimport type {\n Strategy,\n StrategyMeta,\n StrategyTickContext,\n} from '../framework/strategy.js'\nimport type { Decision, Intent, Alert } from '../framework/types.js'\n\n/** Tunables for {@link Momentum}. */\nexport interface MomentumParams {\n /** USDG spent per entry. */\n entryUsdg: number\n /** Minimum price gain over the lookback window to count as a breakout. */\n breakoutPct: number\n /** How many price samples (ticks) form the lookback window. */\n lookbackSamples: number\n /** Trailing stop: exit if price falls this fraction off its post-entry peak. */\n trailingStopPct: number\n /** Hard time exit regardless of PnL. */\n maxHoldSeconds: number\n /** How many graduated tokens to track price history for (most-recently-graduated first). */\n maxTracked: number\n /** Blocks to look back for discovering graduated tokens. */\n discoveryLookbackBlocks: bigint\n}\n\nconst DEFAULTS: MomentumParams = {\n entryUsdg: 10,\n breakoutPct: 0.15,\n lookbackSamples: 6,\n trailingStopPct: 0.2,\n maxHoldSeconds: 60 * 60,\n maxTracked: 15,\n discoveryLookbackBlocks: 200_000n,\n}\n\ninterface PriceHistory {\n token: Address\n symbol: string\n samples: { ts: number; priceUsd: number }[]\n}\n\n/**\n * momentum — volume+price breakout entries on already-graduated (liquid)\n * tokens, trailing-stop exits.\n *\n * EDGE HYPOTHESIS: a token that has already graduated to a locked Uniswap v3\n * pool has survived the highest-mortality phase (the bonding curve / first\n * minutes). A subsequent breakout — price up sharply over a short lookback,\n * confirmed by the pool actually being tradable at size — reflects real\n * incoming demand rather than launch-day noise, and trend-following that with\n * a trailing stop captures the middle of the move while giving back only the\n * tail.\n *\n * FAILURE MODES: breakouts on illiquid tokens are trivially fakeable by a\n * single wallet round-tripping the pool; the strategy only looks at price, not\n * depth, so it can chase a move that reverses the instant it stops buying.\n * Trailing stops whipsaw in choppy markets — expect a string of small losses\n * between real trends. Discovery only looks at NOXA (instant-listed, so\n * \"graduated\" from day one) and Odyssey `PoolMigrated` history within the\n * lookback window; a real breakout minutes after that window closes is missed\n * until the next discovery pass.\n */\nexport class Momentum implements Strategy {\n readonly id = 'momentum'\n readonly title = 'Momentum'\n readonly quote = 'usdg' as const\n private readonly p: MomentumParams\n private readonly history = new Map<string, PriceHistory>()\n private readonly peakSinceEntry = new Map<string, number>()\n private lastDiscoveryAt = 0\n\n constructor(params: Partial<MomentumParams> = {}) {\n this.p = { ...DEFAULTS, ...params }\n }\n\n get meta(): StrategyMeta {\n return {\n edge:\n 'Trend-follow price breakouts on already-graduated, liquid tokens — surviving the launch phase filters ' +\n 'for real demand, and a trailing stop rides the middle of a move.',\n failureModes: [\n 'Breakouts on thin pools are trivially fakeable by a single wallet; price-only signal has no depth check.',\n 'Trailing stops whipsaw in choppy conditions — a string of small losses between real trends is expected.',\n 'Discovery only scans a bounded block lookback; breakouts on tokens outside that window are missed until the next pass.',\n 'Momentum has no edge in a flat or declining market — it is a trend-only strategy by design.',\n ],\n params: { ...this.p },\n }\n }\n\n async tick(ctx: StrategyTickContext): Promise<Decision> {\n const intents: Intent[] = []\n const alerts: Alert[] = []\n\n // Refresh the tracked-token set periodically (cheap log scan, not every tick).\n if (ctx.now - this.lastDiscoveryAt > 5 * 60_000 || this.history.size === 0) {\n await this.discover(ctx)\n this.lastDiscoveryAt = ctx.now\n }\n\n // Sample current prices for tracked tokens.\n for (const h of this.history.values()) {\n const spot = await ctx.market.spotPrice(h.token)\n if (!spot) continue\n h.samples.push({ ts: ctx.now, priceUsd: spot.priceUsd })\n if (h.samples.length > this.p.lookbackSamples + 1) h.samples.shift()\n }\n\n // ── exits: trailing stop / time exit ────────────────────────────────────────\n for (const pos of ctx.positions) {\n const key = pos.token.toLowerCase()\n const spot = await ctx.market.spotPrice(pos.token)\n const ageSec = (ctx.now - pos.openedAt) / 1000\n if (spot) {\n const peak = Math.max(this.peakSinceEntry.get(key) ?? spot.priceUsd, spot.priceUsd)\n this.peakSinceEntry.set(key, peak)\n const drawdown = peak > 0 ? 1 - spot.priceUsd / peak : 0\n if (drawdown >= this.p.trailingStopPct) {\n intents.push(this.exitIntent(pos, ctx, `trailing stop: ${(drawdown * 100).toFixed(1)}% off peak $${peak.toFixed(6)}`))\n this.peakSinceEntry.delete(key)\n continue\n }\n }\n if (ageSec >= this.p.maxHoldSeconds) {\n intents.push(this.exitIntent(pos, ctx, `time-exit ${Math.round(ageSec)}s held`))\n this.peakSinceEntry.delete(key)\n }\n }\n\n // ── entries: breakout over the lookback window ──────────────────────────────\n for (const h of this.history.values()) {\n if (h.samples.length < this.p.lookbackSamples) continue\n if (ctx.positions.some((p) => p.token.toLowerCase() === h.token.toLowerCase())) continue\n const first = h.samples[h.samples.length - this.p.lookbackSamples]\n const last = h.samples[h.samples.length - 1]\n if (!first || !last || first.priceUsd <= 0) continue\n const gain = last.priceUsd / first.priceUsd - 1\n if (gain >= this.p.breakoutPct) {\n const amountIn = parseUsdg(String(this.p.entryUsdg))\n intents.push({\n side: 'buy',\n token: h.token,\n tokenSymbol: h.symbol,\n amountIn,\n quoteToken: ctx.quoteToken,\n quoteSymbol: ctx.quoteSymbol,\n reason: `breakout +${(gain * 100).toFixed(1)}% over ${this.p.lookbackSamples} samples ($${first.priceUsd.toFixed(6)}→$${last.priceUsd.toFixed(6)})`,\n meta: { gain, entryPriceUsd: last.priceUsd },\n })\n this.peakSinceEntry.set(h.token.toLowerCase(), last.priceUsd)\n } else {\n alerts.push({\n level: 'info',\n message: `${h.symbol} watch: ${(gain * 100).toFixed(1)}% vs ${(this.p.breakoutPct * 100).toFixed(0)}% breakout threshold`,\n })\n }\n }\n\n return { intents, alerts }\n }\n\n private exitIntent(pos: StrategyTickContext['positions'][number], ctx: StrategyTickContext, reason: string): Intent {\n return {\n side: 'sell',\n token: pos.token,\n tokenSymbol: pos.tokenSymbol,\n amountIn: pos.amount,\n quoteToken: pos.quoteToken,\n quoteSymbol: pos.quoteSymbol,\n reason,\n }\n }\n\n private async discover(ctx: StrategyTickContext): Promise<void> {\n let launches: Launch[]\n try {\n launches = await getRecentLaunches(ctx.market.client, { lookbackBlocks: this.p.discoveryLookbackBlocks })\n } catch (err) {\n ctx.log(`momentum discovery failed: ${err instanceof Error ? err.message : String(err)}`)\n return\n }\n // NOXA tokens are tradable from block one; Odyssey tokens need a migrated pool —\n // approximated here by simply attempting a spot price and dropping tokens with none.\n const graduated = launches.filter((l) => l.launchpad === 'noxa' || l.pool !== null)\n const candidates = graduated.slice(-this.p.maxTracked)\n for (const l of candidates) {\n const key = l.token.toLowerCase()\n if (this.history.has(key)) continue\n const spot = await ctx.market.spotPrice(l.token)\n if (!spot) continue\n this.history.set(key, { token: l.token, symbol: shortToken(l.token), samples: [{ ts: ctx.now, priceUsd: spot.priceUsd }] })\n }\n // Evict tokens no longer in the discovered window to keep the tracked set fresh.\n const keep = new Set(candidates.map((l) => l.token.toLowerCase()))\n for (const key of [...this.history.keys()]) {\n if (!keep.has(key) && !ctx.positions.some((p) => p.token.toLowerCase() === key)) this.history.delete(key)\n }\n }\n}\n\nfunction shortToken(addr: Address): string {\n return `${addr.slice(0, 6)}…${addr.slice(-4)}`\n}\n","import { getStockToken, parseUsdg } from 'hoodchain'\nimport type {\n Strategy,\n StrategyMeta,\n StrategyTickContext,\n} from '../framework/strategy.js'\nimport type { Alert, Decision, Intent } from '../framework/types.js'\n\n/** Tunables for {@link PremiumWatch}. */\nexport interface PremiumWatchParams {\n /** Symbols to watch. Defaults to a handful of the most liquid priced Stock Tokens. */\n symbols: string[];\n /** Premium/discount (bps, |dex - oracle| / oracle) that triggers an alert. */\n alertThresholdBps: number\n /** Premium/discount (bps) that triggers a convergence TRADE — only reachable in trade-eligible config. */\n tradeThresholdBps: number\n /** USDG spent per convergence trade, when trading is enabled. */\n tradeSizeUsdg: number\n /** Exit once the premium reverts to within this many bps of fair. */\n exitThresholdBps: number\n /** Hard time exit for a convergence position regardless of premium. */\n maxHoldSeconds: number\n /**\n * Convergence TRADING is opt-in and requires BOTH this flag AND\n * `market.client.acknowledgeStockTokenEligibility === true` (the operator's\n * non-US-person affirmation, per `_shared.md`). Missing either keeps the\n * strategy strictly alerts-only, which is the default and the recommended mode.\n */\n enableTrading: boolean\n}\n\nconst DEFAULT_SYMBOLS = ['AAPL', 'TSLA', 'NVDA', 'AMZN', 'MSFT']\n\nconst DEFAULTS: PremiumWatchParams = {\n symbols: DEFAULT_SYMBOLS,\n alertThresholdBps: 50,\n tradeThresholdBps: 150,\n tradeSizeUsdg: 20,\n exitThresholdBps: 20,\n maxHoldSeconds: 2 * 60 * 60,\n enableTrading: false,\n}\n\n/**\n * premium-watch — tracks the spread between each Stock Token's Chainlink\n * oracle price and its live Uniswap v3 DEX price, and alerts on premium/discount.\n * Trades the convergence ONLY when explicitly enabled AND the operator has\n * affirmed Stock Token eligibility — otherwise it is alerts-only, which is the\n * default and the recommended mode for anyone who hasn't cleared the legal gate\n * in `_shared.md`.\n *\n * EDGE HYPOTHESIS: Stock Token pools are much thinner than the underlying\n * equity market, so DEX price can drift from the Chainlink oracle (itself fed\n * by the real market) when flow is one-sided. If the drift is a liquidity\n * artifact rather than new information, buying the discount / shorting the\n * premium (here: buying the discount and later selling back at fair, since the\n * SDK has no short primitive) captures the reversion as the pool re-equilibrates\n * via arbitrage or the next informed trade.\n *\n * FAILURE MODES: a premium can be RIGHT — the DEX price can be reacting to\n * information the Chainlink heartbeat (up to 24h) hasn't caught up to yet, in\n * which case \"fading\" it loses money on purpose. Stock Token pools are thin;\n * the $10 probe used to read `stockDexPrice` may not reflect the price your\n * actual trade size gets, and the trade itself moves the price you're trying to\n * arbitrage. Only long convergence is possible (no shorting), so premiums\n * (DEX rich) are alert-only even when trading is enabled — asymmetric coverage\n * by construction. Chainlink staleness (weekends, feed outages) can make a\n * quote look like a discount when it is just old.\n */\nexport class PremiumWatch implements Strategy {\n readonly id = 'premium-watch'\n readonly title = 'Premium Watch'\n readonly quote = 'usdg' as const\n private readonly p: PremiumWatchParams\n\n constructor(params: Partial<PremiumWatchParams> = {}) {\n this.p = { ...DEFAULTS, ...params }\n }\n\n get meta(): StrategyMeta {\n return {\n edge:\n 'Track Chainlink-vs-DEX spread on Stock Tokens and, only in eligible+opted-in configuration, buy discounts ' +\n 'expecting reversion as the thin pool re-equilibrates. Default mode is alerts-only.',\n failureModes: [\n 'A premium/discount can be correct — DEX price may reflect information the ≤24h Chainlink heartbeat has not caught up to; fading it then loses on purpose.',\n 'Stock Token pools are thin; the probe price does not reflect real trade-size slippage, and the trade itself moves the spread being arbitraged.',\n 'No shorting primitive exists, so only discounts (DEX cheap) are ever tradeable — premiums are alert-only by construction, not by choice.',\n 'Chainlink staleness (weekend gap, feed outage) can masquerade as a discount when the oracle, not the pool, is stale.',\n 'Trading requires an explicit eligibility affirmation (Stock Tokens cannot be sold to US/CA/UK/CH persons) — default is alerts-only until the operator opts in.',\n ],\n params: { ...this.p, enableTrading: this.p.enableTrading },\n }\n }\n\n async tick(ctx: StrategyTickContext): Promise<Decision> {\n const intents: Intent[] = []\n const alerts: Alert[] = []\n const tradingLive = this.p.enableTrading && ctx.market.client.acknowledgeStockTokenEligibility\n\n if (this.p.enableTrading && !ctx.market.client.acknowledgeStockTokenEligibility) {\n alerts.push({\n level: 'warn',\n message:\n 'premium-watch: enableTrading=true but the client has not acknowledged Stock Token eligibility — staying alerts-only',\n })\n }\n\n // ── exits: convergence positions revert or time out ────────────────────────\n for (const pos of ctx.positions) {\n const symbol = pos.tokenSymbol\n const spread = await this.readSpread(ctx, symbol)\n const ageSec = (ctx.now - pos.openedAt) / 1000\n let exitReason: string | null = null\n if (spread && Math.abs(spread.bps) <= this.p.exitThresholdBps) {\n exitReason = `converged: spread ${spread.bps.toFixed(0)}bps within exit band`\n } else if (ageSec >= this.p.maxHoldSeconds) {\n exitReason = `time-exit ${Math.round(ageSec)}s held`\n }\n if (exitReason) {\n intents.push({\n side: 'sell',\n token: pos.token,\n tokenSymbol: symbol,\n amountIn: pos.amount,\n quoteToken: pos.quoteToken,\n quoteSymbol: pos.quoteSymbol,\n reason: exitReason,\n })\n }\n }\n\n // ── observe + (maybe) enter ─────────────────────────────────────────────────\n for (const symbol of this.p.symbols) {\n const spread = await this.readSpread(ctx, symbol)\n if (!spread) continue\n\n const direction = spread.bps > 0 ? 'premium (DEX rich)' : 'discount (DEX cheap)'\n if (Math.abs(spread.bps) >= this.p.alertThresholdBps) {\n alerts.push({\n level: 'warn',\n message: `${symbol} ${direction}: oracle $${spread.oracleUsd.toFixed(2)} vs DEX $${spread.dexUsd.toFixed(2)} (${spread.bps.toFixed(0)}bps)`,\n meta: { symbol, oracleUsd: spread.oracleUsd, dexUsd: spread.dexUsd, bps: spread.bps },\n })\n }\n\n const alreadyHeld = ctx.positions.some((p) => p.tokenSymbol === symbol)\n const isDiscount = spread.bps <= -this.p.tradeThresholdBps\n if (tradingLive && isDiscount && !alreadyHeld) {\n const token = getStockToken(symbol)\n intents.push({\n side: 'buy',\n token: token.address,\n tokenSymbol: symbol,\n amountIn: parseUsdg(String(this.p.tradeSizeUsdg)),\n quoteToken: ctx.quoteToken,\n quoteSymbol: ctx.quoteSymbol,\n reason: `convergence entry: ${symbol} discount ${spread.bps.toFixed(0)}bps (oracle $${spread.oracleUsd.toFixed(2)} vs DEX $${spread.dexUsd.toFixed(2)})`,\n meta: { symbol, oracleUsd: spread.oracleUsd, dexUsd: spread.dexUsd, bps: spread.bps },\n })\n } else if (isDiscount && !tradingLive) {\n alerts.push({\n level: 'info',\n message: `${symbol} discount ${spread.bps.toFixed(0)}bps clears trade threshold but trading is disabled/ineligible — alert only`,\n })\n }\n }\n\n return { intents, alerts }\n }\n\n private async readSpread(\n ctx: StrategyTickContext,\n symbol: string,\n ): Promise<{ oracleUsd: number; dexUsd: number; bps: number } | null> {\n const oracle = await ctx.market.stockChainlinkPrice(symbol)\n if (!oracle) return null\n const token = getStockToken(symbol)\n const dexUsd = await ctx.market.stockDexPrice(token.address, oracle.priceUsd)\n if (dexUsd === null) return null\n const bps = ((dexUsd - oracle.priceUsd) / oracle.priceUsd) * 10_000\n return { oracleUsd: oracle.priceUsd, dexUsd, bps }\n }\n}\n","import { createServer, type IncomingMessage, type Server, type ServerResponse } from 'node:http'\nimport { readFile } from 'node:fs/promises'\nimport { extname, join } from 'node:path'\nimport type { Fleet } from '../framework/fleet.js'\n\nconst MIME: Record<string, string> = {\n '.html': 'text/html; charset=utf-8',\n '.js': 'text/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.json': 'application/json; charset=utf-8',\n '.svg': 'image/svg+xml',\n}\n\nfunction json(res: ServerResponse, status: number, body: unknown): void {\n const payload = JSON.stringify(body, (_key, value) => (typeof value === 'bigint' ? value.toString() : value))\n res.writeHead(status, { 'content-type': 'application/json; charset=utf-8' })\n res.end(payload)\n}\n\nasync function serveStatic(staticRoot: string, req: IncomingMessage, res: ServerResponse): Promise<boolean> {\n const url = new URL(req.url ?? '/', 'http://localhost')\n let rel = url.pathname === '/' ? '/index.html' : url.pathname\n if (rel.includes('..')) return false\n const path = join(staticRoot, rel)\n try {\n const body = await readFile(path)\n res.writeHead(200, { 'content-type': MIME[extname(path)] ?? 'application/octet-stream' })\n res.end(body)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * The fleet dashboard: a tiny read API over {@link Fleet} plus a `POST /kill`\n * panic button, serving the static `dashboard/` UI. No framework — this is a\n * small, auditable surface that is also the E2E harness talks to.\n *\n * `staticRoot` is resolved by the caller rather than guessed from this\n * module's own location: tsup flattens `src/**​/*.ts` into a single-level\n * `dist/`, so a path relative to *this* file's directory would differ between\n * `tsx` (unbundled, nested under `src/server/`) and the built bundle (flat).\n * `main.ts` sits at a stable one-level depth in both trees, so it computes\n * the path once and passes it in.\n */\nexport function createDashboardServer(fleet: Fleet, staticRoot: string): Server {\n return createServer(async (req, res) => {\n const url = new URL(req.url ?? '/', 'http://localhost')\n\n if (url.pathname === '/api/summary' && req.method === 'GET') {\n return json(res, 200, fleet.summary())\n }\n if (url.pathname === '/api/agents' && req.method === 'GET') {\n return json(res, 200, fleet.agentStatuses())\n }\n if (url.pathname.startsWith('/api/agents/') && url.pathname.endsWith('/trades') && req.method === 'GET') {\n const agentId = decodeURIComponent(url.pathname.split('/')[3] ?? '')\n return json(res, 200, fleet.journal.recentTrades(agentId, 100))\n }\n if (url.pathname.startsWith('/api/agents/') && url.pathname.endsWith('/journal') && req.method === 'GET') {\n const agentId = decodeURIComponent(url.pathname.split('/')[3] ?? '')\n return json(res, 200, fleet.journal.recentDecisions(agentId, 200))\n }\n if (url.pathname.startsWith('/api/agents/') && url.pathname.endsWith('/equity') && req.method === 'GET') {\n const agentId = decodeURIComponent(url.pathname.split('/')[3] ?? '')\n return json(res, 200, fleet.journal.equityCurve(agentId, 500))\n }\n if (url.pathname === '/api/trades' && req.method === 'GET') {\n return json(res, 200, fleet.journal.allRecentTrades(100))\n }\n if (url.pathname === '/api/kill' && req.method === 'POST') {\n fleet.tripKill('dashboard')\n return json(res, 200, { killed: true, reason: 'dashboard' })\n }\n if (url.pathname === '/api/health' && req.method === 'GET') {\n return json(res, 200, { ok: true })\n }\n\n if (await serveStatic(staticRoot, req, res)) return\n res.writeHead(404, { 'content-type': 'text/plain' })\n res.end('not found')\n })\n}\n"],"mappings":"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","names":["formatUnits","round","formatUnits","DEFAULTS","shortToken","parseUsdg","DEFAULTS"]}