@sherwoodagent/cli 0.65.2 → 0.72.3

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.
Files changed (93) hide show
  1. package/dist/{abis-4HWN2E3H.js → abis-KP7M4IPM.js} +4 -2
  2. package/dist/addresses-YDPUPGWQ.js +45 -0
  3. package/dist/{agent-AETROOEW.js → agent-6IRZUPFT.js} +10 -10
  4. package/dist/{agent-AETROOEW.js.map → agent-6IRZUPFT.js.map} +1 -1
  5. package/dist/{chat-JSSTSE4K.js → chat-G5M3OK6M.js} +37 -36
  6. package/dist/chat-G5M3OK6M.js.map +1 -0
  7. package/dist/{chunk-IL43IMOC.js → chunk-3NMJ4LUL.js} +23 -18
  8. package/dist/chunk-3NMJ4LUL.js.map +1 -0
  9. package/dist/{chunk-UBAX22KA.js → chunk-52U76TRE.js} +4 -4
  10. package/dist/chunk-52U76TRE.js.map +1 -0
  11. package/dist/{chunk-W2ZUPR3W.js → chunk-5DWB5Y7G.js} +9 -9
  12. package/dist/chunk-5DWB5Y7G.js.map +1 -0
  13. package/dist/{chunk-YZHZSBES.js → chunk-BQMOE4FJ.js} +144 -37
  14. package/dist/chunk-BQMOE4FJ.js.map +1 -0
  15. package/dist/{chunk-VYF2TXPX.js → chunk-D4SPHYXA.js} +6 -6
  16. package/dist/{chunk-UCQEEF3Z.js → chunk-FOAX5PGR.js} +6 -6
  17. package/dist/chunk-FOAX5PGR.js.map +1 -0
  18. package/dist/chunk-FOS47623.js +127 -0
  19. package/dist/chunk-FOS47623.js.map +1 -0
  20. package/dist/{chunk-2W4KGGPU.js → chunk-GSC7PVEO.js} +27 -27
  21. package/dist/chunk-GSC7PVEO.js.map +1 -0
  22. package/dist/chunk-HYUUR6SC.js +111 -0
  23. package/dist/chunk-HYUUR6SC.js.map +1 -0
  24. package/dist/{chunk-RO26TXXE.js → chunk-NWY2U753.js} +2 -2
  25. package/dist/chunk-RAITRCM7.js +308 -0
  26. package/dist/chunk-RAITRCM7.js.map +1 -0
  27. package/dist/{chunk-A6TOJEUD.js → chunk-S6XA7PK4.js} +124 -128
  28. package/dist/chunk-S6XA7PK4.js.map +1 -0
  29. package/dist/{chunk-O4G3SBCI.js → chunk-UUHWD7Y3.js} +109 -129
  30. package/dist/chunk-UUHWD7Y3.js.map +1 -0
  31. package/dist/{chunk-YJSCIQUZ.js → chunk-WECQ6NVS.js} +6 -6
  32. package/dist/chunk-WECQ6NVS.js.map +1 -0
  33. package/dist/{chunk-RHRLYVKM.js → chunk-ZYXFKVYF.js} +25 -12
  34. package/dist/chunk-ZYXFKVYF.js.map +1 -0
  35. package/dist/{client-KBBFAMEI.js → client-QN6SA76M.js} +4 -4
  36. package/dist/{config-REASKDIK.js → config-FAQO37S7.js} +10 -10
  37. package/dist/{cron-EOULYO3R.js → cron-QMLRWDJG.js} +19 -17
  38. package/dist/cron-QMLRWDJG.js.map +1 -0
  39. package/dist/{eas-K4ZBACYN.js → eas-HM2AUOEY.js} +7 -7
  40. package/dist/{governor-2CDSMSG7.js → governor-RRBTDGDX.js} +17 -13
  41. package/dist/index.js +2026 -1222
  42. package/dist/index.js.map +1 -1
  43. package/dist/{ipfs-FAYWUCS2.js → ipfs-VGWTUORE.js} +2 -2
  44. package/dist/{network-3ZU7UBDU.js → network-L6JKNYOJ.js} +7 -7
  45. package/dist/{price-LCTECRQP.js → price-AJ4JMHCT.js} +6 -6
  46. package/dist/{research-FUQOQWKK.js → research-6VEEPEPP.js} +22 -22
  47. package/dist/research-6VEEPEPP.js.map +1 -0
  48. package/dist/research-7Z6SFPPX.js +14 -0
  49. package/dist/{session-FJB2RV2X.js → session-PURMITWE.js} +28 -28
  50. package/dist/session-PURMITWE.js.map +1 -0
  51. package/dist/{session-XAW5UIXP.js → session-XBQY7SBJ.js} +2 -2
  52. package/dist/{trade-U7H5IFH6.js → trade-YTGLGCRQ.js} +34 -34
  53. package/dist/trade-YTGLGCRQ.js.map +1 -0
  54. package/dist/vault-NIPA5JV4.js +54 -0
  55. package/dist/vault-NIPA5JV4.js.map +1 -0
  56. package/dist/{xmtp-6NJ4PW6L.js → xmtp-M32JSE4I.js} +13 -12
  57. package/dist/xmtp-M32JSE4I.js.map +1 -0
  58. package/package.json +4 -3
  59. package/dist/addresses-J2LMGUCT.js +0 -35
  60. package/dist/chat-JSSTSE4K.js.map +0 -1
  61. package/dist/chunk-2LRC7I33.js +0 -186
  62. package/dist/chunk-2LRC7I33.js.map +0 -1
  63. package/dist/chunk-2W4KGGPU.js.map +0 -1
  64. package/dist/chunk-A6TOJEUD.js.map +0 -1
  65. package/dist/chunk-GWMTAW6A.js +0 -494
  66. package/dist/chunk-GWMTAW6A.js.map +0 -1
  67. package/dist/chunk-IL43IMOC.js.map +0 -1
  68. package/dist/chunk-O4G3SBCI.js.map +0 -1
  69. package/dist/chunk-RHRLYVKM.js.map +0 -1
  70. package/dist/chunk-UBAX22KA.js.map +0 -1
  71. package/dist/chunk-UCQEEF3Z.js.map +0 -1
  72. package/dist/chunk-W2ZUPR3W.js.map +0 -1
  73. package/dist/chunk-YJSCIQUZ.js.map +0 -1
  74. package/dist/chunk-YZHZSBES.js.map +0 -1
  75. package/dist/cron-EOULYO3R.js.map +0 -1
  76. package/dist/research-FUQOQWKK.js.map +0 -1
  77. package/dist/research-XEXTHDT3.js +0 -14
  78. package/dist/session-FJB2RV2X.js.map +0 -1
  79. package/dist/trade-U7H5IFH6.js.map +0 -1
  80. package/dist/xmtp-6NJ4PW6L.js.map +0 -1
  81. /package/dist/{abis-4HWN2E3H.js.map → abis-KP7M4IPM.js.map} +0 -0
  82. /package/dist/{addresses-J2LMGUCT.js.map → addresses-YDPUPGWQ.js.map} +0 -0
  83. /package/dist/{chunk-VYF2TXPX.js.map → chunk-D4SPHYXA.js.map} +0 -0
  84. /package/dist/{chunk-RO26TXXE.js.map → chunk-NWY2U753.js.map} +0 -0
  85. /package/dist/{client-KBBFAMEI.js.map → client-QN6SA76M.js.map} +0 -0
  86. /package/dist/{config-REASKDIK.js.map → config-FAQO37S7.js.map} +0 -0
  87. /package/dist/{eas-K4ZBACYN.js.map → eas-HM2AUOEY.js.map} +0 -0
  88. /package/dist/{governor-2CDSMSG7.js.map → governor-RRBTDGDX.js.map} +0 -0
  89. /package/dist/{ipfs-FAYWUCS2.js.map → ipfs-VGWTUORE.js.map} +0 -0
  90. /package/dist/{network-3ZU7UBDU.js.map → network-L6JKNYOJ.js.map} +0 -0
  91. /package/dist/{price-LCTECRQP.js.map → price-AJ4JMHCT.js.map} +0 -0
  92. /package/dist/{research-XEXTHDT3.js.map → research-7Z6SFPPX.js.map} +0 -0
  93. /package/dist/{session-XAW5UIXP.js.map → session-XBQY7SBJ.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/commands/agent.ts","../src/agent/index.ts","../src/providers/data/defillama.ts","../src/providers/data/coingecko.ts","../src/providers/data/feargreed.ts","../src/providers/data/sentiment.ts","../src/agent/strategies/breakout-onchain.ts","../src/agent/strategies/funding-rate.ts","../src/providers/data/dexscreener.ts","../src/agent/strategies/hyperliquid-flow.ts","../src/agent/strategies/multi-timeframe.ts","../src/agent/strategies/cross-sectional-momentum.ts","../src/providers/data/tradingview.ts","../src/agent/strategies/tradingview-signal.ts","../src/agent/strategies/btc-network-health.ts","../src/agent/strategies/prediction-market.ts","../src/agent/strategies/social-volume.ts","../src/agent/strategies/kronos-vol-forecast.ts","../src/agent/strategies/narrative-vacuum.ts","../src/agent/strategies/whale-intent.ts","../src/agent/strategies/index.ts","../src/providers/data/funding-rate.ts","../src/providers/data/token-unlocks.ts","../src/providers/data/twitter.ts","../src/providers/fincept/bridge.ts","../src/providers/fincept/messari.ts","../src/providers/fincept/blockchain.ts","../src/providers/fincept/polymarket.ts","../src/providers/fincept/sentiment.ts","../src/providers/fincept/cryptocompare.ts","../src/providers/fincept/kronos.ts","../src/providers/fincept/defillama.ts","../src/providers/fincept/dexscreener.ts","../src/agent/signal-logger.ts","../src/agent/judge.ts","../src/agent/signal-smoother.ts","../src/agent/entry-gates.ts","../src/agent/regime.ts","../src/agent/correlation.ts","../src/agent/alerts.ts","../src/agent/token-selector.ts","../src/agent/loop.ts","../src/agent/executor.ts","../src/agent/portfolio.ts","../src/agent/risk.ts","../src/agent/reporter.ts","../src/agent/price-validator.ts","../src/agent/backtest.ts","../src/agent/calibrator.ts","../src/agent/replay-calibrator.ts","../src/agent/alert-formatter.ts","../src/agent/execution-pipeline.ts","../src/agent/signal-audit.ts"],"sourcesContent":["/**\n * CLI commands for the autonomous trading agent.\n * sherwood agent <analyze|signals|start|status|history|config|backtest>\n */\n\nimport { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { readFile, writeFile, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport ora from \"ora\";\nimport { TradingAgent } from \"../agent/index.js\";\nimport type { AgentConfig, TokenAnalysis, Alert } from \"../agent/index.js\";\nimport { getLatestSignals } from \"../agent/technical.js\";\nimport type { Candle } from \"../agent/technical.js\";\nimport { CoinGeckoProvider } from \"../providers/data/coingecko.js\";\nimport { DynamicTokenSelector } from \"../agent/token-selector.js\";\nimport { SentimentProvider } from \"../providers/data/sentiment.js\";\nimport {\n scoreTechnical,\n scoreSentiment,\n scoreOnChain,\n scoreFundamental,\n scoreEvent,\n} from \"../agent/scoring.js\";\nimport { AgentLoop } from \"../agent/loop.js\";\nimport type { LoopConfig } from \"../agent/loop.js\";\nimport { PortfolioTracker } from \"../agent/portfolio.js\";\nimport { RiskManager, DEFAULT_RISK_CONFIG } from \"../agent/risk.js\";\nimport type { RiskConfig } from \"../agent/risk.js\";\nimport { Reporter } from \"../agent/reporter.js\";\nimport { Backtester } from \"../agent/backtest.js\";\nimport type { BacktestConfig, WalkForwardConfig } from \"../agent/backtest.js\";\nimport { runCalibration, formatCalibrationTable, DEFAULT_CALIBRATION_TOKENS } from \"../agent/calibrator.js\";\nimport { runHistoryReplay } from \"../agent/replay-calibrator.js\";\nimport { AlertFormatter } from \"../agent/alert-formatter.js\";\nimport { ExecutionPipeline, DEFAULT_SCALED_SIZING } from \"../agent/execution-pipeline.js\";\nimport { auditSignalHistory, suggestRenormalizedWeights, diffAudits } from \"../agent/signal-audit.js\";\nimport type { AuditResult } from \"../agent/signal-audit.js\";\nimport { DEFAULT_WEIGHTS } from \"../agent/scoring.js\";\n\n// Expanded default universe — covers majors, L1/L2s, DeFi blue-chips, top\n// memes, and HL-listed alts so the scanner has more shots on goal each cycle.\n// Every token here has Hyperliquid perp coverage (executor.ts TOKEN_TO_ASSET_INDEX)\n// and CoinGecko OHLC. Use --auto for fully dynamic selection from HL volume.\nconst DEFAULT_TOKENS = [\n \"bitcoin\", \"ethereum\", \"solana\", // majors\n \"ripple\", \"dogecoin\", \"avalanche-2\", \"polkadot\", \"near\", // L1s\n \"arbitrum\", // L2\n \"aave\", \"uniswap\", \"chainlink\", // DeFi\n \"hyperliquid\", \"bittensor\", \"fartcoin\", // HL natives / themes\n];\n\nfunction makeConfig(overrides?: Partial<AgentConfig>): AgentConfig {\n return {\n tokens: DEFAULT_TOKENS,\n cycle: \"4h\",\n dryRun: true,\n maxPositionPct: 5,\n maxRiskPct: 20,\n useX402: true, // Paid signals (Nansen + Messari) on by default — opt out with --no-x402\n x402TopN: 1, // Only run x402 on top 1 token by free-signal score — 96 calls/day vs 288\n smoothFastSignals: true, // Rolling-window smoothing for noisy signals — prevents single-scan flicker\n // judge: undefined — off by default, enable with --use-judge\n ...overrides,\n };\n}\n\nexport function registerAgentCommands(program: Command): void {\n const agent = program.command(\"agent\").description(\"Autonomous trading agent\");\n\n // ── analyze ──\n agent\n .command(\"analyze\")\n .description(\"Analyze token(s) using multi-signal scoring\")\n .argument(\"[tokens...]\", \"Token IDs to analyze (e.g., ethereum bitcoin)\")\n .option(\"--all\", \"Analyze full watchlist\")\n .option(\"--auto\", \"Use dynamic token selection from Hyperliquid market data\")\n .option(\"--json\", \"Output as JSON\")\n .option(\"--no-x402\", \"Skip paid x402 data (Nansen smart-money, Messari fundamentals) — on by default\")\n .option(\"--telegram\", \"Output formatted summary for Telegram\")\n .option(\"--proposals\", \"Generate trade proposals for high-confidence opportunities\")\n .option(\"--weight-profile <name>\", \"Weight profile: default | majors | altcoin | sentHeavy | techHeavy (auto: majors for BTC/ETH/SOL)\")\n .option(\"--scaled-sizing\", \"Score-weighted position sizing (smaller positions for marginal scores)\")\n .option(\"--smooth\", \"Smooth fast signals with rolling 3-reading window\")\n .option(\"--use-judge\", \"Enable LLM judge to confirm/veto borderline trades (requires Anthropic API key)\")\n .option(\"--judge-model <model>\", \"Judge model ID (default: claude-haiku-4-5-20251001)\")\n .option(\"--judge-top-n <n>\", \"Max tokens to judge per cycle (default: 3)\", parseInt)\n .action(async (tokens: string[], options: { all?: boolean; auto?: boolean; json?: boolean; x402: boolean; telegram?: boolean; proposals?: boolean; weightProfile?: string; scaledSizing?: boolean; smooth?: boolean; useJudge?: boolean; judgeModel?: string; judgeTopN?: number }) => {\n let tokenList: string[];\n let selectionSummary: string | undefined;\n\n if (options.auto) {\n const spinner = ora(\"Fetching dynamic token selection from Hyperliquid...\").start();\n try {\n const selector = new DynamicTokenSelector();\n const selection = await selector.selectTokens();\n tokenList = selection.tokens;\n selectionSummary = selector.formatSelectionSummary(selection);\n spinner.stop();\n\n // Show selection summary\n console.log();\n console.log(selectionSummary);\n console.log();\n } catch (error) {\n spinner.fail(`Dynamic selection failed: ${(error as Error).message}`);\n console.log(\"Falling back to default tokens...\");\n tokenList = DEFAULT_TOKENS;\n }\n } else {\n tokenList = options.all ? DEFAULT_TOKENS : tokens.length > 0 ? tokens : DEFAULT_TOKENS;\n }\n\n const scaledSizing = options.scaledSizing\n ? { ...DEFAULT_SCALED_SIZING, enabled: true }\n : undefined;\n const config = makeConfig({\n tokens: tokenList, useX402: options.x402, weightProfile: options.weightProfile, smoothFastSignals: options.smooth,\n ...(options.useJudge ? {\n judge: {\n enabled: true,\n ...(options.judgeModel ? { model: options.judgeModel } : {}),\n ...(options.judgeTopN ? { topN: options.judgeTopN } : {}),\n },\n } : {}),\n });\n const tradingAgent = new TradingAgent(config);\n const spinner = ora(\"Analyzing tokens...\").start();\n\n try {\n spinner.text = \"Analyzing tokens and checking for alerts...\";\n const { analyses: results, alerts } = await tradingAgent.analyzeAllWithAlerts();\n spinner.stop();\n\n if (options.telegram) {\n // Output Telegram-formatted summary\n const regime = results[0]?.regime?.regime;\n const summary = AlertFormatter.formatScanSummary(results, alerts, regime);\n console.log(summary);\n return;\n }\n\n if (options.proposals) {\n // Output trade proposals\n const proposals = ExecutionPipeline.generateProposals(results, { scaledSizing });\n const formatted = ExecutionPipeline.formatProposals(proposals);\n console.log(formatted);\n return;\n }\n\n if (options.json) {\n console.log(JSON.stringify(results.map((r) => ({ token: r.token, decision: r.decision })), null, 2));\n } else {\n console.log(tradingAgent.formatAnalysis(results));\n\n // Show alerts if any\n if (alerts.length > 0) {\n console.log(chalk.bold(\"\\n\" + tradingAgent.formatAlerts(alerts)));\n console.log();\n }\n\n // Detailed signal breakdown\n for (const r of results) {\n console.log(chalk.bold(`\\n ${r.token.toUpperCase()} — Signal Breakdown`));\n console.log(chalk.dim(\" \" + \"─\".repeat(50)));\n\n // Show regime info if available\n if (r.regime) {\n const regimeColor = r.regime.regime === \"trending-up\" ? chalk.green :\n r.regime.regime === \"trending-down\" ? chalk.red :\n r.regime.regime === \"ranging\" ? chalk.yellow :\n r.regime.regime === \"high-volatility\" ? chalk.magenta :\n chalk.cyan;\n console.log(\n ` ${\"Market Regime\".padEnd(22)} ${regimeColor(\"▮\".repeat(10))} ${regimeColor(r.regime.regime.replace('-', ' '))} ${chalk.dim(r.regime.details.slice(0, 60))}`\n );\n }\n\n for (const signal of r.decision.signals) {\n const bar = renderBar(signal.value);\n const color =\n signal.value > 0.1\n ? chalk.green\n : signal.value < -0.1\n ? chalk.red\n : chalk.yellow;\n console.log(\n ` ${signal.source.padEnd(22)} ${bar} ${color(\n (signal.value >= 0 ? \"+\" : \"\") + signal.value.toFixed(3),\n )} ${chalk.dim(signal.details.slice(0, 60))}`,\n );\n }\n }\n console.log();\n }\n } catch (err) {\n spinner.fail(`Analysis failed: ${(err as Error).message}`);\n process.exitCode = 1;\n }\n });\n\n // ── signals ──\n agent\n .command(\"signals\")\n .description(\"Show current signal scores for a token\")\n .argument(\"<token>\", \"Token ID\")\n .action(async (token: string) => {\n const spinner = ora(`Fetching signals for ${token}...`).start();\n\n try {\n const cg = new CoinGeckoProvider();\n const sentimentProvider = new SentimentProvider();\n\n // Fetch OHLC\n spinner.text = \"Fetching OHLC data...\";\n const ohlcRaw = await cg.getOHLC(token, 30);\n const candles: Candle[] = ohlcRaw.map((c: number[]) => ({\n timestamp: c[0]!,\n open: c[1]!,\n high: c[2]!,\n low: c[3]!,\n close: c[4] ?? c[3]!,\n volume: 0,\n }));\n\n // Get volume data\n try {\n spinner.text = \"Fetching volume data...\";\n const marketData = await cg.getMarketData(token, 30);\n if (marketData?.total_volumes) {\n for (const candle of candles) {\n const nearest = marketData.total_volumes.reduce(\n (best: number[], v: number[]) =>\n Math.abs(v[0]! - candle.timestamp) < Math.abs(best[0]! - candle.timestamp)\n ? v\n : best,\n marketData.total_volumes[0]!,\n );\n candle.volume = nearest[1] ?? 0;\n }\n }\n } catch {\n // optional\n }\n\n const techSignals = getLatestSignals(candles);\n\n // Fetch sentiment\n spinner.text = \"Fetching sentiment...\";\n let fgValue = 50;\n let zScore = 0;\n try {\n const fgData = await sentimentProvider.getFearAndGreed();\n if (fgData.length > 0) {\n fgValue = fgData[0]!.value;\n zScore = sentimentProvider.computeSentimentZScore(fgData.map((d) => d.value));\n }\n } catch {\n // default values\n }\n\n spinner.stop();\n\n // Display\n console.log();\n console.log(chalk.bold(` ${token.toUpperCase()} — Detailed Signals`));\n console.log(chalk.dim(\" \" + \"═\".repeat(55)));\n\n // Technical\n console.log(chalk.bold.cyan(\"\\n Technical Indicators\"));\n console.log(` RSI(14): ${formatValue(techSignals.rsi, 30, 70, true)}`);\n console.log(` MACD: ${techSignals.macd.value.toFixed(4)}`);\n console.log(` MACD Signal: ${techSignals.macd.signal.toFixed(4)}`);\n console.log(` MACD Hist: ${formatValue(techSignals.macd.histogram, 0, 0, false)}`);\n console.log(` BB Upper: ${techSignals.bb.upper.toFixed(2)}`);\n console.log(` BB Middle: ${techSignals.bb.middle.toFixed(2)}`);\n console.log(` BB Lower: ${techSignals.bb.lower.toFixed(2)}`);\n console.log(` BB Width: ${techSignals.bb.width.toFixed(4)}`);\n console.log(` BB Squeeze: ${techSignals.bb.squeeze ? chalk.yellow(\"YES\") : \"No\"}`);\n console.log(` EMA(8): ${techSignals.ema.ema8.toFixed(2)}`);\n console.log(` EMA(21): ${techSignals.ema.ema21.toFixed(2)}`);\n console.log(` EMA(50): ${isNaN(techSignals.ema.ema50) ? chalk.dim(\"N/A\") : techSignals.ema.ema50.toFixed(2)}`);\n console.log(` EMA(200): ${isNaN(techSignals.ema.ema200) ? chalk.dim(\"N/A\") : techSignals.ema.ema200.toFixed(2)}`);\n console.log(` ATR(14): ${techSignals.atr.toFixed(2)}`);\n console.log(` VWAP: ${techSignals.vwap.toFixed(2)}`);\n console.log(` Vol Ratio: ${techSignals.volume.ratio.toFixed(2)}x avg`);\n\n // Sentiment\n console.log(chalk.bold.cyan(\"\\n Sentiment\"));\n console.log(` Fear & Greed: ${formatFearGreed(fgValue)}`);\n console.log(` Z-Score: ${zScore.toFixed(2)}`);\n\n // Signal scores\n const techScore = scoreTechnical(techSignals);\n const sentScore = scoreSentiment(fgValue, zScore);\n const onchainScore = scoreOnChain({});\n const fundScore = scoreFundamental({});\n const eventScore = scoreEvent({});\n\n console.log(chalk.bold.cyan(\"\\n Signal Scores\"));\n console.log(` Technical: ${renderBar(techScore.value)} ${colorValue(techScore.value)}`);\n console.log(` Sentiment: ${renderBar(sentScore.value)} ${colorValue(sentScore.value)}`);\n console.log(` On-Chain: ${renderBar(onchainScore.value)} ${colorValue(onchainScore.value)}`);\n console.log(` Fundamental: ${renderBar(fundScore.value)} ${colorValue(fundScore.value)}`);\n console.log(` Event: ${renderBar(eventScore.value)} ${colorValue(eventScore.value)}`);\n console.log();\n } catch (err) {\n spinner.fail(`Failed: ${(err as Error).message}`);\n process.exitCode = 1;\n }\n });\n\n // ── start ──\n agent\n .command(\"start\")\n .description(\"Start autonomous trading loop\")\n .option(\"--cycle <interval>\", \"Cycle interval (15m, 1h, 4h)\", \"4h\")\n .option(\"--dry-run\", \"Paper trading mode\", true)\n .option(\"--tokens <tokens>\", \"Comma-separated token list\")\n .option(\"--auto\", \"Use dynamic token selection from Hyperliquid market data\")\n .option(\"--log <path>\", \"Path to write cycle logs\")\n .option(\"--mode <mode>\", \"Execution mode: dry-run (default), hyperliquid-perp\", \"dry-run\")\n .option(\"--strategy-clone <address>\", \"Strategy clone address on HyperEVM (required for hyperliquid-perp)\")\n .option(\"--chain <chain>\", \"Chain for live execution (hyperevm, hyperevm-testnet)\", \"ethereum\")\n .option(\"--asset-index <n>\", \"HyperCore perp asset index (default: 3 = ETH)\")\n .option(\"--no-x402\", \"Skip paid x402 data (Nansen smart-money, Messari fundamentals) — on by default\")\n .option(\"--weight-profile <name>\", \"Weight profile: default | majors | altcoin | sentHeavy | techHeavy (auto: majors for BTC/ETH/SOL)\")\n .option(\"--scaled-sizing\", \"Score-weighted position sizing (smaller positions for marginal scores)\")\n .option(\"--smooth\", \"Smooth fast signals with rolling 3-reading window\")\n .option(\"--use-judge\", \"Enable LLM judge to confirm/veto borderline trades (requires Anthropic API key)\")\n .option(\"--judge-model <model>\", \"Judge model ID (default: claude-haiku-4-5-20251001)\")\n .option(\"--judge-top-n <n>\", \"Max tokens to judge per cycle (default: 3)\", parseInt)\n .action(async (options: { cycle?: string; dryRun?: boolean; tokens?: string; auto?: boolean; log?: string; mode?: string; strategyClone?: string; chain?: string; assetIndex?: string; x402: boolean; weightProfile?: string; scaledSizing?: boolean; smooth?: boolean; useJudge?: boolean; judgeModel?: string; judgeTopN?: number }) => {\n let tokenList: string[];\n\n if (options.auto) {\n console.log(chalk.dim(\" Using dynamic token selection from Hyperliquid (refreshes every 30 min)\"));\n // For the loop, we'll create a placeholder list that gets replaced dynamically\n // The actual selection happens in the loop itself to allow periodic refresh\n tokenList = ['bitcoin', 'ethereum']; // Minimal list, will be replaced\n } else {\n tokenList = options.tokens ? options.tokens.split(\",\").map((t) => t.trim()) : DEFAULT_TOKENS;\n }\n\n const cycle = (options.cycle ?? \"4h\") as AgentConfig[\"cycle\"];\n\n // Load persisted risk config from disk (written by `agent config --set`)\n let savedRiskConfig: Partial<RiskConfig> = {};\n try {\n const configPath = join(homedir(), '.sherwood', 'agent', 'config.json');\n const data = await readFile(configPath, 'utf-8');\n const parsed = JSON.parse(data) as Record<string, unknown>;\n // Only pick known risk keys with valid numeric values\n for (const [k, v] of Object.entries(parsed)) {\n if (k in DEFAULT_RISK_CONFIG && typeof v === 'number' && Number.isFinite(v)) {\n (savedRiskConfig as Record<string, number>)[k] = v;\n }\n }\n if (Object.keys(savedRiskConfig).length > 0) {\n console.log(chalk.dim(` Loaded ${Object.keys(savedRiskConfig).length} risk config overrides from ~/.sherwood/agent/config.json`));\n }\n } catch {\n // No saved config — use defaults\n }\n\n const isLive = options.mode === 'hyperliquid-perp';\n // NOTE: options.scaledSizing is currently a no-op here because the loop\n // does not call ExecutionPipeline.generateProposals. If that changes,\n // pass { scaledSizing } through LoopConfig and into the proposal call.\n if (isLive && !options.strategyClone) {\n console.error(chalk.red(' --strategy-clone is required for hyperliquid-perp mode'));\n process.exitCode = 1;\n return;\n }\n const proposerKey = process.env.SHERWOOD_PROPOSER_KEY as `0x${string}` | undefined;\n if (isLive && !proposerKey) {\n console.error(chalk.red(' SHERWOOD_PROPOSER_KEY env var is required for live execution'));\n process.exitCode = 1;\n return;\n }\n\n const loopConfig: LoopConfig = {\n agent: makeConfig({\n tokens: tokenList, cycle, dryRun: !isLive, useX402: options.x402,\n weightProfile: options.weightProfile, smoothFastSignals: options.smooth,\n ...(options.useJudge ? {\n judge: {\n enabled: true,\n ...(options.judgeModel ? { model: options.judgeModel } : {}),\n ...(options.judgeTopN ? { topN: options.judgeTopN } : {}),\n },\n } : {}),\n }),\n execution: {\n dryRun: !isLive,\n mode: (options.mode ?? 'dry-run') as 'dry-run' | 'hyperliquid-perp',\n mevProtection: false,\n chain: options.chain ?? 'ethereum',\n strategyClone: options.strategyClone as `0x${string}` | undefined,\n proposerPrivateKey: proposerKey,\n assetIndex: options.assetIndex ? (() => {\n const idx = parseInt(options.assetIndex!, 10);\n if (isNaN(idx) || idx < 0) {\n console.error(chalk.red(` Invalid --asset-index: ${options.assetIndex}`));\n process.exit(1);\n }\n return idx;\n })() : undefined,\n },\n riskConfig: savedRiskConfig,\n logPath: options.log,\n autoDynamicSelection: options.auto ?? false,\n };\n\n const loop = new AgentLoop(loopConfig);\n\n // Graceful shutdown\n const shutdown = () => {\n console.log(chalk.yellow(\"\\n Shutting down gracefully...\"));\n loop.stop();\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n\n await loop.start();\n });\n\n // ── status ──\n agent\n .command(\"status\")\n .description(\"Show current portfolio, open positions, daily PnL\")\n .action(async () => {\n const portfolio = new PortfolioTracker();\n const reporter = new Reporter();\n\n try {\n const state = await portfolio.load();\n\n // Try to update prices for open positions\n if (state.positions.length > 0) {\n try {\n const cg = new CoinGeckoProvider();\n const tokenIds = state.positions.map(p => p.tokenId);\n const priceData = await cg.getPrice(tokenIds, ['usd']);\n const prices: Record<string, number> = {};\n for (const id of tokenIds) {\n const price = priceData?.[id]?.usd;\n if (typeof price === 'number') prices[id] = price;\n }\n if (Object.keys(prices).length > 0) {\n const updated = await portfolio.updatePrices(prices);\n console.log(reporter.formatPortfolioReport(updated));\n return;\n }\n } catch {\n // Fall through to show cached state\n }\n }\n\n console.log(reporter.formatPortfolioReport(state));\n } catch (err) {\n console.error(chalk.red(`Failed to load portfolio: ${(err as Error).message}`));\n process.exitCode = 1;\n }\n });\n\n // ── summary ──\n agent\n .command(\"summary\")\n .description(\"Print a formatted cycle summary for XMTP/Telegram (pipe to chat send --stdin)\")\n .action(async () => {\n const { printSummary } = await import(\"../agent/summary-formatter.js\");\n await printSummary();\n });\n\n // ── history ──\n agent\n .command(\"history\")\n .description(\"Show trade history and performance metrics\")\n .option(\"--days <n>\", \"Number of days to look back\", \"30\")\n .action(async (options: { days?: string }) => {\n const portfolio = new PortfolioTracker();\n const reporter = new Reporter();\n const days = Math.max(1, parseInt(options.days ?? \"30\", 10) || 30);\n\n try {\n const trades = await portfolio.getHistory(days);\n const metrics = await portfolio.getMetrics(days);\n\n console.log('');\n console.log(chalk.bold(` Trade History (last ${days} days)`));\n console.log(chalk.dim(' ' + '═'.repeat(70)));\n\n if (trades.length === 0) {\n console.log(chalk.dim(' No trades recorded in this period.'));\n console.log('');\n return;\n }\n\n // Table header\n console.log(chalk.dim(` ${'Symbol'.padEnd(10)} ${'Side'.padEnd(6)} ${'Entry'.padEnd(12)} ${'Exit'.padEnd(12)} ${'PnL $'.padEnd(12)} ${'PnL %'.padEnd(10)} Exit Reason`));\n console.log(chalk.dim(' ' + '─'.repeat(70)));\n\n for (const t of trades) {\n const pnlColor = t.pnlUsd >= 0 ? chalk.green : chalk.red;\n console.log(\n ` ${t.symbol.padEnd(10)} ${t.side.padEnd(6)} $${t.entryPrice.toFixed(2).padEnd(11)} $${t.exitPrice.toFixed(2).padEnd(11)} ${pnlColor(('$' + t.pnlUsd.toFixed(2)).padEnd(12))} ${pnlColor(((t.pnlPercent * 100).toFixed(1) + '%').padEnd(10))} ${t.exitReason.slice(0, 20)}`,\n );\n }\n\n // Performance metrics\n console.log(reporter.formatMetrics(metrics));\n } catch (err) {\n console.error(chalk.red(`Failed to load history: ${(err as Error).message}`));\n process.exitCode = 1;\n }\n });\n\n // ── config ──\n agent\n .command(\"config\")\n .description(\"Show or set risk parameters\")\n .option(\"--set <key=value>\", \"Set a config value (e.g. --set maxSinglePosition=0.15)\")\n .action(async (options: { set?: string }) => {\n const riskConfig = { ...DEFAULT_RISK_CONFIG };\n\n // Bounds for risk config values to prevent disabling guardrails\n const RISK_BOUNDS: Record<string, [number, number]> = {\n maxPortfolioRisk: [0.01, 0.50],\n maxSinglePosition: [0.01, 0.25],\n maxCorrelatedExposure: [0.05, 0.50],\n maxConcurrentTrades: [1, 20],\n hardStopPercent: [0.01, 0.30],\n trailingStopAtr: [0.5, 5.0],\n dailyLossLimit: [0.01, 0.30],\n weeklyLossLimit: [0.01, 0.50],\n monthlyLossLimit: [0.01, 0.60],\n riskPerTrade: [0.005, 0.05],\n };\n\n if (options.set) {\n const [key, value] = options.set.split('=');\n if (key && value && key in riskConfig) {\n if (key === 'maxSlippage') {\n console.log(chalk.red(` maxSlippage must be set per tier (not supported via --set)`));\n console.log('');\n return;\n }\n const numVal = parseFloat(value);\n if (!isNaN(numVal)) {\n // Integer-type fields must not be fractional\n const INTEGER_KEYS = new Set(['maxConcurrentTrades']);\n if (INTEGER_KEYS.has(key) && !Number.isInteger(numVal)) {\n console.log(chalk.red(` ${key} must be a whole number (got ${numVal})`));\n console.log('');\n return;\n }\n const bounds = RISK_BOUNDS[key];\n if (bounds && (numVal < bounds[0] || numVal > bounds[1])) {\n console.log(chalk.red(` Value ${numVal} out of bounds for ${key}: [${bounds[0]}, ${bounds[1]}]`));\n console.log('');\n return;\n }\n (riskConfig as Record<string, unknown>)[key] = numVal;\n console.log(chalk.green(` Set ${key} = ${numVal}`));\n\n // Persist to disk\n const configDir = join(homedir(), '.sherwood', 'agent');\n const configPath = join(configDir, 'config.json');\n let existingConfig: Record<string, unknown> = {};\n try {\n const data = await readFile(configPath, 'utf-8');\n existingConfig = JSON.parse(data);\n } catch {\n // No existing config file\n }\n existingConfig[key] = numVal;\n await mkdir(configDir, { recursive: true });\n await writeFile(configPath, JSON.stringify(existingConfig, null, 2), 'utf-8');\n console.log(chalk.dim(` Saved to ${configPath}`));\n } else {\n console.log(chalk.red(` Invalid value: ${value}`));\n }\n } else {\n console.log(chalk.red(` Unknown config key: ${key}`));\n console.log(chalk.dim(` Available keys: ${Object.keys(riskConfig).join(', ')}`));\n }\n console.log('');\n return;\n }\n\n // Display current config\n console.log('');\n console.log(chalk.bold(' Risk Configuration'));\n console.log(chalk.dim(' ' + '═'.repeat(50)));\n console.log(` Max Portfolio Risk: ${(riskConfig.maxPortfolioRisk * 100).toFixed(0)}%`);\n console.log(` Max Single Position: ${(riskConfig.maxSinglePosition * 100).toFixed(0)}%`);\n console.log(` Max Correlated Exposure: ${(riskConfig.maxCorrelatedExposure * 100).toFixed(0)}%`);\n console.log(` Max Concurrent Trades: ${riskConfig.maxConcurrentTrades}`);\n console.log(` Hard Stop: ${(riskConfig.hardStopPercent * 100).toFixed(0)}%`);\n console.log(` Trailing Stop ATR: ${riskConfig.trailingStopAtr}x`);\n console.log(chalk.dim(' ' + '─'.repeat(50)));\n console.log(` Daily Loss Limit: ${(riskConfig.dailyLossLimit * 100).toFixed(0)}%`);\n console.log(` Weekly Loss Limit: ${(riskConfig.weeklyLossLimit * 100).toFixed(0)}%`);\n console.log(` Monthly Loss Limit: ${(riskConfig.monthlyLossLimit * 100).toFixed(0)}%`);\n console.log(chalk.dim(' ' + '─'.repeat(50)));\n console.log(` Max Slippage (large): ${(riskConfig.maxSlippage.large! * 100).toFixed(1)}%`);\n console.log(` Max Slippage (mid): ${(riskConfig.maxSlippage.mid! * 100).toFixed(1)}%`);\n console.log(` Max Slippage (small): ${(riskConfig.maxSlippage.small! * 100).toFixed(1)}%`);\n console.log('');\n });\n\n // ── backtest ──\n agent\n .command(\"backtest\")\n .description(\"Backtest strategies on historical data\")\n .argument(\"<token>\", \"Token ID (e.g., bitcoin, ethereum)\")\n .option(\"--from <date>\", \"Start date (YYYY-MM-DD)\", \"2024-01-01\")\n .option(\"--to <date>\", \"End date (YYYY-MM-DD)\", \"2024-12-31\")\n .option(\"--strategies <list>\", \"Comma-separated strategy names\", \"\")\n .option(\"--capital <amount>\", \"Initial capital in USD\", \"10000\")\n .option(\"--cycle <interval>\", \"Candle interval (1h, 4h, 1d)\", \"1d\")\n .option(\"--walk-forward\", \"Enable walk-forward optimization\")\n .option(\"--train <days>\", \"Training window in days for walk-forward\", \"90\")\n .option(\"--test <days>\", \"Test window in days for walk-forward\", \"30\")\n .option(\"--verbose\", \"Show detailed decision logs for each candle\")\n .option(\"--regime\", \"Apply regime-conditional thresholds per-candle (matches live behavior)\")\n .option(\"--trailing-stop <pct>\", \"Trailing stop as decimal (e.g. 0.05 = 5%) — exits if price drops X% from high-water mark\")\n .option(\"--scaled-sizing\", \"Score-weighted position sizing (smaller positions for marginal scores)\")\n .option(\"--weight-profile <name>\", \"Named weight profile: default | majors | altcoin | sentHeavy | techHeavy\")\n .option(\"--smooth\", \"Smooth fast signals (HL flow, smartMoney, dexFlow, fundingRate) with rolling 3-reading window\")\n .action(async (token: string, options: { from: string; to: string; strategies: string; capital: string; cycle: string; walkForward?: boolean; train: string; test: string; verbose?: boolean; regime?: boolean; trailingStop?: string; scaledSizing?: boolean; weightProfile?: string; smooth?: boolean }) => {\n const capital = parseFloat(options.capital);\n const strategies = options.strategies ? options.strategies.split(\",\").map((s) => s.trim()) : [];\n const trailingStopPct = options.trailingStop ? parseFloat(options.trailingStop) : undefined;\n if (trailingStopPct !== undefined && (!Number.isFinite(trailingStopPct) || trailingStopPct <= 0 || trailingStopPct > 0.5)) {\n console.error(chalk.red(\"--trailing-stop must be between 0 and 0.5 (e.g. 0.05 = 5%)\"));\n process.exitCode = 1;\n return;\n }\n // NOTE: options.scaledSizing is currently a no-op in backtest because\n // the backtester doesn't generate TradeProposals — it simulates its own\n // BUY/SELL directly from decision.action. Flag is parsed but unused.\n\n if (options.walkForward) {\n // Walk-forward optimization mode\n const trainWindow = parseInt(options.train, 10);\n const testWindow = parseInt(options.test, 10);\n const stepSize = Math.min(testWindow, 30); // Default step size to test window or 30 days, whichever is smaller\n const totalDays = trainWindow + (testWindow * 3); // Ensure we have enough data for multiple folds\n\n const walkConfig: WalkForwardConfig = {\n tokenId: token,\n totalDays,\n trainWindow,\n testWindow,\n stepSize,\n capital,\n strategies,\n useRegime: options.regime,\n trailingStopPct,\n smoothFastSignals: options.smooth,\n };\n\n const spinner = ora(`Walk-forward testing ${token} (${trainWindow}d train, ${testWindow}d test)...`).start();\n\n try {\n const backtester = new Backtester({\n tokenId: token,\n startDate: options.from,\n endDate: options.to,\n initialCapital: capital,\n strategies,\n cycle: (options.cycle as BacktestConfig[\"cycle\"]) || \"1d\",\n verbose: options.verbose,\n useRegime: options.regime,\n trailingStopPct,\n smoothFastSignals: options.smooth,\n });\n const result = await backtester.walkForwardTest(walkConfig);\n spinner.stop();\n console.log(backtester.formatWalkForwardResults(result, walkConfig));\n } catch (err) {\n spinner.fail(`Walk-forward test failed: ${(err as Error).message}`);\n process.exitCode = 1;\n }\n } else {\n // Regular backtest mode\n const config: BacktestConfig = {\n tokenId: token,\n startDate: options.from,\n endDate: options.to,\n initialCapital: capital,\n strategies,\n cycle: (options.cycle as BacktestConfig[\"cycle\"]) || \"1d\",\n verbose: options.verbose,\n useRegime: options.regime,\n trailingStopPct,\n smoothFastSignals: options.smooth,\n };\n\n const spinner = ora(`Backtesting ${token} from ${config.startDate} to ${config.endDate}...`).start();\n\n try {\n const backtester = new Backtester(config);\n const result = await backtester.run();\n spinner.stop();\n console.log(backtester.formatResults(result));\n } catch (err) {\n spinner.fail(`Backtest failed: ${(err as Error).message}`);\n process.exitCode = 1;\n }\n }\n });\n\n // ── calibrate ──\n agent\n .command(\"calibrate\")\n .description(\"Sweep weight profiles × BUY/SELL thresholds against historical data; ranks by Sharpe\")\n .option(\"--tokens <list>\", \"Comma-separated CoinGecko IDs (default: bitcoin,ethereum,solana,aave,uniswap,chainlink,arbitrum)\")\n .option(\"--days <n>\", \"Lookback window in days\", \"60\")\n .option(\"--capital <amount>\", \"Initial capital per token in USD\", \"10000\")\n .option(\"--top <n>\", \"Show top N configs in the ranked table\", \"20\")\n .option(\"--from-history [path]\", \"Replay against signal-history.jsonl (true production signal stack including HL/funding/dexFlow). Path defaults to ~/.sherwood/agent/signal-history.jsonl\")\n .option(\"--last <days>\", \"Replay only rows captured within the last N days (use after a scoring change to ignore stale captures). Replay path only.\")\n .option(\"--no-regime\", \"When using --from-history, ignore the regime field on each row (use DEFAULT_THRESHOLDS instead)\")\n .action(async (options: { tokens?: string; days: string; capital: string; top: string; fromHistory?: string | boolean; last?: string; regime: boolean }) => {\n const topN = parseInt(options.top, 10);\n\n // ── Replay path: signal-history.jsonl ──\n if (options.fromHistory) {\n const historyPath = typeof options.fromHistory === 'string' ? options.fromHistory : undefined;\n const tokens = options.tokens\n ? options.tokens.split(\",\").map((t) => t.trim()).filter(Boolean)\n : undefined; // default = all tokens in dataset\n\n let lastDays: number | undefined;\n if (options.last !== undefined) {\n lastDays = parseFloat(options.last);\n if (!Number.isFinite(lastDays) || lastDays <= 0) {\n console.error(chalk.red(`Invalid --last: ${options.last} (expected positive number of days)`));\n process.exitCode = 1;\n return;\n }\n }\n\n console.log(chalk.bold(`\\n Replaying calibration against signal-history.jsonl${lastDays ? ` (last ${lastDays}d)` : ''}...\\n`));\n try {\n const results = await runHistoryReplay({\n historyPath,\n tokens,\n lastDays,\n useRegime: options.regime,\n onProgress: (msg) => console.log(chalk.dim(` ${msg}`)),\n });\n console.log(formatCalibrationTable(results, topN));\n console.log(chalk.dim(` Source: signal-history replay (production signal stack)`));\n console.log(chalk.dim(` Saved: ~/.sherwood/agent/replay-calibration-results.json\\n`));\n } catch (err) {\n console.error(chalk.red(`\\n Replay calibration failed: ${(err as Error).message}\\n`));\n process.exitCode = 1;\n }\n return;\n }\n\n // ── Candle path: re-fetch from CoinGecko, recompute signals ──\n const tokens = options.tokens\n ? options.tokens.split(\",\").map((t) => t.trim()).filter(Boolean)\n : DEFAULT_CALIBRATION_TOKENS;\n const days = parseInt(options.days, 10);\n const capital = parseFloat(options.capital);\n\n if (!Number.isFinite(days) || days <= 0) {\n console.error(chalk.red(`Invalid --days: ${options.days}`));\n process.exitCode = 1;\n return;\n }\n if (!Number.isFinite(capital) || capital <= 0) {\n console.error(chalk.red(`Invalid --capital: ${options.capital}`));\n process.exitCode = 1;\n return;\n }\n\n console.log(chalk.bold(`\\n Calibrating ${tokens.length} tokens over ${days}d (candle-based, 200 configs per token)...\\n`));\n try {\n const results = await runCalibration({\n tokens,\n days,\n capital,\n onProgress: (msg) => console.log(chalk.dim(` ${msg}`)),\n });\n console.log(formatCalibrationTable(results, topN));\n } catch (err) {\n console.error(chalk.red(`\\n Calibration failed: ${(err as Error).message}\\n`));\n process.exitCode = 1;\n }\n });\n\n // ── autoresearch ──\n agent\n .command(\"autoresearch\")\n .description(\"Run autonomous parameter optimization against production signal history (Karpathy/Nunchi pattern)\")\n .option(\"--experiments <n>\", \"Number of experiments to run\", \"50\")\n .option(\"--last <days>\", \"Only use signal rows from the last N days\")\n .action(async (options: { experiments?: string; last?: string }) => {\n const { runAutoresearch } = await import(\"../agent/autoresearch.js\");\n await runAutoresearch({\n experiments: options.experiments ? parseInt(options.experiments, 10) : 50,\n lastDays: options.last ? parseFloat(options.last) : undefined,\n });\n });\n\n // ── signal-audit ──\n agent\n .command(\"signal-audit\")\n .description(\"Audit signal history — which signal categories actually fire?\")\n .option(\"--drop-threshold <rate>\", \"Fire-rate below which a category is flagged for dropping\", \"0.1\")\n .option(\"--json\", \"Emit raw JSON instead of formatted table\")\n .option(\"--save <path>\", \"Save current audit as a baseline JSON snapshot\")\n .option(\"--baseline <path>\", \"Diff current audit against a saved baseline\")\n .action(async (options: { dropThreshold?: string; json?: boolean; save?: string; baseline?: string }) => {\n const dropThreshold = parseFloat(options.dropThreshold ?? \"0.1\");\n const result = await auditSignalHistory();\n\n // --save: write snapshot and exit\n if (options.save) {\n await mkdir(join(homedir(), '.sherwood', 'agent'), { recursive: true });\n await writeFile(options.save, JSON.stringify(result, null, 2), 'utf-8');\n console.log(chalk.green(` Saved baseline snapshot to ${options.save}`));\n console.log(chalk.dim(` ${result.totalEntries} entries, ${result.perSignal.length} signals`));\n return;\n }\n\n // --baseline: load and diff\n if (options.baseline) {\n let baseline: AuditResult;\n try {\n const raw = await readFile(options.baseline, 'utf-8');\n baseline = JSON.parse(raw) as AuditResult;\n } catch (err) {\n console.error(chalk.red(` Failed to load baseline: ${(err as Error).message}`));\n process.exitCode = 1;\n return;\n }\n\n const diff = diffAudits(baseline, result);\n\n if (options.json) {\n console.log(JSON.stringify(diff, null, 2));\n return;\n }\n\n console.log(\"\");\n console.log(chalk.bold(\" Signal Audit Diff\"));\n console.log(chalk.dim(\" \" + \"═\".repeat(72)));\n if (baseline.dateRange && result.dateRange) {\n console.log(chalk.dim(` Baseline: ${baseline.dateRange.from.slice(0, 10)} → ${baseline.dateRange.to.slice(0, 10)} (${baseline.totalEntries} entries)`));\n console.log(chalk.dim(` Current: ${result.dateRange.from.slice(0, 10)} → ${result.dateRange.to.slice(0, 10)} (${result.totalEntries} entries)`));\n }\n console.log(\"\");\n\n // Per-category diff\n console.log(chalk.bold(\" Per Category\"));\n console.log(chalk.dim(` ${\"Category\".padEnd(14)} ${\"Was\".padEnd(7)} ${\"Now\".padEnd(7)} ${\"Δ\".padEnd(8)} Status`));\n console.log(chalk.dim(\" \" + \"─\".repeat(72)));\n for (const c of diff.perCategory) {\n const arrow = c.status === \"improved\" ? chalk.green(\"↑\") :\n c.status === \"regressed\" ? chalk.red(\"↓\") :\n c.status === \"new\" ? chalk.cyan(\"✦\") :\n c.status === \"removed\" ? chalk.red(\"✗\") : chalk.dim(\"=\");\n const deltaStr = (c.fireRateDelta >= 0 ? \"+\" : \"\") + (c.fireRateDelta * 100).toFixed(0) + \"pp\";\n const deltaColor = c.status === \"improved\" ? chalk.green : c.status === \"regressed\" ? chalk.red : chalk.dim;\n console.log(\n ` ${c.category.padEnd(14)} ${(c.baseline.fireRate * 100).toFixed(0).padStart(5)}% ${(c.current.fireRate * 100).toFixed(0).padStart(5)}% ${deltaColor(deltaStr.padEnd(7))} ${arrow} ${c.status}`,\n );\n }\n\n // Per-signal diff (only show changes)\n const changedSignals = diff.perSignal.filter((s) => s.status !== \"stable\");\n if (changedSignals.length > 0) {\n console.log(\"\");\n console.log(chalk.bold(\" Changed Signals\"));\n console.log(chalk.dim(` ${\"Signal\".padEnd(22)} ${\"Was\".padEnd(7)} ${\"Now\".padEnd(7)} ${\"Δ\".padEnd(8)} Status`));\n console.log(chalk.dim(\" \" + \"─\".repeat(72)));\n for (const s of changedSignals) {\n const arrow = s.status === \"improved\" ? chalk.green(\"↑\") :\n s.status === \"regressed\" ? chalk.red(\"↓\") :\n s.status === \"new\" ? chalk.cyan(\"✦\") :\n s.status === \"removed\" ? chalk.red(\"✗\") : chalk.dim(\"=\");\n const deltaStr = (s.fireRateDelta >= 0 ? \"+\" : \"\") + (s.fireRateDelta * 100).toFixed(0) + \"pp\";\n const deltaColor = s.status === \"improved\" ? chalk.green : s.status === \"regressed\" ? chalk.red : chalk.dim;\n console.log(\n ` ${s.name.padEnd(22)} ${(s.baseline.fireRate * 100).toFixed(0).padStart(5)}% ${(s.current.fireRate * 100).toFixed(0).padStart(5)}% ${deltaColor(deltaStr.padEnd(7))} ${arrow} ${s.status}`,\n );\n }\n } else {\n console.log(\"\");\n console.log(chalk.dim(\" No per-signal changes above 5pp threshold.\"));\n }\n console.log(\"\");\n return;\n }\n\n if (options.json) {\n console.log(JSON.stringify(result, null, 2));\n return;\n }\n\n console.log(\"\");\n console.log(chalk.bold(\" Signal Activity Audit\"));\n console.log(chalk.dim(\" \" + \"═\".repeat(72)));\n\n if (result.totalEntries === 0) {\n console.log(chalk.dim(` No signal history yet at ${result.filePath}`));\n console.log(chalk.dim(\" Run 'sherwood agent analyze' a few times to populate it.\"));\n console.log(\"\");\n return;\n }\n\n console.log(chalk.dim(` Source: ${result.filePath}`));\n console.log(chalk.dim(` Entries: ${result.totalEntries}`));\n if (result.dateRange) {\n console.log(\n chalk.dim(\n ` Range: ${result.dateRange.from.slice(0, 10)} → ${result.dateRange.to.slice(0, 10)}`,\n ),\n );\n }\n console.log(\"\");\n\n // Per-category summary\n console.log(chalk.bold(\" Per Category\"));\n console.log(\n chalk.dim(\n ` ${\"Category\".padEnd(14)} ${\"Fire%\".padEnd(7)} ${\"Obs\".padEnd(6)} ${\"Mean|v|\".padEnd(9)} Signals`,\n ),\n );\n console.log(chalk.dim(\" \" + \"─\".repeat(72)));\n for (const c of result.perCategory) {\n const color =\n c.recommendation === \"keep\"\n ? chalk.green\n : c.recommendation === \"drop\"\n ? chalk.red\n : chalk.yellow;\n const rec = `[${c.recommendation.toUpperCase()}]`;\n console.log(\n ` ${c.category.padEnd(14)} ${(c.fireRate * 100).toFixed(0).padStart(5)}% ${String(c.observations).padEnd(6)} ${c.meanAbsValue.toFixed(3).padEnd(9)} ${c.signalNames.join(\", \")} ${color(rec)}`,\n );\n }\n\n // Per-signal detail\n console.log(\"\");\n console.log(chalk.bold(\" Per Signal\"));\n console.log(\n chalk.dim(\n ` ${\"Signal\".padEnd(22)} ${\"Category\".padEnd(12)} ${\"Fire%\".padEnd(7)} ${\"Obs\".padEnd(6)} ${\"Mean|v|\".padEnd(9)} ${\"Bias\".padEnd(8)} Conf`,\n ),\n );\n console.log(chalk.dim(\" \" + \"─\".repeat(72)));\n for (const s of result.perSignal) {\n const biasColor = s.directionalBias >= 0 ? chalk.green : chalk.red;\n console.log(\n ` ${s.name.padEnd(22)} ${s.category.padEnd(12)} ${(s.fireRate * 100).toFixed(0).padStart(5)}% ${String(s.observations).padEnd(6)} ${s.meanAbsValue.toFixed(3).padEnd(9)} ${biasColor((s.directionalBias >= 0 ? \"+\" : \"\") + s.directionalBias.toFixed(3)).padEnd(17)} ${s.meanConfidence.toFixed(2)}`,\n );\n }\n\n // Renormalized weight suggestion\n const current = DEFAULT_WEIGHTS as unknown as Record<string, number>;\n const suggested = suggestRenormalizedWeights(current, result.perCategory, dropThreshold);\n const changed = Object.keys(current).some(\n (k) => Math.abs((suggested[k] ?? 0) - (current[k] ?? 0)) > 0.001,\n );\n\n if (changed) {\n console.log(\"\");\n console.log(chalk.bold(\" Suggested Weight Renormalization\"));\n console.log(chalk.dim(` (Dropping categories below ${(dropThreshold * 100).toFixed(0)}% fire rate)`));\n console.log(chalk.dim(\" \" + \"─\".repeat(72)));\n console.log(\n chalk.dim(\n ` ${\"Category\".padEnd(14)} ${\"Current\".padEnd(10)} ${\"Suggested\"}`,\n ),\n );\n for (const [cat, w] of Object.entries(current)) {\n const newW = suggested[cat] ?? 0;\n const tag =\n newW === 0 ? chalk.red(\" ← drop\") : newW > w ? chalk.green(\" ← boost\") : \"\";\n console.log(\n ` ${cat.padEnd(14)} ${w.toFixed(3).padEnd(10)} ${newW.toFixed(3)}${tag}`,\n );\n }\n } else {\n console.log(\"\");\n console.log(chalk.dim(\" All categories above drop threshold — no renormalization suggested.\"));\n }\n console.log(\"\");\n });\n\n // ── serve (dashboard API) ──\n agent\n .command(\"serve\")\n .description(\"Start dashboard API server (REST + WebSocket)\")\n .option(\"--port <number>\", \"Port to listen on\", \"3939\")\n .option(\"--host <string>\", \"Host to bind to (default: 127.0.0.1 if no token, 0.0.0.0 with token)\")\n .option(\"--token <string>\", \"Bearer token for remote access\")\n .action(async (opts: { port: string; host?: string; token?: string }) => {\n const { startServe } = await import(\"../agent/serve.js\");\n const port = parseInt(opts.port, 10);\n const host = opts.host ?? (opts.token ? \"0.0.0.0\" : \"127.0.0.1\");\n await startServe({ port, host, token: opts.token });\n });\n\n // ── alerts ──\n const alerts = agent.command(\"alerts\").description(\"Manage trading alerts\");\n\n alerts\n .command(\"list\")\n .alias(\"\")\n .description(\"Show recent alerts\")\n .option(\"--hours <n>\", \"Look back N hours\", \"24\")\n .action(async (options: { hours?: string }) => {\n try {\n const tradingAgent = new TradingAgent(makeConfig());\n const hours = parseInt(options.hours ?? \"24\", 10) || 24;\n const maxAge = hours * 60 * 60 * 1000;\n\n const recentAlerts = await tradingAgent.getRecentAlerts(maxAge);\n\n if (recentAlerts.length === 0) {\n console.log(chalk.dim(\" No alerts in the last \" + hours + \" hours.\"));\n return;\n }\n\n console.log('');\n console.log(chalk.bold(` Recent Alerts (last ${hours} hours)`));\n console.log(chalk.dim(' ' + '═'.repeat(60)));\n\n for (const alert of recentAlerts.slice(0, 20)) {\n const icon = alert.priority === \"critical\" ? \"🔴\" :\n alert.priority === \"high\" ? \"🟡\" :\n alert.priority === \"medium\" ? \"🔵\" : \"⚫\";\n\n const timeStr = new Date(alert.timestamp).toLocaleString();\n const priorityColor = alert.priority === \"critical\" ? chalk.red :\n alert.priority === \"high\" ? chalk.yellow :\n alert.priority === \"medium\" ? chalk.blue : chalk.gray;\n\n console.log(` ${icon} ${priorityColor(alert.priority.toUpperCase())} — ${alert.title}`);\n console.log(chalk.dim(` ${alert.details.slice(0, 80)}${alert.details.length > 80 ? '...' : ''}`));\n console.log(chalk.dim(` Token: ${alert.tokenId}, ${timeStr}`));\n console.log('');\n }\n } catch (err) {\n console.error(chalk.red(`Failed to load alerts: ${(err as Error).message}`));\n process.exitCode = 1;\n }\n });\n\n alerts\n .command(\"clear\")\n .description(\"Clear all alerts\")\n .action(async () => {\n try {\n const tradingAgent = new TradingAgent(makeConfig());\n await tradingAgent.clearAlerts();\n console.log(chalk.green(\" All alerts cleared.\"));\n } catch (err) {\n console.error(chalk.red(`Failed to clear alerts: ${(err as Error).message}`));\n process.exitCode = 1;\n }\n });\n}\n\n// ── Display Helpers ──\n\nfunction renderBar(value: number): string {\n const width = 20;\n const mid = Math.floor(width / 2);\n const bar = new Array(width).fill(\"░\");\n\n const magnitude = Math.min(Math.abs(value), 1.0);\n const filled = Math.round(magnitude * mid);\n\n if (value > 0) {\n for (let i = mid; i < mid + filled; i++) bar[i] = \"█\";\n return chalk.dim(bar.slice(0, mid).join(\"\")) + chalk.green(bar.slice(mid).join(\"\"));\n } else if (value < 0) {\n for (let i = mid - filled; i < mid; i++) bar[i] = \"█\";\n return chalk.red(bar.slice(0, mid).join(\"\")) + chalk.dim(bar.slice(mid).join(\"\"));\n }\n return chalk.dim(bar.join(\"\"));\n}\n\nfunction colorValue(value: number): string {\n const str = (value >= 0 ? \"+\" : \"\") + value.toFixed(3);\n if (value > 0.1) return chalk.green(str);\n if (value < -0.1) return chalk.red(str);\n return chalk.yellow(str);\n}\n\nfunction formatValue(value: number, low: number, high: number, invertColors: boolean): string {\n const str = isNaN(value) ? \"N/A\" : value.toFixed(2);\n if (isNaN(value)) return chalk.dim(str);\n if (invertColors) {\n if (value < low) return chalk.green(str);\n if (value > high) return chalk.red(str);\n } else {\n if (value > high) return chalk.green(str);\n if (value < low) return chalk.red(str);\n }\n return chalk.yellow(str);\n}\n\nfunction formatFearGreed(value: number): string {\n const str = `${value}`;\n if (value < 25) return chalk.green(str + \" (Extreme Fear)\");\n if (value < 40) return chalk.green(str + \" (Fear)\");\n if (value < 60) return chalk.yellow(str + \" (Neutral)\");\n if (value < 75) return chalk.red(str + \" (Greed)\");\n return chalk.red(str + \" (Extreme Greed)\");\n}\n","/**\n * Main trading agent orchestrator.\n * Gathers data from multiple providers, scores signals, produces trade decisions.\n */\n\nimport chalk from \"chalk\";\nimport { DefiLlamaProvider } from \"../providers/data/defillama.js\";\nimport { CoinGeckoProvider } from \"../providers/data/coingecko.js\";\nimport { SentimentProvider } from \"../providers/data/sentiment.js\";\nimport { getResearchProvider } from \"../providers/research/index.js\";\nimport type { Candle, TechnicalSignals } from \"./technical.js\";\nimport { getLatestSignals } from \"./technical.js\";\nimport {\n scoreTechnical,\n scoreSentiment,\n scoreFundamental,\n scoreEvent,\n computeTradeDecision,\n DEFAULT_WEIGHTS,\n profileForToken,\n} from \"./scoring.js\";\nimport type { Signal, ScoringWeights, TradeDecision } from \"./scoring.js\";\nimport { runStrategies } from \"./strategies/index.js\";\nimport { DexScreenerProvider } from \"../providers/data/dexscreener.js\";\nimport { FundingRateProvider } from \"../providers/data/funding-rate.js\";\nimport { TokenUnlocksProvider } from \"../providers/data/token-unlocks.js\";\nimport { TwitterSentimentProvider } from \"../providers/data/twitter.js\";\nimport {\n getMessariFundamentals,\n getBtcNetworkStats,\n getCryptoPredictions,\n getSocialData,\n getCryptoCompareCandles,\n getProtocolTvl,\n getKronosVolForecast,\n} from '../providers/fincept/index.js';\nimport { logSignal } from \"./signal-logger.js\";\nimport type { JudgeLogData } from \"./signal-logger.js\";\nimport { judge, selectJudgeCandidates, DEFAULT_JUDGE_CONFIG } from \"./judge.js\";\nimport type { JudgeConfig, JudgeVerdict, JudgeContext } from \"./judge.js\";\nimport { SignalSmoother, FileSmootherStorage, DEFAULT_SMOOTHER_CONFIG } from \"./signal-smoother.js\";\nimport { applyVelocityGate, applyRegimeGate, applyRealAlphaGate, resolveVelocity, DEFAULT_ENTRY_GATE_CONFIG } from \"./entry-gates.js\";\nimport type { EntryGateConfig } from \"./entry-gates.js\";\nimport { join as joinPath } from \"node:path\";\nimport { homedir as getHomedir } from \"node:os\";\nimport { HyperliquidProvider } from \"../providers/data/hyperliquid.js\";\nimport type { StrategyContext, StrategyConfig } from \"./strategies/index.js\";\nimport { MarketRegimeDetector } from \"./regime.js\";\nimport type { RegimeAnalysis } from \"./regime.js\";\nimport { CorrelationGuard } from \"./correlation.js\";\nimport type { CorrelationCheck } from \"./correlation.js\";\nimport { AlertSystem } from \"./alerts.js\";\nimport type { Alert } from \"./alerts.js\";\nimport type { PortfolioState } from \"./risk.js\";\nimport { isX402WalletFunded } from \"../lib/x402.js\";\n\nexport type { Signal, ScoringWeights, TradeDecision, Alert, TechnicalSignals };\n\nexport interface AgentConfig {\n tokens: string[];\n cycle: \"15m\" | \"1h\" | \"4h\";\n dryRun: boolean;\n maxPositionPct: number;\n maxRiskPct: number;\n weights?: ScoringWeights;\n /** Named weight profile (default | majors | altcoin | sentHeavy | techHeavy).\n * When set, overrides `weights` and is applied per-token via profileForToken().\n * When unset, BTC/ETH/SOL auto-get the \"majors\" profile, others use default. */\n weightProfile?: string;\n /** When true, includes paid x402 data (Nansen smart-money, Messari fundamentals) in analysis. */\n useX402?: boolean;\n /** Only run x402 paid signals on the top N tokens by free-signal score.\n * Remaining tokens use free signals only. Reduces x402 cost from ~$1.60/run\n * (10 tokens × $0.16) to ~$0.48/run (3 tokens × $0.16).\n * Set to 0 or undefined to run x402 on all tokens (old behavior). */\n x402TopN?: number;\n /** When true, smooth fast/noisy signals (HL flow, smartMoney, dexFlow, fundingRate)\n * with a rolling 3-reading average before scoring. Reduces single-scan flicker.\n * Default false. */\n smoothFastSignals?: boolean;\n /** Per-strategy configuration overrides. */\n strategyConfigs?: Record<string, StrategyConfig>;\n /** LLM judge configuration — confirms or vetoes borderline trade decisions. */\n judge?: Partial<JudgeConfig>;\n /** Entry-gate configuration — velocity/freshness checks applied post-scoring. */\n entryGates?: Partial<EntryGateConfig>;\n /** Backwards-compat opt-out: skip the velocity gate entirely. Equivalent to\n * `entryGates: { velocityGateEnabled: false }`. Default false (gate active). */\n disableVelocityGate?: boolean;\n}\n\nexport interface TokenAnalysis {\n token: string;\n decision: TradeDecision;\n data: {\n technicalSignals?: TechnicalSignals;\n fearAndGreed?: number;\n tvl?: number;\n price?: number;\n };\n regime?: RegimeAnalysis;\n correlation?: CorrelationCheck;\n /** LLM judge verdict (present only when judge is enabled and token was judged). */\n judgeResult?: { verdict: JudgeVerdict; cached: boolean; latencyMs: number };\n /** Original action/score before judge veto (present only when judge vetoed). */\n preJudge?: { action: string; score: number };\n /** Original action/score before velocity gate downgraded to HOLD. */\n preVelocity?: { action: string; score: number };\n /** Original action/score before regime gate blocked a short in non-bearish regime. */\n preRegime?: { action: string; score: number };\n /** Original action/score before real-alpha gate blocked a noisy-only entry. */\n preAlpha?: { action: string; score: number };\n /** Kronos ML-predicted per-candle volatility (fraction). Used by executor\n * for dynamic stop-loss width when available. */\n kronosVol4h?: number;\n}\n\nexport class TradingAgent {\n private config: AgentConfig;\n private defillama: DefiLlamaProvider;\n private coingecko: CoinGeckoProvider;\n private sentiment: SentimentProvider;\n private dexscreener: DexScreenerProvider;\n private fundingRate: FundingRateProvider;\n private tokenUnlocks: TokenUnlocksProvider;\n private twitter: TwitterSentimentProvider;\n private hyperliquid: HyperliquidProvider;\n private regimeDetector: MarketRegimeDetector;\n private correlationGuard: CorrelationGuard;\n private alertSystem: AlertSystem;\n private smoother: SignalSmoother | null = null;\n /** Optional getter for current portfolio state. When set, judge receives\n * real portfolio context (openCount, cashPct, etc.); otherwise judge falls\n * back to neutral defaults and its portfolio-veto branch is effectively\n * disabled. Wired in by AgentLoop via setPortfolioStateGetter(). */\n private portfolioStateGetter: (() => PortfolioState | undefined) | undefined;\n /** Warn-once flag when portfolio getter is missing at judge time. */\n private warnedNoPortfolioGetter = false;\n\n constructor(config: AgentConfig) {\n this.config = config;\n this.defillama = new DefiLlamaProvider();\n this.coingecko = new CoinGeckoProvider();\n this.sentiment = new SentimentProvider();\n this.dexscreener = new DexScreenerProvider();\n this.fundingRate = new FundingRateProvider();\n this.tokenUnlocks = new TokenUnlocksProvider();\n this.twitter = new TwitterSentimentProvider();\n this.hyperliquid = new HyperliquidProvider();\n this.regimeDetector = new MarketRegimeDetector();\n this.correlationGuard = new CorrelationGuard();\n this.alertSystem = new AlertSystem();\n }\n\n /** Analyze a single token — gather all data and score. */\n async analyzeToken(tokenId: string, opts?: { skipX402?: boolean; groupReturns?: Record<string, number> }): Promise<TokenAnalysis> {\n const signals: Signal[] = [];\n let technicalSignals: TechnicalSignals | undefined;\n let fearAndGreedValue: number | undefined;\n let tvl: number | undefined;\n let candles: Candle[] | undefined;\n let sentimentZScore: number | undefined;\n\n // Phase 1: Parallel basic data fetching\n // PRIMARY candle source is Hyperliquid (free, no rate limits, includes volume).\n // CoinGecko OHLC is FALLBACK only — the free tier 429s after ~5 tokens and the\n // circuit breaker kills the remaining 9+, leaving them with zero technical data.\n const [hlCandleResult, ohlcResult, fearGreedResult, tvlResult, hyperliquidResult] = await Promise.allSettled([\n // 1a. PRIMARY: Hyperliquid candles (4h over 30 days ≈ 180 bars — more than enough for EMA/RSI/MACD)\n this.hyperliquid.getCandles(tokenId, '4h', 30 * 24 * 60 * 60 * 1000),\n\n // 1b. FALLBACK: CryptoCompare candles via Fincept (replaces CoinGecko OHLC)\n getCryptoCompareCandles(tokenId, 180),\n\n // 2. Fetch Fear & Greed\n this.sentiment.getFearAndGreed(),\n\n // 3. Fetch TVL data (if DeFi protocol) — Fincept DefiLlama wrapper\n getProtocolTvl(tokenId).then((tvlValue) => {\n if (typeof tvlValue === \"number\" && tvlValue > 0) {\n return { tvl: tvlValue, mcapToTvl: undefined }; // mcapToTvl computed in Phase 2b via Messari\n }\n return null;\n }),\n\n // 4. Fetch Hyperliquid data (free, exchange-native) — move to Phase 1 for price substitution\n this.hyperliquid.getHyperliquidData(tokenId)\n ]);\n\n // Process candle results — prefer Hyperliquid (free, unlimited) over CoinGecko (rate-limited)\n const hlCandles = hlCandleResult.status === 'fulfilled' ? hlCandleResult.value : null;\n const cgCandles = ohlcResult.status === 'fulfilled' ? ohlcResult.value : null;\n const resolvedCandles = (hlCandles && hlCandles.length > 10) ? hlCandles : cgCandles;\n\n if (resolvedCandles && resolvedCandles.length > 10) {\n candles = resolvedCandles;\n technicalSignals = getLatestSignals(candles);\n signals.push(scoreTechnical(technicalSignals));\n\n // Price momentum signal — captures \"the market is moving\" before\n // lagging indicators (RSI, MACD, EMA) catch up. Uses the same candle\n // data we already have. Scored as a technical-category signal so it\n // adds to (not replaces) the existing technical analysis.\n if (candles.length >= 24) {\n const recent = candles.slice(-24);\n const currentClose = recent[recent.length - 1]!.close;\n const recentLow = Math.min(...recent.map((c) => c.low));\n const recentHigh = Math.max(...recent.map((c) => c.high));\n const pctFromLow = recentLow > 0 ? (currentClose - recentLow) / recentLow : 0;\n const pctFromHigh = recentHigh > 0 ? (recentHigh - currentClose) / recentHigh : 0;\n\n let momentumValue = 0;\n let momentumDetails = '';\n // Bullish momentum: price near 24-candle highs\n if (pctFromHigh < 0.01) {\n momentumValue = Math.min(0.6, pctFromLow * 5); // scales: 3% move → 0.15, 5% → 0.25, 10% → 0.50\n momentumDetails = `Price +${(pctFromLow * 100).toFixed(1)}% from 24-candle low, near highs`;\n }\n // Bearish momentum: price near 24-candle lows\n else if (pctFromLow < 0.01) {\n momentumValue = -Math.min(0.6, pctFromHigh * 5);\n momentumDetails = `Price -${(pctFromHigh * 100).toFixed(1)}% from 24-candle high, near lows`;\n }\n // Mid-range: proportional to position within range\n else if (recentHigh > recentLow) {\n const rangePosition = (currentClose - recentLow) / (recentHigh - recentLow); // 0=low, 1=high\n momentumValue = (rangePosition - 0.5) * 0.4; // -0.2 to +0.2\n momentumDetails = `Mid-range (${(rangePosition * 100).toFixed(0)}th percentile of 24-candle range)`;\n }\n\n if (Math.abs(momentumValue) > 0.02) {\n signals.push({\n name: 'momentum',\n value: momentumValue,\n confidence: Math.min(0.7, 0.3 + Math.abs(momentumValue)),\n source: 'Price Momentum',\n details: momentumDetails,\n });\n }\n }\n } else if (ohlcResult.status === 'rejected') {\n console.error(chalk.dim(` Technical analysis failed for ${tokenId}: ${ohlcResult.reason}`));\n }\n\n // Process Fear & Greed results\n if (fearGreedResult.status === 'fulfilled' && fearGreedResult.value?.length > 0) {\n const fgData = fearGreedResult.value;\n fearAndGreedValue = fgData[0]!.value;\n const values = fgData.map((d) => d.value);\n sentimentZScore = this.sentiment.computeSentimentZScore(values);\n // F&G is used as a regime-gate (extreme fear = allow BUY, extreme greed = allow SELL)\n // NOT as a scoring signal. It fires at +0.76 on 100% of observations when F&G < 25,\n // making it a constant bias rather than a directional signal. The sentimentContrarian\n // strategy (which modulates based on actual F&G value) remains active.\n // signals.push(scoreSentiment(fearAndGreedValue, sentimentZScore)); // REMOVED: constant bias\n } else if (fearGreedResult.status === 'rejected') {\n console.error(chalk.dim(` Sentiment data failed: ${fearGreedResult.reason}`));\n }\n\n // Process Hyperliquid results first to use for price substitution\n let hyperliquidData: any = undefined;\n if (hyperliquidResult.status === 'fulfilled' && hyperliquidResult.value) {\n hyperliquidData = hyperliquidResult.value;\n }\n\n // Process TVL results — mcapToTvl is now computed in Phase 2b via Messari\n if (tvlResult.status === 'fulfilled' && tvlResult.value) {\n tvl = tvlResult.value.tvl;\n }\n // scoreFundamental is called in Phase 2b after Messari data is available\n\n // Shared research data — captured in phase 2, reused in phase 3 strategies\n let nansenData: any = undefined;\n let messariData: any = undefined;\n // Nansen flow data for NarrativeVacuum + WhaleIntent strategies\n let nansenFlowData: StrategyContext['nansenFlowData'] = undefined;\n let nansenHlPerps: StrategyContext['nansenHlPerps'] = undefined;\n\n // Check x402 wallet USDC balance once per scan cycle.\n // If wallet is unfunded, skip x402 calls entirely to avoid diluting scores.\n // undefined = x402 not configured (don't exclude categories)\n // true = x402 configured + wallet funded\n // false = x402 configured but wallet empty → exclude dead categories\n const x402Enabled = this.config.useX402 && !opts?.skipX402;\n let x402Available: boolean | undefined = x402Enabled ? false : undefined;\n if (x402Enabled) {\n x402Available = await isX402WalletFunded();\n if (!x402Available) {\n console.error(chalk.yellow(` x402 wallet has insufficient USDC — skipping paid signals (smartMoney, event) for this cycle`));\n }\n }\n\n // Phase 2: Nansen smart-money data\n //\n // Fires when NANSEN_API_KEY is set (direct Pro API, ~$0.005/call) OR\n // when x402 is enabled + funded (micropayment, ~$0.06/call).\n // Two calls in parallel:\n // 1. Smart-money netflows → feeds NarrativeVacuum + WhaleIntent strategies\n // 2. HL perp trades → cross-venue confirmation for WhaleIntent\n const nansenAvailable = !!process.env.NANSEN_API_KEY || (x402Enabled && x402Available);\n if (nansenAvailable) {\n // Map tokenId to HL symbol for perp-trades query\n const hlSymbolMap: Record<string, string> = {\n bitcoin: \"BTC\", ethereum: \"ETH\", solana: \"SOL\",\n arbitrum: \"ARB\", aave: \"AAVE\", uniswap: \"UNI\",\n dogecoin: \"DOGE\", ripple: \"XRP\", hyperliquid: \"HYPE\",\n \"worldcoin-wld\": \"WLD\", bittensor: \"TAO\", zcash: \"ZEC\",\n fartcoin: \"FARTCOIN\", pepe: \"PEPE\", polkadot: \"DOT\",\n };\n const hlSymbol = hlSymbolMap[tokenId] ?? tokenId.toUpperCase();\n\n const nansenProvider = getResearchProvider(\"nansen\") as import(\"../providers/research/nansen.js\").NansenProvider;\n\n // Two Nansen calls in parallel:\n // 1. HL perp trades — cross-venue smart money positioning\n // 2. Smart money netflow — aggregate on-chain accumulation/distribution\n const [hlPerpResult, flowResult] = await Promise.allSettled([\n nansenProvider.queryHyperliquidSmartMoney(hlSymbol),\n nansenProvider.query({ type: 'smart-money', target: hlSymbol }),\n ]);\n\n // Process HL perp trades — derive a smartMoney signal from recent trade direction\n if (hlPerpResult.status === 'fulfilled') {\n const hlResult = hlPerpResult.value;\n const trades = hlResult.data.trades as Array<Record<string, unknown>> | undefined;\n if (trades && trades.length > 0) {\n // Count longs vs shorts among smart money's recent trades\n let longValueUsd = 0;\n let shortValueUsd = 0;\n for (const t of trades) {\n const val = Number(t.value_usd ?? 0);\n const side = String(t.side ?? '').toLowerCase();\n if (side === 'long') longValueUsd += val;\n else if (side === 'short') shortValueUsd += val;\n }\n const totalValue = longValueUsd + shortValueUsd;\n const longRatio = totalValue > 0 ? longValueUsd / totalValue : 0.5;\n // longRatio 0.7+ = smart money heavily long = bullish\n // longRatio 0.3- = smart money heavily short = bearish\n const smBias = (longRatio - 0.5) * 2; // -1 to +1\n\n // Push as a smartMoney signal. Scale to ±0.5 max (not ±0.8) —\n // a single Nansen snapshot shouldn't dominate the entire score.\n // At smartMoney weight 0.15 (majors profile), ±0.5 contributes\n // ±0.075 to aggregate — meaningful but well below sentiment/onchain.\n signals.push({\n name: 'smartMoney',\n value: smBias * 0.5,\n confidence: Math.min(0.7, 0.3 + (trades.length / 30) * 0.4),\n source: 'Nansen HL Smart Money',\n details: `${trades.length} trades: $${(longValueUsd / 1e6).toFixed(1)}M long / $${(shortValueUsd / 1e6).toFixed(1)}M short (${(longRatio * 100).toFixed(0)}% long)`,\n });\n console.error(chalk.dim(` Nansen HL perps: ${trades.length} trades, ${(longRatio * 100).toFixed(0)}% long bias, cost ${hlResult.costUsdc}`));\n // Capture for WhaleIntent strategy\n nansenHlPerps = { longRatio, tradeCount: trades.length, longValueUsd, shortValueUsd };\n // Derive flow data from HL perps when netflow endpoint fails (422 on token_symbol)\n if (!nansenFlowData) {\n const netFlowFromPerps = longValueUsd - shortValueUsd;\n nansenFlowData = { netFlow24hUsd: netFlowFromPerps, traderCount: trades.length };\n }\n } else {\n console.error(chalk.dim(` Nansen HL perps: no recent trades for ${hlSymbol}`));\n }\n } else {\n console.error(chalk.dim(` x402 Nansen HL perps unavailable: ${hlPerpResult.reason}`));\n }\n\n // Smart Money Netflow — aggregate accumulation/distribution across chains\n if (flowResult.status === 'fulfilled' && flowResult.value?.data) {\n const flowData = flowResult.value.data as Record<string, unknown>;\n const flows = flowData.flows as Array<Record<string, unknown>> | undefined;\n // Sum net_flow_24h_usd across all chains for this token\n let netFlow = 0;\n let traderCount = 0;\n if (flows && flows.length > 0) {\n for (const f of flows) {\n netFlow += Number(f.net_flow_24h_usd ?? 0);\n traderCount += Number(f.trader_count ?? 0);\n }\n } else {\n // Single-row response format\n netFlow = Number(flowData.net_flow_24h_usd ?? 0);\n traderCount = Number(flowData.trader_count ?? 0);\n }\n\n if (netFlow !== 0 && !isNaN(netFlow)) {\n const normalizedFlow = Math.max(-0.5, Math.min(0.5, netFlow / 2_000_000));\n signals.push({\n name: 'flowIntelligence',\n value: normalizedFlow,\n confidence: Math.min(0.7, 0.3 + Math.abs(normalizedFlow)),\n source: 'Nansen Smart Money Netflow',\n details: `Smart money 24h net flow: $${(netFlow / 1_000_000).toFixed(2)}M ${netFlow > 0 ? '(accumulating)' : '(distributing)'} (${traderCount} wallets)`,\n });\n console.error(chalk.dim(` Nansen netflow: $${(netFlow / 1_000_000).toFixed(2)}M, ${traderCount} wallets, cost ${flowResult.value.costUsdc}`));\n }\n // Capture for NarrativeVacuum + WhaleIntent strategies\n nansenFlowData = { netFlow24hUsd: netFlow, traderCount };\n }\n\n // Push event signal (no Messari — use free path)\n signals.push(scoreEvent({}));\n }\n // Free path: don't push empty fundamental/event signals that always return\n // value=0. They participate in per-category weight normalization and dilute\n // signals that actually fire. Only push when there's real data (TVL from\n // phase 1, or Nansen/Messari from x402).\n\n // Phase 2b: Fincept data (parallel, all fault-tolerant)\n const [messariResult, btcNetResult, predictionResult, socialResult, kronosResult] =\n await Promise.allSettled([\n getMessariFundamentals(tokenId),\n tokenId === 'bitcoin' ? getBtcNetworkStats() : Promise.resolve(null),\n getCryptoPredictions(),\n getSocialData(tokenId),\n // Kronos vol forecast — uses existing candles (1h cache, ~2.3s on CPU)\n candles && candles.length >= 30 ? getKronosVolForecast(tokenId, candles) : Promise.resolve(null),\n ]);\n\n const messariFundamentals = messariResult.status === 'fulfilled' ? messariResult.value ?? undefined : undefined;\n const btcNetworkData = btcNetResult.status === 'fulfilled' ? btcNetResult.value ?? undefined : undefined;\n const predictionData = predictionResult.status === 'fulfilled' && predictionResult.value?.length\n ? { markets: predictionResult.value }\n : undefined;\n const socialData = socialResult.status === 'fulfilled' ? socialResult.value ?? undefined : undefined;\n const kronosData = kronosResult.status === 'fulfilled' ? kronosResult.value ?? undefined : undefined;\n\n // Feed Messari fundamentals into scoreFundamental if available\n if (messariFundamentals && messariFundamentals.marketCap > 0) {\n const mcapToTvl = tvl && tvl > 0 ? messariFundamentals.marketCap / tvl : undefined;\n const existingFundIdx = signals.findIndex((s) => s.name === 'fundamental');\n const fundSignal = scoreFundamental({\n mcapToTvl,\n revenueGrowth: messariFundamentals.revenueGrowth7d,\n });\n if (existingFundIdx >= 0) {\n signals[existingFundIdx] = fundSignal;\n } else {\n signals.push(fundSignal);\n }\n }\n\n // Phase 3: Parallel strategy data fetching (symbol, funding rate, unlocks)\n let marketData: any = undefined;\n\n try {\n // Dead strategies removed in Apr 2026 audit (twitter, meanReversion,\n // tvlMomentum, tokenUnlock, smartMoney strategy).\n const [symbolResult, fundingRateResult, unlockResult] = await Promise.allSettled([\n // Resolve token symbol + get market data for strategies\n this.coingecko.getCoinDetails(tokenId).then(async (coinDetails) => {\n const symbol = coinDetails?.symbol?.toUpperCase();\n const marketData = await this.coingecko.getMarketData(tokenId, 7);\n return { symbol, marketData };\n }),\n\n // Fetch funding rate data (free, from Binance)\n this.fundingRate.getFundingRate(tokenId),\n\n // Fetch token unlock estimates (free, from DefiLlama FDV)\n this.tokenUnlocks.getUnlocks(tokenId),\n ]);\n\n // Process symbol/market data results\n let tokenSymbol: string | undefined;\n if (symbolResult.status === 'fulfilled' && symbolResult.value) {\n tokenSymbol = symbolResult.value.symbol;\n marketData = symbolResult.value.marketData;\n }\n\n // Process funding rate — prefer Hyperliquid (native, same venue we trade on).\n // Fall back to Binance only if HL doesn't cover this token.\n // HL publishes hourly funding; convert to 8h-equivalent so strategy\n // thresholds remain unit-consistent with Binance.\n let fundingRateData: StrategyContext['fundingRateData'] = undefined;\n if (hyperliquidData && typeof hyperliquidData.fundingRate === 'number' && Number.isFinite(hyperliquidData.fundingRate)) {\n const rate1h = hyperliquidData.fundingRate;\n const rate8h = rate1h * 8;\n const annualizedRate = rate1h * 24 * 365;\n fundingRateData = { rate8h, annualizedRate, exchange: 'hyperliquid' };\n } else if (fundingRateResult.status === 'fulfilled' && fundingRateResult.value) {\n const fr = fundingRateResult.value;\n fundingRateData = { rate8h: fr.rate8h, annualizedRate: fr.annualizedRate, exchange: fr.exchange };\n }\n\n // Process unlock results\n let unlockData: StrategyContext['unlockData'] = undefined;\n if (unlockResult.status === 'fulfilled' && unlockResult.value) {\n unlockData = unlockResult.value;\n }\n\n // Twitter data omitted — strategy disabled (see DEFAULT_STRATEGIES).\n const twitterData: StrategyContext['twitterData'] = undefined;\n\n // Hyperliquid data already processed in Phase 1\n\n const stratCtx: StrategyContext = {\n tokenId,\n candles, // reuse from phase 1\n technicals: technicalSignals,\n fearAndGreed: fearAndGreedValue !== undefined\n ? { value: fearAndGreedValue, classification: fearAndGreedValue < 25 ? 'fear' : fearAndGreedValue > 75 ? 'greed' : 'neutral' }\n : undefined,\n sentimentZScore, // reuse from phase 1\n tvlData: tvl,\n marketData,\n nansenData, // from phase 2\n messariData, // from phase 2\n dexData: undefined, // DexFlowStrategy fetches this internally\n fundingRateData, // from phase 3\n unlockData, // from phase 3\n twitterData, // from phase 3\n hyperliquidData, // from phase 3\n tokenSymbol, // from phase 3\n groupReturns: opts?.groupReturns, // cross-sectional momentum\n messariFundamentals,\n btcNetworkData,\n predictionData,\n socialData,\n kronosData,\n nansenFlowData,\n nansenHlPerps,\n };\n\n let strategySignals = await runStrategies(stratCtx, this.config.strategyConfigs);\n\n // Smooth fast/noisy signals (HL flow, smartMoney, dexFlow, fundingRate)\n // with a rolling 3-reading average. Slow signals pass through unchanged.\n // Disabled by default — enable via --smooth flag or config.smoothFastSignals.\n if (this.config.smoothFastSignals) {\n if (!this.smoother) {\n this.smoother = new SignalSmoother(\n new FileSmootherStorage(joinPath(getHomedir(), '.sherwood', 'agent', 'signal-cache.json')),\n DEFAULT_SMOOTHER_CONFIG,\n );\n }\n try {\n strategySignals = await this.smoother.smooth(tokenId, strategySignals);\n } catch (err) {\n console.error(chalk.dim(` Signal smoothing failed (using raw): ${(err as Error).message}`));\n }\n }\n\n // Merge strategy signals: only add those with meaningful confidence (>0.05)\n for (const sig of strategySignals) {\n if (sig.confidence > 0.05) {\n signals.push(sig);\n }\n }\n } catch (err) {\n console.error(chalk.dim(` Strategy modules failed: ${(err as Error).message}`));\n }\n\n // 6. Market regime detection\n let regimeAnalysis: RegimeAnalysis | undefined;\n try {\n let btcCandles: Candle[] = candles || [];\n\n // If analyzing a non-BTC token, fetch BTC candles for regime analysis.\n // Use Hyperliquid (free, no rate limits) instead of CoinGecko (429s, 400s).\n if (tokenId !== \"bitcoin\" && (!candles || candles.length < 60)) {\n const hlBtcCandles = await this.hyperliquid.getCandles(\"bitcoin\", \"4h\", 30 * 24 * 60 * 60 * 1000);\n if (hlBtcCandles && hlBtcCandles.length > 60) {\n btcCandles = hlBtcCandles;\n }\n }\n\n if (btcCandles.length > 60) {\n regimeAnalysis = await this.regimeDetector.detect(btcCandles);\n }\n } catch (err) {\n console.error(chalk.dim(` Regime detection failed: ${(err as Error).message}`));\n }\n\n // 7. BTC correlation check\n let correlationCheck: CorrelationCheck | undefined;\n try {\n correlationCheck = await this.correlationGuard.checkCorrelation(tokenId);\n } catch (err) {\n console.error(chalk.dim(` Correlation check failed: ${(err as Error).message}`));\n }\n\n // 8. Compute decision with regime adjustments, correlation suppression,\n // and regime-conditional action thresholds. Weights resolution order:\n // explicit this.config.weights > weightProfile > per-token auto-profile.\n const resolvedWeights = this.config.weights\n ?? profileForToken(tokenId, this.config.weightProfile);\n const decision = computeTradeDecision(\n signals,\n resolvedWeights,\n regimeAnalysis?.strategyAdjustments,\n correlationCheck,\n regimeAnalysis?.regime,\n x402Available,\n );\n\n const currentPrice = hyperliquidData?.markPrice\n ?? (candles && candles.length > 0 ? candles[candles.length - 1]!.close : 0);\n\n const result: TokenAnalysis = {\n token: tokenId,\n decision,\n data: { technicalSignals, fearAndGreed: fearAndGreedValue, tvl, price: currentPrice },\n regime: regimeAnalysis,\n correlation: correlationCheck,\n kronosVol4h: kronosData?.predictedVol4h,\n };\n\n // ── Velocity freshness gate (Orca-inspired) ──\n // Refuse BUY/SELL if the most recent short-term price move is flat or\n // opposed to the signal direction — prevents \"buying local tops\" and\n // \"shorting local bottoms\" when the composite score fires late.\n const gateConfig: EntryGateConfig = {\n ...DEFAULT_ENTRY_GATE_CONFIG,\n ...this.config.entryGates,\n // Legacy opt-out flag wins if set explicitly.\n ...(this.config.disableVelocityGate ? { velocityGateEnabled: false } : {}),\n };\n // Hyperliquid does not currently expose a `priceChg1h` field; resolveVelocity\n // is written to accept one in case the provider adds it later. For now we\n // always fall through to the candle-based derivation (4h cadence with\n // days=30 OHLC — tightest proxy available).\n const priceChg1h = (hyperliquidData as { priceChg1h?: number } | undefined)?.priceChg1h;\n const velocity = resolveVelocity(priceChg1h, candles);\n const velocityGated = applyVelocityGate(result, velocity, gateConfig, (msg) =>\n console.error(chalk.yellow(msg)),\n );\n\n // Regime gate — block shorts in non-bearish regimes (trending-up, ranging,\n // low-volatility). Trade log analysis: 25% short WR (-$194) vs 67% long WR\n // (+$279). Most short losses were counter-trend fades.\n const regimeGated = applyRegimeGate(velocityGated, regimeAnalysis?.regime, gateConfig, (msg) =>\n console.error(chalk.yellow(msg)),\n );\n\n // Real-alpha gate — block entries that are only supported by noisy inputs.\n const gatedResult = applyRealAlphaGate(regimeGated, gateConfig, (msg) =>\n console.error(chalk.yellow(msg)),\n );\n\n // Note: logSignal() is intentionally NOT called here. It runs in\n // analyzeAll() AFTER the judge pass, so judge verdicts can be included\n // in signal-history.jsonl. See analyzeAll() below.\n\n return gatedResult;\n }\n\n /** Update the token watchlist without recreating the agent (preserves caches). */\n updateTokens(tokens: string[]): void {\n this.config.tokens = tokens;\n }\n\n /** Register a getter for current portfolio state. Called by AgentLoop so\n * the judge can see real openCount / cashPct / daily PnL / cooldowns when\n * building its veto decisions. If not set, judge uses neutral defaults. */\n setPortfolioStateGetter(fn: () => PortfolioState | undefined): void {\n this.portfolioStateGetter = fn;\n }\n\n /** Analyze all watchlist tokens.\n *\n * When `x402TopN` is set and x402 is enabled, uses a two-pass approach:\n * 1. Score ALL tokens with free signals only (no x402 cost)\n * 2. Re-score the top N tokens by free-signal score WITH x402 paid data\n *\n * This reduces x402 cost from ~$0.16×10 = $1.60/run to ~$0.16×3 = $0.48/run\n * while concentrating paid data on the tokens most likely to fire trades.\n */\n async analyzeAll(): Promise<TokenAnalysis[]> {\n const topN = this.config.x402TopN;\n const shouldTwoPass = this.config.useX402 && topN && topN > 0 && topN < this.config.tokens.length;\n\n // ── Cross-sectional momentum: pre-compute 7-day returns for all tokens ──\n // Fetches 1d candles (8 days lookback) from Hyperliquid for each token.\n // These are lightweight requests (8 data points each) and Hyperliquid has\n // no rate limit, so the overhead is minimal (~50-100ms total, parallelized).\n const groupReturns: Record<string, number> = {};\n try {\n const returnFetches = this.config.tokens.map(async (token) => {\n const candles = await this.hyperliquid.getCandles(token, '1d', 8 * 24 * 60 * 60 * 1000);\n if (candles && candles.length >= 2) {\n const firstClose = candles[0]!.close;\n const lastClose = candles[candles.length - 1]!.close;\n if (firstClose > 0) {\n groupReturns[token] = (lastClose / firstClose) - 1;\n }\n }\n });\n await Promise.all(returnFetches);\n } catch (err) {\n console.error(chalk.dim(` Cross-sectional group returns failed: ${(err as Error).message}`));\n }\n const hasGroupReturns = Object.keys(groupReturns).length >= 3;\n\n let results: TokenAnalysis[];\n\n if (!shouldTwoPass) {\n // Original single-pass: analyze all tokens with whatever x402 config says\n results = [];\n for (const token of this.config.tokens) {\n const result = await this.analyzeToken(token, {\n groupReturns: hasGroupReturns ? groupReturns : undefined,\n });\n results.push(result);\n }\n } else {\n // Pass 1: all tokens with free signals only\n console.error(chalk.dim(` x402 top-${topN} mode: scoring all ${this.config.tokens.length} tokens with free signals first...`));\n const freeResults: TokenAnalysis[] = [];\n for (const token of this.config.tokens) {\n const result = await this.analyzeToken(token, {\n skipX402: true,\n groupReturns: hasGroupReturns ? groupReturns : undefined,\n });\n freeResults.push(result);\n }\n\n // Sort by absolute score descending — top N get the x402 enrichment\n const ranked = [...freeResults].sort(\n (a, b) => Math.abs(b.decision.score) - Math.abs(a.decision.score),\n );\n const topTokens = new Set(ranked.slice(0, topN).map((r) => r.token));\n console.error(chalk.dim(` x402 enriching top ${topN}: ${[...topTokens].join(', ')}`));\n\n // Pass 2: re-analyze top N with x402 enabled\n const enrichedMap = new Map<string, TokenAnalysis>();\n for (const token of topTokens) {\n const enriched = await this.analyzeToken(token, {\n groupReturns: hasGroupReturns ? groupReturns : undefined,\n });\n enrichedMap.set(token, enriched);\n }\n\n // Merge: use enriched results for top N, free results for the rest\n results = freeResults.map((r) => enrichedMap.get(r.token) ?? r);\n }\n\n // ── LLM Judge pass ──\n // After all tokens scored, apply the judge to borderline actionable decisions.\n // Budget-gated: only the top-N borderline candidates are judged per cycle.\n const judgeConfig: JudgeConfig = { ...DEFAULT_JUDGE_CONFIG, ...this.config.judge };\n if (judgeConfig.enabled) {\n const candidates = selectJudgeCandidates(\n results.map((r) => ({ token: r.token, score: r.decision.score, action: r.decision.action })),\n judgeConfig,\n );\n\n if (candidates.size > 0) {\n console.error(chalk.dim(` [judge] Reviewing ${candidates.size} borderline decision(s): ${[...candidates].join(', ')}`));\n }\n\n // Snapshot portfolio state once for the judge pass — all tokens in this\n // cycle see the same portfolio view. Cheaper than a per-token load and\n // avoids races with any concurrent mutation.\n const pstate = this.portfolioStateGetter?.();\n if (!pstate && candidates.size > 0 && !this.warnedNoPortfolioGetter) {\n console.warn(chalk.yellow(\n ` [judge] Warning: portfolio state getter not wired. Judge portfolio-veto branch disabled — using neutral defaults.`,\n ));\n this.warnedNoPortfolioGetter = true;\n }\n\n for (const result of results) {\n if (!candidates.has(result.token)) continue;\n\n // Build real portfolio snapshot for the judge. Falls back to neutral\n // defaults when the getter isn't wired (e.g. one-shot analyzeAll callers).\n let portfolio: JudgeContext[\"portfolio\"] = {\n openCount: 0, cashPct: 1, hasPositionThisToken: false, inStopCooldown: false, dailyPnlPct: 0,\n };\n if (pstate) {\n const totalValue = pstate.totalValue || pstate.cash || 0;\n const cooldowns = pstate.stopCooldowns ?? {};\n const cooldownAt = cooldowns[result.token];\n // Match RiskManager's STOP_COOLDOWN_MS (4h). We only need to know\n // whether a cooldown is currently active, not the exact deadline.\n const STOP_COOLDOWN_MS = 4 * 60 * 60 * 1000;\n const inStopCooldown = cooldownAt !== undefined &&\n (Date.now() - cooldownAt) < STOP_COOLDOWN_MS;\n // dailyPnlPct ≈ dailyPnl / (totalValue − dailyPnl), reconstructing\n // the start-of-day value from current total minus today's delta.\n const startOfDayValue = totalValue - pstate.dailyPnl;\n portfolio = {\n openCount: pstate.positions.length,\n cashPct: totalValue > 0 ? pstate.cash / totalValue : 1,\n hasPositionThisToken: !!pstate.positions.find((p) => p.tokenId === result.token),\n inStopCooldown,\n dailyPnlPct: startOfDayValue > 0 ? pstate.dailyPnl / startOfDayValue : 0,\n };\n }\n\n const ctx: JudgeContext = {\n tokenId: result.token,\n currentPrice: result.data.price ?? 0,\n decision: result.decision,\n technicalSignals: result.data.technicalSignals,\n fearAndGreed: result.data.fearAndGreed,\n regime: result.regime?.regime,\n btcBias: result.correlation?.btcBias,\n suppressionFactor: result.correlation?.suppressionFactor,\n portfolio,\n };\n\n const judgeResult = await judge(ctx, judgeConfig);\n result.judgeResult = judgeResult;\n\n if (judgeResult.verdict.verdict === \"veto\") {\n result.preJudge = { action: result.decision.action, score: result.decision.score };\n result.decision = { ...result.decision, action: \"HOLD\" };\n console.error(chalk.yellow(` [judge] VETO ${result.token}: ${judgeResult.verdict.reasoning}`));\n } else if (judgeResult.verdict.verdict === \"confirm\" && judgeResult.verdict.reasoning !== \"fallback\") {\n console.error(chalk.dim(` [judge] Confirmed ${result.token} (${judgeResult.cached ? \"cached\" : `${judgeResult.latencyMs}ms`})`));\n }\n }\n }\n\n // Persist analysis to ~/.sherwood/agent/signal-history.jsonl. Runs AFTER\n // the judge pass so judgeVerdict fields land in signal-history.jsonl.\n // Fire-and-forget — never blocks scoring, never crashes on disk failure.\n for (const result of results) {\n const resolvedWeights = this.config.weights\n ?? profileForToken(result.token, this.config.weightProfile);\n const price = result.data.price ?? 0;\n const judgeData: JudgeLogData | undefined = result.judgeResult\n ? {\n verdict: result.judgeResult.verdict,\n // Prefer pre-veto action/score when judge vetoed; otherwise log\n // current (post-judge) action/score — they're identical on confirm.\n preJudgeAction: result.preJudge?.action ?? result.decision.action,\n preJudgeScore: result.preJudge?.score ?? result.decision.score,\n latencyMs: result.judgeResult.latencyMs,\n cached: result.judgeResult.cached,\n }\n : undefined;\n logSignal(result, price, resolvedWeights, judgeData);\n }\n\n return results;\n }\n\n /** Analyze all tokens and generate alerts for state changes. */\n async analyzeAllWithAlerts(): Promise<{ analyses: TokenAnalysis[]; alerts: Alert[] }> {\n const analyses = await this.analyzeAll();\n const alerts = await this.alertSystem.processAnalysis(analyses);\n return { analyses, alerts };\n }\n\n /** Get recent alerts. */\n async getRecentAlerts(maxAge?: number): Promise<Alert[]> {\n return this.alertSystem.getRecentAlerts(maxAge);\n }\n\n /** Clear all alerts. */\n async clearAlerts(): Promise<void> {\n return this.alertSystem.clearAlerts();\n }\n\n /** Get urgent alerts (CRITICAL/HIGH from last 30min) and mark as sent. */\n async getUrgentAlerts(): Promise<Alert[]> {\n return this.alertSystem.getUrgentAlerts();\n }\n\n /** Format alerts for display. */\n formatAlerts(alerts: Alert[], useMarkdown: boolean = false): string {\n return this.alertSystem.formatAlerts(alerts, useMarkdown);\n }\n\n /** Format analysis results for display. */\n formatAnalysis(results: TokenAnalysis[]): string {\n const lines: string[] = [];\n\n // Header\n lines.push(\"\");\n lines.push(chalk.bold(\" Sherwood Trading Agent — Analysis Results\"));\n lines.push(chalk.dim(\" \" + \"─\".repeat(60)));\n\n // Market regime display (use the first result's regime if available)\n const regime = results[0]?.regime;\n if (regime) {\n const regimeColor = regime.regime === \"trending-up\" ? chalk.green :\n regime.regime === \"trending-down\" ? chalk.red :\n regime.regime === \"ranging\" ? chalk.yellow :\n regime.regime === \"high-volatility\" ? chalk.magenta :\n chalk.cyan;\n\n const regimeStr = regimeColor(regime.regime.toUpperCase().replace('-', ' '));\n const confidenceStr = `${Math.round(regime.confidence * 100)}%`;\n const trendStr = regime.btcTrend === \"up\" ? chalk.green(\"↑\") :\n regime.btcTrend === \"down\" ? chalk.red(\"↓\") :\n chalk.yellow(\"→\");\n\n lines.push(` Market Regime: ${regimeStr} (${confidenceStr} confidence) | BTC trend: ${trendStr} | Volatility: ${regime.volatilityLevel}`);\n lines.push(chalk.dim(\" \" + \"─\".repeat(60)));\n }\n\n // BTC correlation display (use the first non-BTC result's correlation if available)\n const correlation = results.find(r => r.token !== \"bitcoin\")?.correlation;\n if (correlation) {\n const biasColor = correlation.btcBias === \"bullish\" ? chalk.green :\n correlation.btcBias === \"bearish\" ? chalk.red :\n chalk.yellow;\n\n const biasStr = biasColor(correlation.btcBias.toUpperCase());\n const scoreStr = correlation.btcScore >= 0 ? `+${correlation.btcScore.toFixed(2)}` : correlation.btcScore.toFixed(2);\n\n let suppressionStr = \"\";\n if (correlation.shouldSuppress && correlation.btcBias === \"bearish\") {\n const suppressionPct = Math.round((1 - correlation.suppressionFactor) * 100);\n suppressionStr = ` — alt longs suppressed ${suppressionPct}%`;\n } else if (!correlation.shouldSuppress && correlation.btcBias === \"bullish\") {\n const boostPct = Math.round((correlation.suppressionFactor - 1) * 100);\n if (boostPct > 0) suppressionStr = ` — alt longs boosted ${boostPct}%`;\n }\n\n lines.push(` BTC Correlation: ${biasStr} (${scoreStr})${suppressionStr}`);\n lines.push(chalk.dim(\" \" + \"─\".repeat(60)));\n }\n\n lines.push(\"\");\n\n // Column headers\n const header = ` ${\"Token\".padEnd(14)} ${\"Score\".padEnd(8)} ${\"Action\".padEnd(14)} ${\"Conf\".padEnd(8)} Key Signals`;\n lines.push(chalk.bold(header));\n lines.push(chalk.dim(\" \" + \"─\".repeat(80)));\n\n for (const r of results) {\n const d = r.decision;\n\n // Color the action\n let actionStr: string;\n switch (d.action) {\n case \"STRONG_BUY\":\n actionStr = chalk.bgGreen.black(\" STRONG BUY \");\n break;\n case \"BUY\":\n actionStr = chalk.green(\"BUY\");\n break;\n case \"HOLD\":\n actionStr = chalk.yellow(\"HOLD\");\n break;\n case \"SELL\":\n actionStr = chalk.red(\"SELL\");\n break;\n case \"STRONG_SELL\":\n actionStr = chalk.bgRed.white(\" STRONG SELL \");\n break;\n }\n\n // Score with color\n const scoreColor = d.score > 0.3 ? chalk.green : d.score < -0.3 ? chalk.red : chalk.yellow;\n const scoreStr = scoreColor(d.score.toFixed(3).padStart(7));\n\n // Confidence\n const confStr = `${(d.confidence * 100).toFixed(0)}%`;\n\n // Key signals (first 2)\n const keySignals = d.signals\n .filter((s) => Math.abs(s.value) > 0.1)\n .slice(0, 2)\n .map((s) => {\n const v = s.value;\n const color = v > 0 ? chalk.green : v < 0 ? chalk.red : chalk.yellow;\n return color(`${s.source}: ${v > 0 ? \"+\" : \"\"}${v.toFixed(2)}`);\n })\n .join(\", \");\n\n lines.push(\n ` ${r.token.padEnd(14)} ${scoreStr} ${actionStr.padEnd(14)} ${confStr.padEnd(8)} ${keySignals}`,\n );\n }\n\n lines.push(\"\");\n lines.push(chalk.dim(` Generated at ${new Date().toISOString()}`));\n lines.push(\"\");\n\n return lines.join(\"\\n\");\n }\n}\n","/**\n * DefiLlama free API provider — no API key needed.\n * Provides TVL, protocol, DEX volume, yield, price, and stablecoin data.\n */\n\nimport type { Provider, ProviderInfo } from \"../../types.js\";\n\nexport class DefiLlamaProvider implements Provider {\n info(): ProviderInfo {\n return {\n name: \"DefiLlama\",\n type: \"research\",\n capabilities: [\n \"protocol-tvl\",\n \"protocol-list\",\n \"protocol-details\",\n \"dex-volumes\",\n \"yields\",\n \"token-prices\",\n \"stablecoins\",\n ],\n supportedChains: [],\n };\n }\n\n /** Get current TVL for a protocol (returns a number). */\n async getProtocolTvl(protocol: string): Promise<number> {\n try {\n const res = await fetch(`https://api.llama.fi/tvl/${encodeURIComponent(protocol)}`);\n if (!res.ok) throw new Error(`DefiLlama tvl error: ${res.status} ${res.statusText}`);\n const data = await res.json();\n return data as number;\n } catch (err) {\n throw new Error(`Failed to fetch TVL for ${protocol}: ${(err as Error).message}`);\n }\n }\n\n /** Get all protocols with TVL & chain info. */\n async getProtocols(): Promise<any[]> {\n try {\n const res = await fetch(\"https://api.llama.fi/protocols\");\n if (!res.ok) throw new Error(`DefiLlama protocols error: ${res.status} ${res.statusText}`);\n return (await res.json()) as any[];\n } catch (err) {\n throw new Error(`Failed to fetch protocols: ${(err as Error).message}`);\n }\n }\n\n /** Get detailed protocol info with historical TVL. */\n async getProtocolDetails(protocol: string): Promise<any> {\n try {\n const res = await fetch(`https://api.llama.fi/protocol/${encodeURIComponent(protocol)}`);\n if (!res.ok) throw new Error(`DefiLlama protocol detail error: ${res.status} ${res.statusText}`);\n return await res.json();\n } catch (err) {\n throw new Error(`Failed to fetch protocol details for ${protocol}: ${(err as Error).message}`);\n }\n }\n\n /** Get DEX volumes, optionally filtered by chain. */\n async getDexVolumes(chain?: string): Promise<any> {\n try {\n const url = chain\n ? `https://api.llama.fi/overview/dexs/${encodeURIComponent(chain)}`\n : \"https://api.llama.fi/overview/dexs\";\n const res = await fetch(url);\n if (!res.ok) throw new Error(`DefiLlama dex volumes error: ${res.status} ${res.statusText}`);\n return await res.json();\n } catch (err) {\n throw new Error(`Failed to fetch DEX volumes: ${(err as Error).message}`);\n }\n }\n\n /** Get yield/pool data from DefiLlama yields API. */\n async getYields(): Promise<any> {\n try {\n const res = await fetch(\"https://yields.llama.fi/pools\");\n if (!res.ok) throw new Error(`DefiLlama yields error: ${res.status} ${res.statusText}`);\n return await res.json();\n } catch (err) {\n throw new Error(`Failed to fetch yields: ${(err as Error).message}`);\n }\n }\n\n /**\n * Get current token prices.\n * @param coins Array of \"chain:address\" strings, e.g. [\"ethereum:0x...\"]\n */\n async getTokenPrices(coins: string[]): Promise<any> {\n try {\n const joined = coins.map(encodeURIComponent).join(\",\");\n const res = await fetch(`https://coins.llama.fi/prices/current/${joined}`);\n if (!res.ok) throw new Error(`DefiLlama prices error: ${res.status} ${res.statusText}`);\n return await res.json();\n } catch (err) {\n throw new Error(`Failed to fetch token prices: ${(err as Error).message}`);\n }\n }\n\n /** Get stablecoin data. */\n async getStablecoins(): Promise<any> {\n try {\n const res = await fetch(\"https://stablecoins.llama.fi/stablecoins\");\n if (!res.ok) throw new Error(`DefiLlama stablecoins error: ${res.status} ${res.statusText}`);\n return await res.json();\n } catch (err) {\n throw new Error(`Failed to fetch stablecoins: ${(err as Error).message}`);\n }\n }\n}\n","/**\n * CoinGecko free API provider with aggressive caching and rate-limiting.\n */\n\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { createHash } from 'node:crypto';\nimport type { Provider, ProviderInfo } from \"../../types.js\";\n\nconst BASE_URL = \"https://api.coingecko.com/api/v3\";\n\n// Shared mutex queue across all instances to prevent 429s.\n// Each request chains onto this promise so only one runs at a time with a 2.5s gap.\nlet requestQueue: Promise<void> = Promise.resolve();\nlet sharedLastCallTime = 0;\nlet MIN_INTERVAL = 2500; // 2.5s between calls (CG free tier: 30/min = 1 every 2s)\n\n// Circuit breaker: when we observe a 429, freeze all CG requests for this window.\n// Previous behavior retried 10s → 30s → 60s per call; because the queue is global\n// serial, a single rate-limit window ballooned one cycle from ~60s to ~15 min\n// (18 tokens × up to 100s per 429). Now we fail fast, let callers use cache/null,\n// and resume after the window. Break resets once a successful call lands.\nconst CIRCUIT_BREAK_MS = 5 * 60 * 1000; // 5 minutes\nlet circuitOpenUntil = 0;\n\n// Cache TTLs by endpoint (in milliseconds)\nconst CACHE_TTLS = {\n ohlc: 2 * 60 * 60 * 1000, // 2 hours - historical candles don't change\n market_chart: 60 * 60 * 1000, // 1 hour\n coin_details: 60 * 60 * 1000, // 1 hour\n simple_price: 5 * 60 * 1000, // 5 minutes\n trending: 30 * 60 * 1000, // 30 minutes\n} as const;\n\nexport class CoinGeckoProvider implements Provider {\n private cacheDir: string;\n private apiKey?: string;\n\n constructor() {\n this.cacheDir = join(homedir(), '.sherwood', 'agent', 'cache', 'coingecko');\n // Check for API key from env or config\n this.apiKey = process.env.COINGECKO_API_KEY;\n\n // Adjust throttle for demo key\n if (this.apiKey) {\n MIN_INTERVAL = 1500; // Demo key has better limits\n }\n }\n\n info(): ProviderInfo {\n return {\n name: \"CoinGecko\",\n type: \"research\",\n capabilities: [\"price\", \"market-data\", \"ohlc\", \"coin-details\", \"trending\"],\n supportedChains: [],\n };\n }\n\n /**\n * Generate cache key from endpoint and params.\n */\n private getCacheKey(endpoint: string, params: string): string {\n const hash = createHash('sha1').update(endpoint + params).digest('hex').substring(0, 12);\n return `${hash}.json`;\n }\n\n /**\n * Get cache TTL for an endpoint.\n */\n private getCacheTTL(url: string): number {\n if (url.includes('/ohlc')) return CACHE_TTLS.ohlc;\n if (url.includes('/market_chart')) return CACHE_TTLS.market_chart;\n if (url.includes('/coins/') && !url.includes('/market_chart')) return CACHE_TTLS.coin_details;\n if (url.includes('/simple/price')) return CACHE_TTLS.simple_price;\n if (url.includes('/trending')) return CACHE_TTLS.trending;\n return CACHE_TTLS.simple_price; // Default fallback\n }\n\n /**\n * Read from cache if available and fresh.\n */\n private async readCache(cacheKey: string, ttl: number): Promise<any | null> {\n try {\n const cacheFile = join(this.cacheDir, cacheKey);\n const raw = await readFile(cacheFile, 'utf-8');\n const cached = JSON.parse(raw) as { ts: number; data: any };\n\n if (Date.now() - cached.ts < ttl) {\n return cached.data;\n }\n } catch {\n // Cache miss or invalid cache\n }\n return null;\n }\n\n /**\n * Write to cache with atomic write (tmp file + rename).\n */\n private async writeCache(cacheKey: string, data: any): Promise<void> {\n try {\n await mkdir(this.cacheDir, { recursive: true });\n const cacheFile = join(this.cacheDir, cacheKey);\n const tmpFile = cacheFile + '.tmp';\n\n await writeFile(tmpFile, JSON.stringify({ ts: Date.now(), data }), 'utf-8');\n // Atomic rename (requires fs.rename which we'll use from import)\n await import('node:fs/promises').then(fs => fs.rename(tmpFile, cacheFile));\n } catch {\n // Cache write failure is non-fatal\n }\n }\n\n /**\n * Serialised request method — every CoinGecko call goes through here.\n * Checks cache first, then uses shared promise chain for API calls.\n */\n private async fetchJson(url: string): Promise<any> {\n // Extract cache key from full URL path (includes token ID)\n const urlObj = new URL(url);\n const cacheKey = this.getCacheKey(urlObj.pathname, urlObj.search);\n const ttl = this.getCacheTTL(url);\n\n // Check cache first — if hit, return immediately without touching rate limiter\n const cached = await this.readCache(cacheKey, ttl);\n if (cached) {\n return cached;\n }\n\n // Circuit breaker: if a recent 429 tripped the breaker, fail fast instead of\n // queueing up more doomed calls. A cycle scanning 18 tokens was observed to\n // take 14-18 minutes during rate-limit windows because every serialized call\n // exhausted its 10s/30s/60s retry budget. Fail-fast lets each call burn\n // ~0ms instead of ~100s and returns control to the caller immediately.\n if (Date.now() < circuitOpenUntil) {\n throw new Error(`CoinGecko circuit breaker open — rate-limited, retrying after ${new Date(circuitOpenUntil).toISOString()}`);\n }\n\n // Cache miss — make API call with rate limiting\n const job = requestQueue.then(async () => {\n // Re-check breaker inside the serialized job: callers that queued behind\n // the 429-triggering call should also fail fast rather than replay 10-60s\n // backoffs one-by-one.\n if (Date.now() < circuitOpenUntil) {\n throw new Error(`CoinGecko circuit breaker open — rate-limited, retrying after ${new Date(circuitOpenUntil).toISOString()}`);\n }\n\n const now = Date.now();\n const elapsed = now - sharedLastCallTime;\n if (elapsed < MIN_INTERVAL) {\n await new Promise((resolve) => setTimeout(resolve, MIN_INTERVAL - elapsed));\n }\n sharedLastCallTime = Date.now();\n\n // Add API key header if available\n const headers: Record<string, string> = {};\n if (this.apiKey) {\n headers['x-cg-demo-api-key'] = this.apiKey;\n }\n\n const res = await fetch(url, { headers });\n if (res.status === 429) {\n // Rate limited — trip the circuit breaker and fail fast. Do NOT retry\n // inline: the 10s/30s/60s per-call backoff made cycles balloon to\n // 15+ minutes when CG was cranky. Caller gets a clean failure, can\n // fall back to cached/null, and subsequent CG calls in this cycle\n // skip the API entirely until the window closes.\n circuitOpenUntil = Date.now() + CIRCUIT_BREAK_MS;\n throw new Error(`CoinGecko 429 — circuit breaker opened for ${CIRCUIT_BREAK_MS / 1000}s — ${url}`);\n }\n if (!res.ok) throw new Error(`CoinGecko error: ${res.status} ${res.statusText} — ${url}`);\n\n const data = await res.json();\n // Success closes the breaker (we got through, so the rate window passed).\n circuitOpenUntil = 0;\n await this.writeCache(cacheKey, data);\n return data;\n });\n\n // Chain the next request after this one settles (success or failure)\n requestQueue = job.then(() => {}, () => {});\n return job;\n }\n\n /**\n * Get simple prices for multiple tokens.\n * Returns price, 24h vol, 24h change, and market cap per token.\n */\n async getPrice(\n ids: string[],\n vsCurrencies: string[] = [\"usd\"],\n ): Promise<Record<string, any>> {\n const params = new URLSearchParams({\n ids: ids.join(\",\"),\n vs_currencies: vsCurrencies.join(\",\"),\n include_24hr_vol: \"true\",\n include_24hr_change: \"true\",\n include_market_cap: \"true\",\n });\n return this.fetchJson(`${BASE_URL}/simple/price?${params}`);\n }\n\n /**\n * Get market chart data (prices, market_caps, total_volumes) over time.\n * Note: only fetches for a single id at a time.\n */\n async getMarketData(\n id: string,\n days: number = 30,\n ): Promise<{ prices: number[][]; market_caps: number[][]; total_volumes: number[][] }> {\n const params = new URLSearchParams({\n vs_currency: \"usd\",\n days: String(days),\n });\n return this.fetchJson(`${BASE_URL}/coins/${encodeURIComponent(id)}/market_chart?${params}`);\n }\n\n /**\n * Get OHLC candle data.\n * days: 1/7/14/30/90/180/365/max\n * Returns array of [timestamp, open, high, low, close].\n */\n async getOHLC(\n id: string,\n days: number = 30,\n ): Promise<number[][]> {\n const params = new URLSearchParams({\n vs_currency: \"usd\",\n days: String(days),\n });\n return this.fetchJson(`${BASE_URL}/coins/${encodeURIComponent(id)}/ohlc?${params}`);\n }\n\n /** Get detailed coin information. */\n async getCoinDetails(id: string): Promise<any> {\n const params = new URLSearchParams({\n localization: \"false\",\n tickers: \"false\",\n community_data: \"true\",\n developer_data: \"true\",\n });\n return this.fetchJson(`${BASE_URL}/coins/${encodeURIComponent(id)}?${params}`);\n }\n\n /** Get trending coins. */\n async getTrending(): Promise<any> {\n return this.fetchJson(`${BASE_URL}/search/trending`);\n }\n}\n","/**\n * Dedicated Fear & Greed Index provider with history tracking and caching.\n */\n\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\n\nexport interface FearGreedEntry {\n value: number;\n classification: string;\n timestamp: string;\n}\n\ninterface FearGreedApiResponse {\n name: string;\n data: Array<{\n value: string;\n value_classification: string;\n timestamp: string;\n }>;\n}\n\nconst API_URL = 'https://api.alternative.me/fng/';\n\nexport class FearGreedProvider {\n private cacheDir: string;\n private cacheFile: string;\n private cacheTTL = 30 * 60 * 1000; // 30 minutes\n\n constructor() {\n this.cacheDir = join(homedir(), '.sherwood', 'agent', 'cache');\n this.cacheFile = join(this.cacheDir, 'feargreed.json');\n }\n\n /** Get current Fear & Greed data. */\n async getCurrent(): Promise<FearGreedEntry> {\n const data = await this.fetchWithCache(1);\n if (!data.length) throw new Error('No Fear & Greed data available');\n return data[0]!;\n }\n\n /** Get historical data for the last N days. */\n async getHistory(days: number = 30): Promise<FearGreedEntry[]> {\n return this.fetchWithCache(days);\n }\n\n /**\n * Check if F&G has been in extreme zone for N consecutive days.\n * @param threshold - Value boundary (e.g. 25 for fear, 75 for greed)\n * @param days - Minimum consecutive days in extreme zone\n */\n async isExtreme(\n threshold: number = 25,\n days: number = 3,\n ): Promise<{ extreme: boolean; direction: 'fear' | 'greed' | null; consecutive: number }> {\n const history = await this.getHistory(Math.max(days + 5, 30));\n\n if (history.length === 0) {\n return { extreme: false, direction: null, consecutive: 0 };\n }\n\n // Check consecutive days of extreme fear (below threshold)\n let fearStreak = 0;\n for (const entry of history) {\n if (entry.value <= threshold) {\n fearStreak++;\n } else {\n break;\n }\n }\n\n // Check consecutive days of extreme greed (above 100 - threshold)\n const greedThreshold = 100 - threshold;\n let greedStreak = 0;\n for (const entry of history) {\n if (entry.value >= greedThreshold) {\n greedStreak++;\n } else {\n break;\n }\n }\n\n if (fearStreak >= days) {\n return { extreme: true, direction: 'fear', consecutive: fearStreak };\n }\n if (greedStreak >= days) {\n return { extreme: true, direction: 'greed', consecutive: greedStreak };\n }\n\n return {\n extreme: false,\n direction: null,\n consecutive: Math.max(fearStreak, greedStreak),\n };\n }\n\n /** Fetch data with local file cache. */\n private async fetchWithCache(limit: number): Promise<FearGreedEntry[]> {\n // Try reading cache\n try {\n const raw = await readFile(this.cacheFile, 'utf-8');\n const cached = JSON.parse(raw) as { ts: number; data: FearGreedEntry[] };\n if (Date.now() - cached.ts < this.cacheTTL && cached.data.length >= limit) {\n return cached.data.slice(0, limit);\n }\n } catch {\n // No cache or invalid — fetch fresh\n }\n\n const data = await this.fetchFromApi(Math.max(limit, 30));\n\n // Write cache\n try {\n await mkdir(this.cacheDir, { recursive: true });\n await writeFile(this.cacheFile, JSON.stringify({ ts: Date.now(), data }), 'utf-8');\n } catch {\n // Cache write failure is non-fatal\n }\n\n return data.slice(0, limit);\n }\n\n /** Fetch from the alternative.me API. */\n private async fetchFromApi(limit: number): Promise<FearGreedEntry[]> {\n const url = `${API_URL}?limit=${limit}`;\n const res = await fetch(url);\n if (!res.ok) {\n throw new Error(`Fear & Greed API error: ${res.status} ${res.statusText}`);\n }\n const json = (await res.json()) as FearGreedApiResponse;\n return json.data.map((d) => ({\n value: Number(d.value),\n classification: d.value_classification,\n timestamp: d.timestamp,\n }));\n }\n}\n","/**\n * Sentiment data provider — delegates to FearGreedProvider for API access,\n * adds z-score utilities and Provider interface compliance.\n */\n\nimport type { Provider, ProviderInfo } from \"../../types.js\";\nimport { FearGreedProvider, type FearGreedEntry } from \"./feargreed.js\";\n\n/** @deprecated Use FearGreedEntry from feargreed.ts instead. */\nexport type FearAndGreedData = FearGreedEntry;\n\nconst fearGreed = new FearGreedProvider();\n\nexport class SentimentProvider implements Provider {\n info(): ProviderInfo {\n return {\n name: \"Sentiment\",\n type: \"research\",\n capabilities: [\"fear-and-greed\", \"sentiment-zscore\"],\n supportedChains: [],\n };\n }\n\n /** Fetch last 30 days of Fear & Greed index data (delegates to FearGreedProvider). */\n async getFearAndGreed(): Promise<FearGreedEntry[]> {\n return fearGreed.getHistory(30);\n }\n\n /** Get just the latest Fear & Greed value (delegates to FearGreedProvider). */\n async getFearAndGreedCurrent(): Promise<FearGreedEntry> {\n return fearGreed.getCurrent();\n }\n\n /**\n * Compute z-score of the latest value compared to the array.\n * z = (latest - mean) / stddev\n */\n computeSentimentZScore(values: number[]): number {\n if (values.length < 2) return 0;\n const latest = values[0]!;\n const mean = values.reduce((a, b) => a + b, 0) / values.length;\n const variance = values.reduce((sum, v) => sum + (v - mean) ** 2, 0) / values.length;\n const stddev = Math.sqrt(variance);\n if (stddev === 0) return 0;\n return (latest - mean) / stddev;\n }\n}\n","/**\n * Breakout + On-Chain Confirmation Strategy\n * Detects price breakouts confirmed by volume and optionally by smart money flow.\n */\n\nimport type { Signal } from '../scoring.js';\nimport type { Strategy, StrategyContext, Candle } from './types.js';\nimport { clamp } from '../utils.js';\n\nfunction detect20DayBreakout(candles: Candle[]): { direction: 'up' | 'down' | 'none'; volumeConfirmed: boolean } {\n if (candles.length < 21) return { direction: 'none', volumeConfirmed: false };\n\n const lookback = candles.slice(-21, -1); // previous 20 candles (excluding latest)\n const latest = candles[candles.length - 1]!;\n\n const high20 = Math.max(...lookback.map(c => c.high));\n const low20 = Math.min(...lookback.map(c => c.low));\n\n // Volume confirmation: latest volume > 2x 20-period average\n const avgVolume = lookback.reduce((sum, c) => sum + c.volume, 0) / lookback.length;\n const volumeConfirmed = avgVolume > 0 && latest.volume > avgVolume * 2;\n\n if (latest.close > high20) {\n return { direction: 'up', volumeConfirmed };\n } else if (latest.close < low20) {\n return { direction: 'down', volumeConfirmed };\n }\n\n return { direction: 'none', volumeConfirmed: false };\n}\n\nfunction checkEmaAlignment(technicals: { ema: { ema8: number; ema21: number; ema50: number } }): 'bullish' | 'bearish' | 'neutral' {\n const { ema8, ema21, ema50 } = technicals.ema;\n if (isNaN(ema8) || isNaN(ema21) || isNaN(ema50)) return 'neutral';\n\n if (ema8 > ema21 && ema21 > ema50) return 'bullish';\n if (ema8 < ema21 && ema21 < ema50) return 'bearish';\n return 'neutral';\n}\n\nexport class BreakoutOnChainStrategy implements Strategy {\n name = 'breakoutOnChain';\n description = 'Detects 20-day breakouts with volume confirmation and optional smart-money support';\n requiredData = ['candles', 'technicals'];\n\n async analyze(ctx: StrategyContext): Promise<Signal> {\n if (!ctx.candles || ctx.candles.length < 21) {\n return {\n name: this.name,\n value: 0.0,\n confidence: 0.1,\n source: 'Breakout + On-Chain',\n details: 'Insufficient candle data for breakout detection (need 21+)',\n };\n }\n\n const details: string[] = [];\n let value = 0;\n let confidence = 0.4;\n\n // Detect breakout\n const breakout = detect20DayBreakout(ctx.candles);\n\n if (breakout.direction === 'none') {\n details.push('No 20-day breakout detected (ranging)');\n\n // Still check EMA alignment for a mild signal\n if (ctx.technicals) {\n const ema = checkEmaAlignment(ctx.technicals);\n if (ema === 'bullish') {\n value = 0.1;\n details.push('EMA alignment bullish (8>21>50)');\n } else if (ema === 'bearish') {\n value = -0.1;\n details.push('EMA alignment bearish (8<21<50)');\n }\n }\n\n return {\n name: this.name,\n value,\n confidence: 0.3,\n source: 'Breakout + On-Chain',\n details: details.join('; '),\n };\n }\n\n // Breakout detected\n if (breakout.direction === 'up') {\n value = breakout.volumeConfirmed ? 0.8 : 0.5;\n confidence = breakout.volumeConfirmed ? 0.7 : 0.5;\n details.push(`Bullish 20-day breakout${breakout.volumeConfirmed ? ' with volume confirmation (>2x avg)' : ' (low volume — caution)'}`);\n } else {\n value = breakout.volumeConfirmed ? -0.8 : -0.5;\n confidence = breakout.volumeConfirmed ? 0.7 : 0.5;\n details.push(`Bearish 20-day breakdown${breakout.volumeConfirmed ? ' with volume confirmation (>2x avg)' : ' (low volume — caution)'}`);\n }\n\n // EMA alignment bonus\n if (ctx.technicals) {\n const ema = checkEmaAlignment(ctx.technicals);\n if ((ema === 'bullish' && value > 0) || (ema === 'bearish' && value < 0)) {\n const bonus = value > 0 ? 0.2 : -0.2;\n value = clamp(value + bonus);\n confidence += 0.1;\n details.push(`EMA alignment confirms direction (+${Math.abs(bonus).toFixed(1)})`);\n } else if ((ema === 'bearish' && value > 0) || (ema === 'bullish' && value < 0)) {\n details.push('EMA alignment contradicts breakout — mixed signal');\n confidence -= 0.1;\n }\n }\n\n // Nansen smart money confirmation (optional)\n if (ctx.nansenData) {\n const data = ctx.nansenData as Record<string, unknown>;\n const flows = data.flows as Array<Record<string, unknown>> | undefined;\n if (flows && flows.length > 0) {\n const netflow = flows.reduce(\n (sum, f) => sum + (Number(f.netflow ?? f.net_flow ?? 0)),\n 0,\n );\n // Negative netflow = buying, positive = selling\n const smartMoneyBuying = netflow < 0;\n const smartMoneySelling = netflow > 0;\n\n if ((smartMoneyBuying && value > 0) || (smartMoneySelling && value < 0)) {\n const bonus = value > 0 ? 0.2 : -0.2;\n value = clamp(value + bonus);\n confidence += 0.1;\n details.push(`Smart money confirms breakout direction (+${Math.abs(bonus).toFixed(1)})`);\n } else if ((smartMoneySelling && value > 0) || (smartMoneyBuying && value < 0)) {\n details.push('Smart money diverges from breakout — caution');\n confidence -= 0.05;\n }\n }\n }\n\n return {\n name: this.name,\n value: clamp(value),\n confidence: Math.min(Math.max(confidence, 0.1), 1.0),\n source: 'Breakout + On-Chain',\n details: details.join('; '),\n };\n }\n}\n","/**\n * Funding Rate Harvester Strategy\n * Looks at perpetual futures funding rates for arbitrage opportunities.\n * Currently a placeholder — requires external funding rate API integration.\n */\n\nimport type { Signal } from '../scoring.js';\nimport type { Strategy, StrategyContext } from './types.js';\nimport { clamp } from '../utils.js';\n\n/**\n * Calculate annualized yield from an 8-hour funding rate.\n * Funding is paid 3x per day (every 8 hours).\n * @param rate8h - The 8-hour funding rate as a decimal (e.g., 0.0005 = 0.05%)\n * @returns Annualized yield as a decimal (e.g., 0.5475 = 54.75%)\n */\nexport function annualizedYieldFromFundingRate(rate8h: number): number {\n // 3 funding periods per day × 365 days = 1095 periods per year\n return rate8h * 3 * 365;\n}\n\n/**\n * Calculate expected return from funding rate arbitrage over a period.\n * @param rate8h - 8-hour funding rate as decimal\n * @param days - Holding period in days\n * @returns Expected return as decimal\n */\nexport function expectedReturnFromFunding(rate8h: number, days: number): number {\n return rate8h * 3 * days;\n}\n\nexport class FundingRateStrategy implements Strategy {\n name = 'fundingRate';\n description = 'Identifies funding rate arbitrage opportunities in perp markets (requires external data source)';\n requiredData = ['fundingRateData'];\n\n async analyze(ctx: StrategyContext): Promise<Signal> {\n // Check if funding rate data is available in the context\n // For now, this is a placeholder — funding rate data isn't provided by current providers\n const fundingData = ctx.fundingRateData;\n\n if (!fundingData) {\n return {\n name: this.name,\n value: 0.0,\n confidence: 0.0,\n source: 'Funding Rate Harvester',\n details: 'No funding rate data available — requires external data source (Binance, Bybit, etc.)',\n };\n }\n\n const rate = fundingData.rate8h;\n const annualized = annualizedYieldFromFundingRate(rate);\n const ratePct = (rate * 100).toFixed(4);\n const annualizedPct = (annualized * 100).toFixed(1);\n const details: string[] = [];\n let value = 0;\n let confidence = 0.5;\n\n // Thresholds calibrated to BTC/ETH majors (typical 8h funding is ±0.01%).\n // Tightened from the previous altcoin-era defaults that only fired at\n // ±0.02%/±0.05%, which almost never happen on majors in chop.\n if (rate > 0.0002) {\n // High positive funding: longs pay shorts → market overleveraged long → bearish contrarian\n value = -0.5;\n confidence = 0.6;\n details.push(\n `High positive funding ${ratePct}% (annualized ${annualizedPct}%): overleveraged longs — bearish contrarian on ${fundingData.exchange}`,\n );\n } else if (rate > 0.00005) {\n // Mild positive funding: slightly overleveraged long → mildly bearish\n value = -0.3;\n confidence = 0.4;\n details.push(\n `Mild positive funding ${ratePct}% (annualized ${annualizedPct}%): longs paying shorts — mildly bearish on ${fundingData.exchange}`,\n );\n } else if (rate < -0.0002) {\n // High negative funding: shorts pay longs → market overleveraged short → bullish contrarian\n value = 0.5;\n confidence = 0.6;\n details.push(\n `High negative funding ${ratePct}% (annualized ${annualizedPct}%): overleveraged shorts — bullish contrarian on ${fundingData.exchange}`,\n );\n } else if (rate < -0.00005) {\n // Mild negative funding: slightly overleveraged short → mildly bullish\n value = 0.3;\n confidence = 0.4;\n details.push(\n `Mild negative funding ${ratePct}% (annualized ${annualizedPct}%): shorts paying longs — mildly bullish on ${fundingData.exchange}`,\n );\n } else {\n // Funding within ±0.005% (8h): truly neutral\n value = 0.0;\n confidence = 0.3;\n details.push(\n `Funding rate ${ratePct}% near neutral: no directional signal`,\n );\n }\n\n return {\n name: this.name,\n value: clamp(value),\n confidence,\n source: 'Funding Rate Harvester',\n details: details.join('; '),\n };\n }\n}\n","/**\n * DEXScreener free API provider — real-time DEX data across all chains.\n * Base URL: https://api.dexscreener.com/latest\n * No API key needed. Rate limit: 300 req/min.\n */\n\nconst BASE_URL = 'https://api.dexscreener.com/latest';\n\nexport interface DexPair {\n chainId: string;\n dexId: string;\n pairAddress: string;\n baseToken: { address: string; name: string; symbol: string };\n quoteToken: { address: string; name: string; symbol: string };\n priceUsd: string;\n priceChange: { h1: number; h6: number; h24: number };\n volume: { h1: number; h6: number; h24: number };\n liquidity: { usd: number };\n fdv: number;\n txns: {\n h1: { buys: number; sells: number };\n h24: { buys: number; sells: number };\n };\n}\n\ninterface DexSearchResponse {\n pairs: DexPair[] | null;\n}\n\ninterface DexPairsResponse {\n pairs: DexPair[] | null;\n pair?: DexPair;\n}\n\n// Module-level throttle shared across all instances to enforce rate limit\nlet sharedLastCallTime = 0;\nconst SHARED_MIN_INTERVAL = 200; // 200ms = 300 req/min\n\nexport class DexScreenerProvider {\n private async throttle(): Promise<void> {\n const now = Date.now();\n const elapsed = now - sharedLastCallTime;\n if (elapsed < SHARED_MIN_INTERVAL) {\n await new Promise((resolve) => setTimeout(resolve, SHARED_MIN_INTERVAL - elapsed));\n }\n sharedLastCallTime = Date.now();\n }\n\n private async fetchJson<T>(url: string): Promise<T> {\n await this.throttle();\n const res = await fetch(url);\n if (!res.ok) {\n throw new Error(`DEXScreener API error: ${res.status} ${res.statusText} — ${url}`);\n }\n return res.json() as Promise<T>;\n }\n\n /** Search for token pairs by query string (token name, symbol, or address). */\n async searchPairs(query: string): Promise<DexPair[]> {\n const encoded = encodeURIComponent(query);\n const data = await this.fetchJson<DexSearchResponse>(\n `${BASE_URL}/dex/search/?q=${encoded}`,\n );\n return data.pairs ?? [];\n }\n\n /** Get pair data by chain and pair address. */\n async getPair(chain: string, pairAddress: string): Promise<DexPair> {\n const data = await this.fetchJson<DexPairsResponse>(\n `${BASE_URL}/dex/pairs/${encodeURIComponent(chain)}/${encodeURIComponent(pairAddress)}`,\n );\n if (data.pair) return data.pair;\n if (data.pairs && data.pairs.length > 0) return data.pairs[0]!;\n throw new Error(`No pair found: ${chain}/${pairAddress}`);\n }\n\n /** Get all DEX pairs for a token address (across all chains). */\n async getTokenPairs(tokenAddress: string): Promise<DexPair[]> {\n const data = await this.fetchJson<DexPairsResponse>(\n `${BASE_URL}/dex/tokens/${encodeURIComponent(tokenAddress)}`,\n );\n return data.pairs ?? [];\n }\n\n /** Get trending/top pairs by searching for common tokens. */\n async getTrending(): Promise<DexPair[]> {\n // DEXScreener doesn't have a dedicated trending endpoint in the free API,\n // so we search for well-known tokens and sort by volume\n const results = await this.searchPairs('WETH');\n return results\n .filter((p) => p.volume?.h24 > 0)\n .sort((a, b) => (b.volume?.h24 ?? 0) - (a.volume?.h24 ?? 0))\n .slice(0, 20);\n }\n}\n","/**\n * Hyperliquid Flow Strategy\n * Combines exchange-native signals for maximum alpha:\n * - Funding rate arbitrage\n * - OI + price divergence\n * - Order book imbalance\n * - Whale trade flow\n */\n\nimport type { Signal } from '../scoring.js';\nimport type { Strategy, StrategyContext } from './types.js';\nimport { clamp } from '../utils.js';\n\nexport class HyperliquidFlowStrategy implements Strategy {\n name = 'hyperliquidFlow';\n description = 'Exchange-native flow analysis using Hyperliquid funding, OI, order book, and whale trades';\n requiredData = ['hyperliquidData'];\n\n async analyze(ctx: StrategyContext): Promise<Signal> {\n const hlData = ctx.hyperliquidData;\n\n if (!hlData) {\n return {\n name: this.name,\n value: 0.0,\n confidence: 0.0,\n source: 'Hyperliquid Flow',\n details: 'No Hyperliquid data available',\n };\n }\n\n const signals = [\n this.analyzeFundingRate(hlData.fundingRate),\n this.analyzeOIPriceDivergence(hlData.oiChangePct, hlData.markPrice, hlData.prevDayPrice),\n this.analyzeOrderBookImbalance(hlData.orderBookImbalance),\n this.analyzeWhaleFlow(hlData.largeTradesBias),\n ];\n\n // Combine signals with weights\n const weights = [0.25, 0.30, 0.25, 0.20]; // funding, OI+price, orderbook, whales\n let totalValue = 0;\n let totalWeight = 0;\n const details: string[] = [];\n\n for (let i = 0; i < signals.length; i++) {\n const signal = signals[i]!;\n const weight = weights[i]!;\n totalValue += signal.value * weight;\n totalWeight += weight;\n if (signal.details) details.push(signal.details);\n }\n\n const value = totalValue / totalWeight;\n\n // Calculate confidence\n let confidence = 0.6; // Base confidence (exchange-native data)\n if (hlData.volume24h > 100_000_000) confidence += 0.1; // High liquidity\n\n // Boost confidence if multiple signals agree\n const positiveSignals = signals.filter(s => s.value > 0.2).length;\n const negativeSignals = signals.filter(s => s.value < -0.2).length;\n if (positiveSignals >= 3 || negativeSignals >= 3) confidence += 0.1;\n\n return {\n name: this.name,\n value: clamp(value),\n confidence: Math.min(1.0, confidence),\n source: 'Hyperliquid Flow',\n details: details.join(' | '),\n };\n }\n\n /** Analyze funding rate for mean-reversion opportunities. */\n private analyzeFundingRate(rate: number): { value: number; details: string } {\n const ratePct = (rate * 100).toFixed(4);\n\n if (rate > 0.001) {\n // >0.1% funding: strong bearish (overleveraged longs)\n return {\n value: -0.8,\n details: `High positive funding ${ratePct}%: overleveraged longs → bearish`,\n };\n } else if (rate > 0.0003) {\n // >0.03% funding: bearish\n return {\n value: -0.6,\n details: `Elevated positive funding ${ratePct}%: longs paying → bearish`,\n };\n } else if (rate < -0.001) {\n // <-0.1% funding: strong bullish (overleveraged shorts)\n return {\n value: 0.8,\n details: `High negative funding ${ratePct}%: overleveraged shorts → bullish`,\n };\n } else if (rate < -0.0003) {\n // <-0.03% funding: bullish\n return {\n value: 0.6,\n details: `Elevated negative funding ${ratePct}%: shorts paying → bullish`,\n };\n } else {\n // Neutral funding\n return {\n value: 0.0,\n details: `Neutral funding ${ratePct}%: no directional signal`,\n };\n }\n }\n\n /** Analyze OI + price divergence patterns. */\n private analyzeOIPriceDivergence(oiChangePct: number, markPrice: number, prevDayPrice: number): { value: number; details: string } {\n const priceChange = ((markPrice - prevDayPrice) / prevDayPrice) * 100;\n const oiRising = oiChangePct > 1; // >1% OI increase\n const oiFalling = oiChangePct < -1; // >1% OI decrease\n const priceRising = priceChange > 1; // >1% price increase\n const priceFalling = priceChange < -1; // >1% price decrease\n\n if (oiRising && priceRising) {\n return {\n value: 0.6,\n details: `OI+${oiChangePct.toFixed(1)}% Price+${priceChange.toFixed(1)}%: strong trend → bullish`,\n };\n } else if (oiRising && priceFalling) {\n return {\n value: 0.3,\n details: `OI+${oiChangePct.toFixed(1)}% Price${priceChange.toFixed(1)}%: shorts building → potential squeeze`,\n };\n } else if (oiFalling && priceRising) {\n return {\n value: -0.3,\n details: `OI${oiChangePct.toFixed(1)}% Price+${priceChange.toFixed(1)}%: weak rally → potential trap`,\n };\n } else if (oiFalling && priceFalling) {\n return {\n value: 0.2,\n details: `OI${oiChangePct.toFixed(1)}% Price${priceChange.toFixed(1)}%: capitulation → potential bottom`,\n };\n } else {\n return {\n value: 0.0,\n details: `OI${oiChangePct.toFixed(1)}% Price${priceChange.toFixed(1)}%: no clear divergence`,\n };\n }\n }\n\n /** Analyze order book imbalance. */\n private analyzeOrderBookImbalance(imbalance: number): { value: number; details: string } {\n const imbalancePct = (imbalance * 100).toFixed(1);\n\n if (imbalance > 0.5) {\n return {\n value: 0.8,\n details: `Very heavy bids (${imbalancePct}%): strong demand → bullish`,\n };\n } else if (imbalance > 0.3) {\n return {\n value: 0.5,\n details: `Heavy bids (${imbalancePct}%): demand pressure → bullish`,\n };\n } else if (imbalance < -0.5) {\n return {\n value: -0.8,\n details: `Very heavy asks (${imbalancePct}%): strong supply → bearish`,\n };\n } else if (imbalance < -0.3) {\n return {\n value: -0.5,\n details: `Heavy asks (${imbalancePct}%): supply pressure → bearish`,\n };\n } else {\n return {\n value: 0.0,\n details: `Balanced orderbook (${imbalancePct}%): no directional bias`,\n };\n }\n }\n\n /** Analyze whale trade flow direction. */\n private analyzeWhaleFlow(bias: number): { value: number; details: string } {\n const biasPct = (bias * 100).toFixed(1);\n\n if (bias > 0.3) {\n return {\n value: 0.4,\n details: `Whales buying (${biasPct}%): smart money accumulation → bullish`,\n };\n } else if (bias < -0.3) {\n return {\n value: -0.4,\n details: `Whales selling (${biasPct}%): smart money distribution → bearish`,\n };\n } else {\n return {\n value: bias, // Scale linearly for smaller values\n details: `Whale flow (${biasPct}%): moderate directional bias`,\n };\n }\n }\n}","/**\n * Multi-Timeframe Confluence Strategy\n * Analyzes the same token across multiple timeframes for signal confluence.\n * Strong signals occur when multiple timeframes agree on direction.\n */\n\nimport type { Signal } from '../scoring.js';\nimport type { Strategy, StrategyContext, Candle } from './types.js';\nimport { calculateEMA, calculateRSI } from '../technical.js';\nimport { clamp } from '../utils.js';\n\ninterface TimeframeAnalysis {\n timeframe: string;\n trend: number; // +1 bullish, 0 neutral, -1 bearish\n momentum: number; // +1 bullish, 0 neutral, -1 bearish\n structure: number; // +1 bullish, 0 neutral, -1 bearish\n score: number; // combined score\n}\n\nexport class MultiTimeframeStrategy implements Strategy {\n name = 'multiTimeframe';\n description = 'Multi-timeframe confluence analysis using daily, weekly, and short-term data';\n requiredData = ['candles'];\n\n async analyze(ctx: StrategyContext): Promise<Signal> {\n if (!ctx.candles || ctx.candles.length < 30) {\n return {\n name: this.name,\n value: 0.0,\n confidence: 0.1,\n source: 'Multi-Timeframe',\n details: 'Insufficient candle data for multi-timeframe analysis (need 30+ daily candles)',\n };\n }\n\n const details: string[] = [];\n let value = 0;\n let confidence = 0.4;\n\n try {\n // Prepare timeframe data\n const dailyCandles = ctx.candles;\n const weeklyCandles = this.aggregateWeeklyCandles(dailyCandles);\n const shortTermCandles = dailyCandles.slice(-10); // Last 10 days as short-term proxy\n\n // Analyze each timeframe with appropriate periods\n const shortTermAnalysis = this.analyzeTimeframe(shortTermCandles, 'Short-term', 5);\n const dailyAnalysis = this.analyzeTimeframe(dailyCandles, 'Daily', 20);\n const weeklyAnalysis = this.analyzeTimeframe(weeklyCandles, 'Weekly', 5); // Reduced from 10\n\n const analyses = [shortTermAnalysis, dailyAnalysis, weeklyAnalysis];\n\n // Calculate confluence score\n const bullishCount = analyses.filter(a => a.score > 0).length;\n const bearishCount = analyses.filter(a => a.score < 0).length;\n const neutralCount = analyses.filter(a => a.score === 0).length;\n\n // Confluence scoring - adjusted for more realistic signals\n if (bullishCount === 3) {\n value = 0.8;\n confidence += 0.2;\n details.push('All timeframes bullish (strong confluence)');\n } else if (bearishCount === 3) {\n value = -0.8;\n confidence += 0.2;\n details.push('All timeframes bearish (strong confluence)');\n } else if (bullishCount === 2) {\n value = 0.4;\n confidence += 0.1;\n details.push('2 of 3 timeframes bullish');\n } else if (bearishCount === 2) {\n value = -0.4;\n confidence += 0.1;\n details.push('2 of 3 timeframes bearish');\n } else if (bullishCount > bearishCount) {\n value = 0.2;\n details.push('Majority bullish (weak confluence)');\n } else if (bearishCount > bullishCount) {\n value = -0.2;\n details.push('Majority bearish (weak confluence)');\n } else {\n value = 0.0;\n details.push('Mixed timeframe signals');\n }\n\n // Add timeframe breakdown details\n for (const analysis of analyses) {\n const score = analysis.score > 0 ? 'bullish' : analysis.score < 0 ? 'bearish' : 'neutral';\n details.push(`${analysis.timeframe}: ${score} (trend:${analysis.trend} momentum:${analysis.momentum} structure:${analysis.structure})`);\n }\n\n // Data quality factor\n const dataQualityFactor = Math.min(dailyCandles.length / 50, 1.0); // Full confidence at 50+ days\n confidence *= dataQualityFactor;\n\n if (dataQualityFactor < 1.0) {\n details.push(`Data quality: ${(dataQualityFactor * 100).toFixed(0)}% (${dailyCandles.length} days)`);\n }\n\n } catch (error) {\n return {\n name: this.name,\n value: 0.0,\n confidence: 0.1,\n source: 'Multi-Timeframe',\n details: `Analysis error: ${(error as Error).message}`,\n };\n }\n\n return {\n name: this.name,\n value: clamp(value),\n confidence: Math.min(Math.max(confidence, 0.1), 1.0),\n source: 'Multi-Timeframe',\n details: details.join('; '),\n };\n }\n\n private aggregateWeeklyCandles(dailyCandles: Candle[]): Candle[] {\n if (dailyCandles.length < 7) return [];\n\n const weeklyCandles: Candle[] = [];\n const sortedCandles = [...dailyCandles].sort((a, b) => a.timestamp - b.timestamp);\n\n // Group by ISO week\n const weekGroups = new Map<string, Candle[]>();\n\n for (const candle of sortedCandles) {\n const date = new Date(candle.timestamp);\n const year = date.getFullYear();\n const week = this.getISOWeek(date);\n const weekKey = `${year}-W${week.toString().padStart(2, '0')}`;\n\n if (!weekGroups.has(weekKey)) {\n weekGroups.set(weekKey, []);\n }\n weekGroups.get(weekKey)!.push(candle);\n }\n\n // Aggregate each week\n for (const [weekKey, candles] of weekGroups) {\n if (candles.length === 0) continue;\n\n const open = candles[0]!.open;\n const close = candles[candles.length - 1]!.close;\n const high = Math.max(...candles.map(c => c.high));\n const low = Math.min(...candles.map(c => c.low));\n const volume = candles.reduce((sum, c) => sum + c.volume, 0);\n const timestamp = candles[0]!.timestamp;\n\n weeklyCandles.push({ timestamp, open, high, low, close, volume });\n }\n\n return weeklyCandles.sort((a, b) => a.timestamp - b.timestamp);\n }\n\n private getISOWeek(date: Date): number {\n const tempDate = new Date(date.valueOf());\n const dayNumber = (date.getDay() + 6) % 7;\n tempDate.setDate(tempDate.getDate() - dayNumber + 3);\n const firstThursday = tempDate.valueOf();\n tempDate.setMonth(0, 1);\n if (tempDate.getDay() !== 4) {\n tempDate.setMonth(0, 1 + ((4 - tempDate.getDay()) + 7) % 7);\n }\n return 1 + Math.ceil((firstThursday - tempDate.valueOf()) / 604800000);\n }\n\n private analyzeTimeframe(candles: Candle[], label: string, emaPeriod: number): TimeframeAnalysis {\n if (candles.length < Math.max(emaPeriod * 0.7, 10)) { // More lenient requirement\n return {\n timeframe: label,\n trend: 0,\n momentum: 0,\n structure: 0,\n score: 0,\n };\n }\n\n // 1. Trend: price vs EMA\n const closes = candles.map(c => c.close);\n const emaValues = calculateEMA(closes, emaPeriod);\n const currentPrice = closes[closes.length - 1]!;\n const currentEMA = emaValues[emaValues.length - 1];\n\n let trend = 0;\n if (!isNaN(currentEMA!)) {\n trend = currentPrice > currentEMA! ? 1 : currentPrice < currentEMA! ? -1 : 0;\n }\n\n // 2. Momentum: RSI zones\n const rsiValues = calculateRSI(candles, 14);\n const currentRSI = rsiValues[rsiValues.length - 1];\n\n let momentum = 0;\n if (!isNaN(currentRSI!)) {\n if (currentRSI! > 60) momentum = 1;\n else if (currentRSI! < 40) momentum = -1;\n else momentum = 0;\n }\n\n // 3. Structure: last 3 candles pattern\n let structure = 0;\n if (candles.length >= 3) {\n const last3 = candles.slice(-3);\n const highs = last3.map(c => c.high);\n const lows = last3.map(c => c.low);\n\n const higherHighs = highs[1] > highs[0] && highs[2] > highs[1];\n const higherLows = lows[1] > lows[0] && lows[2] > lows[1];\n const lowerHighs = highs[1] < highs[0] && highs[2] < highs[1];\n const lowerLows = lows[1] < lows[0] && lows[2] < lows[1];\n\n if (higherHighs && higherLows) structure = 1;\n else if (lowerHighs && lowerLows) structure = -1;\n else structure = 0;\n }\n\n // Combine into score\n const score = trend + momentum + structure;\n\n return {\n timeframe: label,\n trend,\n momentum,\n structure,\n score: Math.sign(score) // Convert to -1, 0, or 1\n };\n }\n}","/**\n * Cross-Sectional Momentum Strategy\n *\n * Based on Kakushadze & Serur \"151 Trading Strategies\" §10.3, adapted for\n * crypto perpetuals. Ranks tokens by their 7-day return RELATIVE to the\n * group mean, expressed as a z-score. In flat markets where absolute signals\n * are muted, the token outperforming (or underperforming) the group still\n * produces a directional signal.\n *\n * Uses momentum (not contrarian) weighting — crypto trends persist:\n * z_i = (R_i - R_m) / σ_R\n * signal = clamp(z_i * 0.3, -1, 1)\n */\n\nimport type { Signal } from '../scoring.js';\nimport type { Strategy, StrategyContext } from './types.js';\nimport { clamp } from '../utils.js';\n\n/** Minimum number of tokens in the group to produce a meaningful z-score. */\nconst MIN_GROUP_SIZE = 3;\n\n/** Scaling factor: z=1 → signal value 0.30. */\nconst Z_SCALE = 0.3;\n\n/** Below this stdev the group is effectively flat — no meaningful dispersion. */\nconst MIN_STDEV = 1e-10;\n\nexport class CrossSectionalMomentumStrategy implements Strategy {\n name = 'crossSectionalMomentum';\n description = 'Ranks tokens by relative 7-day performance vs group (§10.3 Kakushadze)';\n requiredData = ['groupReturns'];\n\n async analyze(ctx: StrategyContext): Promise<Signal> {\n const returns = ctx.groupReturns;\n if (!returns || Object.keys(returns).length < MIN_GROUP_SIZE) {\n return {\n name: this.name,\n value: 0,\n confidence: 0.1,\n source: 'Cross-Sectional',\n details: 'Insufficient group data',\n };\n }\n\n const myReturn = returns[ctx.tokenId];\n if (myReturn === undefined) {\n return {\n name: this.name,\n value: 0,\n confidence: 0.1,\n source: 'Cross-Sectional',\n details: 'Token not in group',\n };\n }\n\n const vals = Object.values(returns);\n const mean = vals.reduce((s, v) => s + v, 0) / vals.length;\n const variance = vals.reduce((s, v) => s + (v - mean) ** 2, 0) / vals.length;\n const stdev = Math.sqrt(variance);\n\n if (stdev < MIN_STDEV) {\n return {\n name: this.name,\n value: 0,\n confidence: 0.1,\n source: 'Cross-Sectional',\n details: 'Zero group dispersion',\n };\n }\n\n const zScore = (myReturn - mean) / stdev;\n const value = clamp(zScore * Z_SCALE);\n const confidence = clamp(0.3 + Math.abs(zScore) * 0.2, 0.1, 0.9);\n\n return {\n name: this.name,\n value,\n confidence,\n source: 'Cross-Sectional Momentum',\n details: `7d return ${(myReturn * 100).toFixed(1)}% vs group mean ${(mean * 100).toFixed(1)}% (z=${zScore.toFixed(2)})`,\n };\n }\n}\n","/**\n * TradingView MCP data provider — spawns a local TradingView MCP server\n * (JSON-RPC over stdio) and exposes coin analysis + multi-timeframe alignment.\n *\n * The MCP server is spawned once on first call and kept alive for the process\n * lifetime. If it dies, it is respawned on the next call.\n *\n * Results are cached for 5 minutes per (symbol, tool, timeframe) key.\n */\n\nimport { spawn, type ChildProcess } from 'node:child_process';\nimport { createInterface, type Interface as ReadlineInterface } from 'node:readline';\n\n// ---------------------------------------------------------------------------\n// Symbol mapping — CoinGecko token IDs → TradingView BINANCE symbols\n// ---------------------------------------------------------------------------\n\nconst TV_SYMBOLS: Record<string, string> = {\n bitcoin: 'BTCUSDT',\n ethereum: 'ETHUSDT',\n solana: 'SOLUSDT',\n arbitrum: 'ARBUSDT',\n chainlink: 'LINKUSDT',\n aave: 'AAVEUSDT',\n uniswap: 'UNIUSDT',\n dogecoin: 'DOGEUSDT',\n avalanche: 'AVAXUSDT',\n 'avalanche-2': 'AVAXUSDT',\n near: 'NEARUSDT',\n sui: 'SUIUSDT',\n aptos: 'APTUSDT',\n injective: 'INJUSDT',\n pendle: 'PENDLEUSDT',\n pepe: 'PEPEUSDT',\n polygon: 'MATICUSDT',\n optimism: 'OPUSDT',\n litecoin: 'LTCUSDT',\n cosmos: 'ATOMUSDT',\n filecoin: 'FILUSDT',\n maker: 'MKRUSDT',\n cardano: 'ADAUSDT',\n polkadot: 'DOTUSDT',\n render: 'RENDERUSDT',\n jupiter: 'JUPUSDT',\n hyperliquid: 'HYPEUSDT',\n ethena: 'ENAUSDT',\n zcash: 'ZECUSDT',\n ripple: 'XRPUSDT',\n bittensor: 'TAOUSDT',\n fartcoin: 'FARTCOINUSDT',\n binancecoin: 'BNBUSDT',\n blur: 'BLURUSDT',\n 'worldcoin-wld': 'WLDUSDT',\n 'pudgy-penguins': 'PENGUUSDT',\n 'fetch-ai': 'FETUSDT',\n};\n\nconst DEFAULT_EXCHANGE = 'BINANCE';\nconst MCP_BIN = '/home/ana/.local/tradingview-mcp/bin/tradingview-mcp';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface TVAnalysis {\n symbol: string;\n timeframe: string;\n /** Overall recommendation: -1 (Strong Sell) to +1 (Strong Buy). */\n recommendAll: number;\n /** Buy/sell signal string from market_sentiment. */\n buySellSignal: string;\n /** Number of buy indicators. */\n buyCount: number;\n /** Number of sell indicators. */\n sellCount: number;\n /** Number of neutral indicators. */\n neutralCount: number;\n /** Total indicator count. */\n totalIndicators: number;\n /** Raw response payload for debugging. */\n raw: any;\n}\n\nexport interface TVAlignment {\n symbol: string;\n /** Per-timeframe recommendation (-1 to +1). */\n timeframes: Record<string, number>;\n /** Number of timeframes agreeing on direction. */\n agreeing: number;\n /** Total timeframes analysed. */\n total: number;\n raw: any;\n}\n\n// ---------------------------------------------------------------------------\n// Cache\n// ---------------------------------------------------------------------------\n\ninterface CacheEntry<T> {\n ts: number;\n data: T;\n}\n\nconst CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes\nconst cache = new Map<string, CacheEntry<any>>();\n\nfunction cacheGet<T>(key: string): T | null {\n const entry = cache.get(key);\n if (!entry) return null;\n if (Date.now() - entry.ts > CACHE_TTL_MS) {\n cache.delete(key);\n return null;\n }\n return entry.data as T;\n}\n\nfunction cacheSet<T>(key: string, data: T): void {\n cache.set(key, { ts: Date.now(), data });\n}\n\n// ---------------------------------------------------------------------------\n// MCP process management (singleton)\n// ---------------------------------------------------------------------------\n\nlet mcpProcess: ChildProcess | null = null;\nlet mcpReadline: ReadlineInterface | null = null;\nlet mcpReady = false;\nlet nextId = 1;\n\n/** Pending JSON-RPC responses keyed by request id. */\nconst pending = new Map<number, {\n resolve: (value: any) => void;\n reject: (reason: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n}>();\n\nconst RPC_TIMEOUT_MS = 15_000;\n\nfunction ensureProcess(): ChildProcess {\n if (mcpProcess && !mcpProcess.killed && mcpProcess.exitCode === null) {\n return mcpProcess;\n }\n\n // Reset state on respawn\n mcpReady = false;\n for (const [, p] of pending) {\n clearTimeout(p.timer);\n p.reject(new Error('MCP process died — respawning'));\n }\n pending.clear();\n\n mcpProcess = spawn(MCP_BIN, [], {\n stdio: ['pipe', 'pipe', 'pipe'],\n env: { ...process.env },\n });\n\n mcpReadline = createInterface({ input: mcpProcess.stdout! });\n mcpReadline.on('line', (line: string) => {\n try {\n const msg = JSON.parse(line);\n const id = msg.id;\n if (id !== undefined && pending.has(id)) {\n const p = pending.get(id)!;\n clearTimeout(p.timer);\n pending.delete(id);\n if (msg.error) {\n p.reject(new Error(`MCP error ${msg.error.code}: ${msg.error.message}`));\n } else {\n p.resolve(msg.result);\n }\n }\n } catch {\n // Non-JSON line or notification — ignore\n }\n });\n\n mcpProcess.on('exit', () => {\n mcpProcess = null;\n mcpReady = false;\n });\n\n mcpProcess.stderr?.on('data', () => {\n // Swallow stderr to prevent backpressure\n });\n\n return mcpProcess;\n}\n\nfunction rpcSend(method: string, params: any): Promise<any> {\n const proc = ensureProcess();\n const id = nextId++;\n const msg = JSON.stringify({ jsonrpc: '2.0', id, method, params }) + '\\n';\n\n return new Promise<any>((resolve, reject) => {\n const timer = setTimeout(() => {\n pending.delete(id);\n reject(new Error(`MCP RPC timeout for ${method} (${RPC_TIMEOUT_MS}ms)`));\n }, RPC_TIMEOUT_MS);\n pending.set(id, { resolve, reject, timer });\n\n proc.stdin!.write(msg, (err) => {\n if (err) {\n clearTimeout(timer);\n pending.delete(id);\n reject(err);\n }\n });\n });\n}\n\nasync function ensureInitialized(): Promise<void> {\n if (mcpReady) return;\n\n ensureProcess();\n\n // Send initialize\n await rpcSend('initialize', {\n protocolVersion: '2024-11-05',\n capabilities: {},\n clientInfo: { name: 'sherwood', version: '1.0' },\n });\n\n // Send initialized notification (no id, fire-and-forget)\n const proc = ensureProcess();\n proc.stdin!.write(\n JSON.stringify({ jsonrpc: '2.0', method: 'notifications/initialized', params: {} }) + '\\n',\n );\n\n mcpReady = true;\n}\n\nasync function callTool(name: string, args: Record<string, any>): Promise<any> {\n await ensureInitialized();\n const result = await rpcSend('tools/call', { name, arguments: args });\n // MCP tool results come as { content: [{ type: \"text\", text: \"...\" }] }\n if (result?.content?.[0]?.text) {\n try {\n return JSON.parse(result.content[0].text);\n } catch {\n return result.content[0].text;\n }\n }\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Map a buy/sell signal string to a numeric value (-1 to +1).\n */\nfunction signalToValue(signal: string): number {\n const s = (signal || '').toLowerCase().trim();\n if (s === 'strong buy') return 1.0;\n if (s === 'buy') return 0.5;\n if (s === 'neutral') return 0.0;\n if (s === 'sell') return -0.5;\n if (s === 'strong sell') return -1.0;\n return 0.0;\n}\n\n/**\n * Get coin analysis from TradingView MCP.\n * Returns null if the token is unmapped or the MCP call fails.\n */\nexport async function getCoinAnalysis(\n tokenId: string,\n timeframe: string = '4h',\n): Promise<TVAnalysis | null> {\n const symbol = TV_SYMBOLS[tokenId];\n if (!symbol) return null;\n\n const cacheKey = `tv:coin:${symbol}:${timeframe}`;\n const cached = cacheGet<TVAnalysis>(cacheKey);\n if (cached) return cached;\n\n try {\n const raw = await callTool('coin_analysis', {\n symbol,\n exchange: DEFAULT_EXCHANGE,\n timeframe,\n });\n\n // Parse the response — structure varies, extract what we need\n const recommendAll = typeof raw?.recommend_all === 'number'\n ? raw.recommend_all\n : typeof raw?.summary?.RECOMMENDATION === 'string'\n ? signalToValue(raw.summary.RECOMMENDATION)\n : 0;\n\n const buySellSignal: string =\n raw?.market_sentiment?.buy_sell_signal\n ?? raw?.summary?.RECOMMENDATION\n ?? 'Neutral';\n\n const buyCount = raw?.summary?.BUY ?? raw?.buy_count ?? 0;\n const sellCount = raw?.summary?.SELL ?? raw?.sell_count ?? 0;\n const neutralCount = raw?.summary?.NEUTRAL ?? raw?.neutral_count ?? 0;\n\n const result: TVAnalysis = {\n symbol,\n timeframe,\n recommendAll: Math.max(-1, Math.min(1, recommendAll)),\n buySellSignal,\n buyCount,\n sellCount,\n neutralCount,\n totalIndicators: buyCount + sellCount + neutralCount,\n raw,\n };\n\n cacheSet(cacheKey, result);\n return result;\n } catch (err) {\n console.error(`TradingView MCP coin_analysis failed for ${symbol}: ${(err as Error).message}`);\n return null;\n }\n}\n\n/**\n * Get multi-timeframe alignment from TradingView MCP.\n * Returns null if the token is unmapped or the MCP call fails.\n */\nexport async function getMultiTimeframeAlignment(\n tokenId: string,\n): Promise<TVAlignment | null> {\n const symbol = TV_SYMBOLS[tokenId];\n if (!symbol) return null;\n\n const cacheKey = `tv:mtf:${symbol}`;\n const cached = cacheGet<TVAlignment>(cacheKey);\n if (cached) return cached;\n\n try {\n const raw = await callTool('multi_timeframe_analysis', {\n symbol,\n exchange: DEFAULT_EXCHANGE,\n });\n\n // Parse per-timeframe recommendations\n const timeframes: Record<string, number> = {};\n const tfKeys = ['15m', '1h', '4h', 'daily', 'weekly'];\n\n for (const tf of tfKeys) {\n const tfData = raw?.[tf] ?? raw?.timeframes?.[tf];\n if (tfData) {\n const rec =\n typeof tfData.recommend_all === 'number'\n ? tfData.recommend_all\n : typeof tfData.RECOMMENDATION === 'string'\n ? signalToValue(tfData.RECOMMENDATION)\n : typeof tfData.recommendation === 'string'\n ? signalToValue(tfData.recommendation)\n : null;\n if (rec !== null) {\n timeframes[tf] = Math.max(-1, Math.min(1, rec));\n }\n }\n }\n\n const values = Object.values(timeframes);\n const majoritySign = values.reduce((s, v) => s + Math.sign(v), 0);\n const direction = majoritySign > 0 ? 1 : majoritySign < 0 ? -1 : 0;\n const agreeing = values.filter((v) => Math.sign(v) === direction).length;\n\n const result: TVAlignment = {\n symbol,\n timeframes,\n agreeing,\n total: values.length,\n raw,\n };\n\n cacheSet(cacheKey, result);\n return result;\n } catch (err) {\n console.error(`TradingView MCP multi_timeframe failed for ${symbol}: ${(err as Error).message}`);\n return null;\n }\n}\n\n/**\n * Check if the TradingView MCP binary exists.\n */\nexport function isTradingViewAvailable(): boolean {\n try {\n require('node:fs').accessSync(MCP_BIN, require('node:fs').constants.X_OK);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Gracefully shut down the MCP process (call on agent exit).\n */\nexport function shutdownTradingView(): void {\n if (mcpProcess && !mcpProcess.killed) {\n mcpProcess.kill('SIGTERM');\n mcpProcess = null;\n mcpReady = false;\n }\n}\n","/**\n * TradingView Signal Strategy\n *\n * Consumes coin analysis from the local TradingView MCP server and converts\n * it into a directional signal (-1 to +1). Falls back to a zero-confidence\n * neutral signal when the MCP server is unavailable or the token is unmapped.\n */\n\nimport type { Signal } from '../scoring.js';\nimport type { Strategy, StrategyContext } from './types.js';\nimport { clamp } from '../utils.js';\nimport { getCoinAnalysis, type TVAnalysis } from '../../providers/data/tradingview.js';\n\nexport class TradingViewSignalStrategy implements Strategy {\n name = 'tradingviewSignal';\n description = 'TradingView technical indicators via local MCP server (4h timeframe)';\n requiredData = [] as string[]; // No StrategyContext fields needed — uses own provider\n\n async analyze(ctx: StrategyContext): Promise<Signal> {\n const analysis = await getCoinAnalysis(ctx.tokenId, '4h');\n\n if (!analysis) {\n return {\n name: this.name,\n value: 0.0,\n confidence: 0.0,\n source: 'TradingView MCP',\n details: 'TV MCP unavailable or token unmapped',\n };\n }\n\n return this.buildSignal(analysis);\n }\n\n private buildSignal(tv: TVAnalysis): Signal {\n // Primary value: Recommend.All (-1 to +1) from TradingView summary\n let value = tv.recommendAll;\n\n // Fallback: derive from buy_sell_signal string if recommendAll is zero\n if (value === 0 && tv.buySellSignal.toLowerCase() !== 'neutral') {\n value = signalStringToValue(tv.buySellSignal);\n }\n\n // Confidence: proportion of indicators that agree with the direction\n let confidence = 0.5; // baseline\n if (tv.totalIndicators > 0) {\n const agreeingCount =\n value > 0\n ? tv.buyCount\n : value < 0\n ? tv.sellCount\n : tv.neutralCount;\n confidence = agreeingCount / tv.totalIndicators;\n }\n\n // Boost confidence when signal is extreme (Strong Buy / Strong Sell)\n const absValue = Math.abs(value);\n if (absValue >= 0.8) {\n confidence = Math.min(confidence + 0.1, 1.0);\n }\n\n const details = [\n `TV ${tv.timeframe}: ${tv.buySellSignal}`,\n `Recommend.All=${tv.recommendAll.toFixed(3)}`,\n `buy=${tv.buyCount} sell=${tv.sellCount} neutral=${tv.neutralCount}`,\n ].join('; ');\n\n return {\n name: this.name,\n value: clamp(value),\n confidence: Math.max(0.1, Math.min(confidence, 1.0)),\n source: 'TradingView MCP',\n details,\n };\n }\n}\n\n/**\n * Map a TV buy/sell signal label to a numeric value.\n */\nfunction signalStringToValue(signal: string): number {\n const s = signal.toLowerCase().trim();\n if (s === 'strong buy') return 1.0;\n if (s === 'buy') return 0.5;\n if (s === 'neutral') return 0.0;\n if (s === 'sell') return -0.5;\n if (s === 'strong sell') return -1.0;\n return 0.0;\n}\n","/**\n * BTC Network Health Strategy\n *\n * Uses Blockchain.com network data (via Fincept) to gauge Bitcoin network\n * health: transaction count, miner revenue, and mempool demand.\n * Only fires for bitcoin — returns zero-confidence for other tokens.\n */\n\nimport type { Signal } from '../scoring.js';\nimport type { Strategy, StrategyContext } from './types.js';\nimport { clamp } from '../utils.js';\n\nexport class BtcNetworkHealthStrategy implements Strategy {\n name = 'btcNetworkHealth';\n description = 'Analyzes BTC network health — tx count, miner revenue, mempool demand';\n requiredData = ['btcNetworkData'];\n\n async analyze(ctx: StrategyContext): Promise<Signal> {\n if (ctx.tokenId !== 'bitcoin') {\n return {\n name: this.name,\n value: 0,\n confidence: 0,\n source: 'BTC Network Health',\n details: 'Not bitcoin — skipped',\n };\n }\n\n if (!ctx.btcNetworkData) {\n return {\n name: this.name,\n value: 0,\n confidence: 0.1,\n source: 'BTC Network Health',\n details: 'No BTC network data available',\n };\n }\n\n const { transactionCount, minerRevenueBtc, mempoolSize } = ctx.btcNetworkData;\n const details: string[] = [];\n let value = 0;\n\n // Transaction count signals\n if (transactionCount > 300_000) {\n value += 0.2;\n details.push(`active network (${(transactionCount / 1000).toFixed(0)}k txs)`);\n } else if (transactionCount < 200_000) {\n value -= 0.1;\n details.push(`low activity (${(transactionCount / 1000).toFixed(0)}k txs)`);\n }\n\n // Miner revenue signals\n if (minerRevenueBtc > 1000) {\n value += 0.15;\n details.push(`healthy miners (${minerRevenueBtc.toFixed(0)} BTC revenue)`);\n }\n\n // Mempool demand signals\n if (mempoolSize > 100_000) {\n value += 0.1;\n details.push(`high demand (${(mempoolSize / 1000).toFixed(0)}k mempool)`);\n }\n\n const confidence = details.length > 0 ? 0.5 : 0.2;\n\n return {\n name: this.name,\n value: clamp(value),\n confidence,\n source: 'BTC Network Health',\n details: details.length > 0 ? details.join('; ') : 'Network metrics within normal range',\n };\n }\n}\n","/**\n * Prediction Market Strategy\n *\n * Uses Polymarket/Manifold prediction market probabilities to derive\n * directional signals. High-conviction markets (>75% or <25%) for\n * bullish/bearish crypto events contribute to the score.\n */\n\nimport type { Signal } from \"../scoring.js\";\nimport type { Strategy, StrategyContext } from \"./types.js\";\nimport { clamp } from \"../utils.js\";\n\nconst BULLISH_KEYWORDS = [\"approve\", \"etf\", \"cut\", \"ease\", \"adopt\", \"pass\", \"bull\"];\nconst BEARISH_KEYWORDS = [\"ban\", \"restrict\", \"crash\", \"reject\", \"hike\", \"bear\", \"default\"];\n\nfunction classifyQuestion(question: string): \"bullish\" | \"bearish\" | \"neutral\" {\n const q = question.toLowerCase();\n if (BULLISH_KEYWORDS.some((kw) => q.includes(kw))) return \"bullish\";\n if (BEARISH_KEYWORDS.some((kw) => q.includes(kw))) return \"bearish\";\n return \"neutral\";\n}\n\nexport class PredictionMarketStrategy implements Strategy {\n name = \"predictionMarket\";\n description =\n \"Derives directional signals from high-conviction prediction market probabilities\";\n requiredData = [\"predictionData\"];\n\n async analyze(ctx: StrategyContext): Promise<Signal> {\n const predictionData = ctx.predictionData;\n\n if (!predictionData || !predictionData.markets || predictionData.markets.length === 0) {\n return {\n name: this.name,\n value: 0.0,\n confidence: 0.0,\n source: \"Prediction Markets\",\n details: \"No prediction market data available\",\n };\n }\n\n let bullishScore = 0;\n let bearishScore = 0;\n const details: string[] = [];\n\n for (const market of predictionData.markets) {\n const classification = classifyQuestion(market.question);\n if (classification === \"neutral\") continue;\n\n const prob = market.probability;\n\n // Only process high-conviction markets\n if (prob > 0.75) {\n // Scale: 0 at 75%, 1 at 100%, then × 0.15 per market (max contribution)\n const strength = (prob - 0.75) * 4 * 0.15;\n if (classification === \"bullish\") {\n bullishScore += strength;\n details.push(`Bullish event likely (${(prob * 100).toFixed(0)}%): ${market.question}`);\n } else {\n bearishScore += strength;\n details.push(`Bearish event likely (${(prob * 100).toFixed(0)}%): ${market.question}`);\n }\n } else if (prob < 0.25) {\n // Event is unlikely — mild contrarian signal\n if (classification === \"bullish\") {\n bearishScore += 0.05;\n details.push(`Bullish event unlikely (${(prob * 100).toFixed(0)}%): ${market.question}`);\n } else {\n bullishScore += 0.05;\n details.push(`Bearish event unlikely (${(prob * 100).toFixed(0)}%): ${market.question}`);\n }\n }\n }\n\n const rawValue = bullishScore - bearishScore;\n const value = clamp(rawValue, -0.5, 0.5);\n\n return {\n name: this.name,\n value,\n confidence: details.length > 0 ? 0.5 : 0.1,\n source: \"Prediction Markets\",\n details: details.length > 0 ? details.join(\"; \") : \"No high-conviction markets found\",\n };\n }\n}\n","/**\n * Contrarian Social Volume Strategy\n * Trades against crowd attention — elevated news coverage is contrarian bearish.\n */\n\nimport type { Signal } from '../scoring.js';\nimport type { Strategy, StrategyContext } from './types.js';\nimport { clamp } from '../utils.js';\n\nexport class SocialVolumeStrategy implements Strategy {\n name = 'socialVolume';\n description = 'Contrarian social volume: fades extreme news attention, neutral when coverage is low';\n requiredData = ['socialData'];\n\n async analyze(ctx: StrategyContext): Promise<Signal> {\n if (!ctx.socialData) {\n return {\n name: this.name,\n value: 0.0,\n confidence: 0.1,\n source: 'Social Volume',\n details: 'No social data available',\n };\n }\n\n const { newsCount24h } = ctx.socialData;\n let value = 0;\n let confidence = 0.3;\n let detail: string;\n\n if (newsCount24h > 10) {\n // Extreme attention — contrarian bearish\n value = -0.2;\n confidence = 0.5;\n detail = `Extreme news attention (${newsCount24h} articles): contrarian bearish`;\n } else if (newsCount24h > 5) {\n // Elevated attention — mild caution\n value = -0.1;\n confidence = 0.4;\n detail = `Elevated news attention (${newsCount24h} articles): mild caution`;\n } else if (newsCount24h >= 1) {\n // Normal coverage\n value = 0;\n confidence = 0.3;\n detail = `Normal news coverage (${newsCount24h} articles)`;\n } else {\n // Under the radar\n value = 0;\n confidence = 0.3;\n detail = 'Under the radar: no recent news coverage';\n }\n\n return {\n name: this.name,\n value: clamp(value),\n confidence,\n source: 'Social Volume',\n details: detail,\n };\n }\n}\n","/**\n * Kronos Volatility Forecast Strategy — uses ML-predicted future volatility\n * to generate signals and inform risk management.\n *\n * How it works:\n * 1. Kronos generates N Monte Carlo price paths from historical OHLCV\n * 2. Path spread = predicted volatility (wider spread = more uncertain future)\n * 3. Directional bias = mean path direction (if most paths go up → bullish)\n *\n * Signal logic:\n * - directionalBias > 0.3: bullish signal (+0.2 to +0.4 scaled)\n * - directionalBias < -0.3: bearish signal (-0.2 to -0.4 scaled)\n * - High predicted vol (pathSpread > 8%): increase confidence on directional\n * signals (high vol = big moves, worth trading), but clamp signal magnitude\n * (don't overbet on volatile predictions)\n * - Low predicted vol (pathSpread < 3%): reduce confidence (narrow paths =\n * range-bound, less opportunity)\n *\n * The volatility forecast is ALSO consumed by the executor (via StrategyContext)\n * for dynamic stop-loss width, separate from this signal.\n */\n\nimport type { Signal } from '../scoring.js';\nimport type { Strategy, StrategyContext } from './types.js';\nimport { clamp } from '../utils.js';\n\nexport class KronosVolForecastStrategy implements Strategy {\n name = 'kronosVolForecast';\n description = 'Kronos ML volatility forecast (directional bias + vol regime)';\n requiredData = ['kronosData'];\n\n async analyze(ctx: StrategyContext): Promise<Signal> {\n const k = ctx.kronosData;\n if (!k) {\n return {\n name: this.name,\n value: 0,\n confidence: 0,\n source: this.description,\n details: 'No Kronos forecast available',\n };\n }\n\n let value = 0;\n const details: string[] = [];\n let confidence = 0.3;\n\n // Directional bias from mean predicted path\n const bias = k.directionalBias;\n if (Math.abs(bias) > 0.3) {\n // Strong directional signal: scale from 0 at 0.3 to ±0.4 at ±1.0\n const strength = (Math.abs(bias) - 0.3) / 0.7; // 0 to 1\n value = Math.sign(bias) * (0.2 + strength * 0.2); // ±0.2 to ±0.4\n const dir = bias > 0 ? 'bullish' : 'bearish';\n details.push(`Kronos ${dir} bias ${(bias * 100).toFixed(0)}% → ${value >= 0 ? '+' : ''}${value.toFixed(2)}`);\n confidence = 0.4 + strength * 0.2; // 0.4 to 0.6\n } else if (Math.abs(bias) > 0.1) {\n // Mild directional lean — informational, weak signal\n value = bias * 0.15; // ±0.015 to ±0.045\n details.push(`Kronos mild ${bias > 0 ? 'bullish' : 'bearish'} lean ${(bias * 100).toFixed(0)}%`);\n } else {\n details.push(`Kronos neutral (bias ${(bias * 100).toFixed(0)}%)`);\n }\n\n // Vol regime context\n const spread = k.pathSpreadPct;\n if (spread > 8) {\n // High predicted vol: directional signals are MORE meaningful\n // (big moves expected — worth taking positions)\n if (Math.abs(value) > 0.05) {\n confidence = Math.min(confidence + 0.15, 0.75);\n }\n details.push(`High vol forecast (${spread.toFixed(1)}% path spread)`);\n } else if (spread < 3) {\n // Low predicted vol: range-bound expected, reduce conviction\n confidence *= 0.6;\n details.push(`Low vol forecast (${spread.toFixed(1)}% path spread)`);\n } else {\n details.push(`Normal vol (${spread.toFixed(1)}% path spread)`);\n }\n\n return {\n name: this.name,\n value: clamp(value),\n confidence: Math.min(confidence, 1.0),\n source: this.description,\n details: details.join('; '),\n };\n }\n}\n","/**\n * Narrative-Vacuum Whale Signal — detects when whale flow spikes but\n * social/news volume is quiet (\"the market is positioning before the\n * narrative catches up\").\n *\n * Signal: WhaleFlowZ × (1 - SocialVolumeZ)\n * - High whale flow + low social chatter = smart money moving silently → follow\n * - High whale flow + high social chatter = crowd already knows → no edge\n * - Low whale flow + any chatter = nothing happening\n *\n * Data sources:\n * - Nansen smart-money netflow (whale positioning)\n * - SocialData/CryptoCompare news count (narrative proxy)\n *\n * Fires as an \"onchain\" category signal.\n */\n\nimport type { Signal } from '../scoring.js';\nimport type { Strategy, StrategyContext } from './types.js';\nimport { clamp } from '../utils.js';\n\nexport class NarrativeVacuumStrategy implements Strategy {\n name = 'narrativeVacuum';\n description = 'Whale flow divergence from social narrative (Nansen + social data)';\n requiredData = ['nansenFlowData', 'socialData'];\n\n async analyze(ctx: StrategyContext): Promise<Signal> {\n const flow = ctx.nansenFlowData;\n const social = ctx.socialData;\n\n if (!flow) {\n return {\n name: this.name,\n value: 0,\n confidence: 0,\n source: this.description,\n details: 'No Nansen flow data',\n };\n }\n\n const details: string[] = [];\n let value = 0;\n let confidence = 0.3;\n\n // ── Whale flow strength ──\n // netFlow24hUsd: positive = smart money accumulating, negative = distributing\n const netFlow = flow.netFlow24hUsd;\n const traderCount = flow.traderCount;\n\n // Normalize flow: $1M+ is a strong signal\n const flowStrength = Math.min(1, Math.abs(netFlow) / 2_000_000); // 0-1 scale\n const flowDirection = Math.sign(netFlow); // +1 accumulating, -1 distributing\n\n if (flowStrength < 0.1) {\n // Minimal whale activity — no signal\n return {\n name: this.name,\n value: 0,\n confidence: 0.1,\n source: this.description,\n details: `Low whale flow ($${(netFlow / 1000).toFixed(0)}K, ${traderCount} traders) — no edge`,\n };\n }\n\n // ── Social narrative intensity ──\n // newsCount24h: 0 = silence, 1-3 = quiet, 5+ = moderate, 10+ = loud\n const newsCount = social?.newsCount24h ?? 0;\n\n // Social intensity score: 0 = total silence, 1 = very loud\n const socialIntensity = Math.min(1, newsCount / 10);\n\n // ── Narrative vacuum = high flow × low chatter ──\n // vacuum: 1 = whale flow with zero chatter (strongest signal)\n // vacuum: 0 = whale flow with heavy chatter (crowd already knows)\n const vacuum = 1 - socialIntensity;\n const vacuumScore = flowStrength * vacuum;\n\n if (vacuumScore > 0.3) {\n // Strong narrative vacuum — follow the whales\n value = flowDirection * (0.2 + vacuumScore * 0.3); // ±0.2 to ±0.5\n confidence = 0.5 + vacuumScore * 0.25; // 0.5 to 0.75\n\n const dir = netFlow > 0 ? 'accumulating' : 'distributing';\n details.push(\n `Whale ${dir} $${(Math.abs(netFlow) / 1_000_000).toFixed(1)}M (${traderCount} traders) ` +\n `with only ${newsCount} news articles — narrative vacuum (score ${vacuumScore.toFixed(2)})`\n );\n } else if (flowStrength > 0.3 && socialIntensity > 0.5) {\n // Whale flow exists but crowd already talking — no vacuum edge\n details.push(\n `Whale flow $${(Math.abs(netFlow) / 1_000_000).toFixed(1)}M but ${newsCount} articles — ` +\n `narrative already priced in`\n );\n confidence = 0.2;\n } else {\n details.push(\n `Moderate flow $${(Math.abs(netFlow) / 1000).toFixed(0)}K, ${newsCount} articles — ` +\n `insufficient vacuum`\n );\n }\n\n return {\n name: this.name,\n value: clamp(value),\n confidence: Math.min(confidence, 1.0),\n source: this.description,\n details: details.join('; '),\n };\n }\n}\n","/**\n * Whale Intent Classifier — distinguishes operational whale transfers\n * (ETF AP flows, exchange rebalancing) from directional positioning.\n *\n * Key insight from X research: the edge is NOT \"big transfer happened\"\n * but WHAT TYPE of transfer it is. ETF authorized participants move\n * BTC/ETH regularly for operational reasons (creations/redemptions) —\n * these are noise. Discretionary smart-money moves at unusual times,\n * sizes, or patterns are the real signal.\n *\n * Intent classification heuristics:\n * - High trader count + consistent direction = consensus positioning (strong)\n * - Few traders + large size = single whale (moderate, could be operational)\n * - Flow aligned with HL perp positioning = cross-venue confirmation (very strong)\n * - Flow opposing HL perp positioning = divergence (caution)\n *\n * Data sources:\n * - Nansen smart-money netflow (aggregate)\n * - Nansen HL perp trades (cross-venue confirmation)\n * - Hyperliquid funding rate (market sentiment proxy)\n *\n * Fires as a \"smartMoney\" category signal.\n */\n\nimport type { Signal } from '../scoring.js';\nimport type { Strategy, StrategyContext } from './types.js';\nimport { clamp } from '../utils.js';\n\nexport class WhaleIntentStrategy implements Strategy {\n name = 'whaleIntent';\n description = 'Whale transfer intent classifier (operational vs directional, Nansen)';\n requiredData = ['nansenFlowData'];\n\n async analyze(ctx: StrategyContext): Promise<Signal> {\n const flow = ctx.nansenFlowData;\n if (!flow) {\n return {\n name: this.name,\n value: 0,\n confidence: 0,\n source: this.description,\n details: 'No Nansen flow data',\n };\n }\n\n const details: string[] = [];\n let value = 0;\n let confidence = 0.2;\n\n const netFlow = flow.netFlow24hUsd;\n const traderCount = flow.traderCount;\n const flowDirection = Math.sign(netFlow); // +1 buy, -1 sell\n\n // ── Intent classification ──\n\n // 1. Trader count analysis: many traders = consensus, few = single whale/operational\n const isConsensus = traderCount >= 5;\n const isSingleWhale = traderCount <= 2 && Math.abs(netFlow) > 500_000;\n\n // 2. Flow magnitude\n const flowMagnitude = Math.abs(netFlow);\n const isSignificant = flowMagnitude > 200_000; // >$200K\n const isLarge = flowMagnitude > 1_000_000; // >$1M\n\n if (!isSignificant) {\n return {\n name: this.name,\n value: 0,\n confidence: 0.1,\n source: this.description,\n details: `Flow too small ($${(flowMagnitude / 1000).toFixed(0)}K) for intent classification`,\n };\n }\n\n // 3. Cross-venue confirmation: does HL perp positioning agree?\n const hlPerps = ctx.nansenHlPerps;\n let crossVenueAgreement = false;\n let crossVenueDivergence = false;\n\n if (hlPerps && hlPerps.longRatio !== undefined) {\n const hlDirection = hlPerps.longRatio > 0.6 ? 1 : hlPerps.longRatio < 0.4 ? -1 : 0;\n if (hlDirection !== 0 && hlDirection === flowDirection) {\n crossVenueAgreement = true;\n details.push(`HL perps confirm: ${(hlPerps.longRatio * 100).toFixed(0)}% long`);\n } else if (hlDirection !== 0 && hlDirection === -flowDirection) {\n crossVenueDivergence = true;\n details.push(`HL perps DIVERGE: ${(hlPerps.longRatio * 100).toFixed(0)}% long vs on-chain ${flowDirection > 0 ? 'accumulation' : 'distribution'}`);\n }\n }\n\n // 4. Funding rate context: extreme funding = crowded trade\n const fundingRate = ctx.fundingRateData?.rate8h ?? 0;\n const isFundingExtreme = Math.abs(fundingRate) > 0.0005; // >0.05% per 8h\n\n // ── Score computation ──\n\n if (isConsensus && isLarge) {\n // Multiple smart wallets, large flow = strong directional intent\n value = flowDirection * 0.4;\n confidence = 0.6;\n const dir = netFlow > 0 ? 'accumulating' : 'distributing';\n details.push(`Consensus ${dir}: ${traderCount} smart wallets, $${(flowMagnitude / 1e6).toFixed(1)}M`);\n\n if (crossVenueAgreement) {\n value = flowDirection * 0.5;\n confidence = 0.75;\n details.push('Cross-venue confirmed');\n }\n } else if (isConsensus) {\n // Multiple wallets, moderate flow\n value = flowDirection * 0.25;\n confidence = 0.45;\n details.push(`Smart money consensus: ${traderCount} wallets, $${(flowMagnitude / 1000).toFixed(0)}K`);\n } else if (isSingleWhale) {\n // Single large transfer — could be operational (ETF AP, exchange rebalance)\n // Lower confidence unless cross-venue confirms\n if (crossVenueAgreement) {\n value = flowDirection * 0.3;\n confidence = 0.5;\n details.push(`Single whale $${(flowMagnitude / 1e6).toFixed(1)}M — cross-venue confirmed, likely directional`);\n } else {\n value = flowDirection * 0.15;\n confidence = 0.3;\n details.push(`Single whale $${(flowMagnitude / 1e6).toFixed(1)}M — may be operational (ETF/AP/rebalance)`);\n }\n }\n\n // Funding extreme warning: if funding is extreme in the same direction as flow,\n // the trade may be crowded (whale joining a crowded side = lower edge)\n if (isFundingExtreme && Math.sign(fundingRate) === flowDirection) {\n confidence *= 0.7; // reduce confidence\n details.push(`Crowded funding (${(fundingRate * 100).toFixed(3)}%) — whale joining crowded side`);\n }\n\n // Divergence penalty\n if (crossVenueDivergence) {\n confidence *= 0.5;\n details.push('WARNING: on-chain vs perps divergence — conflicting signals');\n }\n\n return {\n name: this.name,\n value: clamp(value),\n confidence: Math.min(confidence, 1.0),\n source: this.description,\n details: details.join('; '),\n };\n }\n}\n","/**\n * Strategy registry — runs all enabled strategies and collects signals.\n */\n\nimport type { Signal } from '../scoring.js';\nimport type { Strategy, StrategyContext, StrategyConfig } from './types.js';\nimport { SentimentContrarianStrategy } from './sentiment-contrarian.js';\nimport { BreakoutOnChainStrategy } from './breakout-onchain.js';\nimport { FundingRateStrategy } from './funding-rate.js';\nimport { DexFlowStrategy } from './dex-flow.js';\nimport { HyperliquidFlowStrategy } from './hyperliquid-flow.js';\nimport { MultiTimeframeStrategy } from './multi-timeframe.js';\nimport { CrossSectionalMomentumStrategy } from './cross-sectional-momentum.js';\nimport { TradingViewSignalStrategy } from './tradingview-signal.js';\nimport { BtcNetworkHealthStrategy } from './btc-network-health.js';\nimport { PredictionMarketStrategy } from './prediction-market.js';\nimport { SocialVolumeStrategy } from './social-volume.js';\nimport { KronosVolForecastStrategy } from './kronos-vol-forecast.js';\nimport { NarrativeVacuumStrategy } from './narrative-vacuum.js';\nimport { WhaleIntentStrategy } from './whale-intent.js';\n\nexport type { Strategy, StrategyContext, StrategyConfig };\nexport { SentimentContrarianStrategy, BreakoutOnChainStrategy, FundingRateStrategy };\nexport { DexFlowStrategy, HyperliquidFlowStrategy };\nexport { MultiTimeframeStrategy, CrossSectionalMomentumStrategy };\nexport { TradingViewSignalStrategy };\nexport { BtcNetworkHealthStrategy };\nexport { PredictionMarketStrategy, SocialVolumeStrategy };\nexport { KronosVolForecastStrategy };\nexport { NarrativeVacuumStrategy, WhaleIntentStrategy };\n\nexport const DEFAULT_STRATEGIES: Strategy[] = [\n // SentimentContrarianStrategy removed from default list — fires only at F&G\n // extremes (<25 or >75), returns zero 85% of the time, wasting 20% of sentiment\n // weight budget. Re-enable via config when F&G regime is extreme.\n new BreakoutOnChainStrategy(), // technical — breakout + volume\n new FundingRateStrategy(), // onchain — HL funding rates\n // DexFlowStrategy disabled — our tokens (BTC, SOL, HYPE, FARTCOIN) trade on\n // CEXes, not EVM DEXes. DEXScreener returns 0 txns/hr for most tokens.\n new HyperliquidFlowStrategy(), // onchain — HL flow/OI/orderbook\n new MultiTimeframeStrategy(), // technical — multi-TF EMA alignment\n new CrossSectionalMomentumStrategy(), // technical — relative strength ranking\n new TradingViewSignalStrategy(), // technical — TradingView MCP indicators\n new BtcNetworkHealthStrategy(), // technical — BTC network health\n new PredictionMarketStrategy(), // event — prediction market catalysts\n new SocialVolumeStrategy(), // sentiment — social volume contrarian\n new KronosVolForecastStrategy(), // technical — ML volatility forecast\n new NarrativeVacuumStrategy(), // onchain — whale flow vs narrative vacuum\n new WhaleIntentStrategy(), // smartMoney — whale intent classifier\n];\n\n/**\n * Run all enabled strategies and collect signals.\n * Each strategy runs independently — one failure doesn't break others.\n */\nexport async function runStrategies(\n ctx: StrategyContext,\n configs?: Record<string, StrategyConfig>,\n): Promise<Signal[]> {\n const signals: Signal[] = [];\n\n const tasks = DEFAULT_STRATEGIES.map(async (strategy) => {\n // Check if strategy is explicitly disabled\n const config = configs?.[strategy.name];\n if (config && !config.enabled) return null;\n\n try {\n const signal = await strategy.analyze(ctx);\n\n // Apply weight override if configured\n if (config?.weight !== undefined) {\n // Weight override is handled at the scoring layer, but we tag it\n (signal as any)._weightOverride = config.weight;\n }\n\n return signal;\n } catch (err) {\n // Strategy failed — return a zero-confidence signal so scoring isn't affected\n console.error(`Strategy ${strategy.name} failed: ${(err as Error).message}`);\n return {\n name: strategy.name,\n value: 0.0,\n confidence: 0.0,\n source: strategy.description,\n details: `Error: ${(err as Error).message}`,\n } satisfies Signal;\n }\n });\n\n const results = await Promise.allSettled(tasks);\n\n for (const result of results) {\n if (result.status === 'fulfilled' && result.value) {\n signals.push(result.value);\n }\n }\n\n return signals;\n}\n","/**\n * Funding rate provider — fetches perpetual futures funding rates from Binance.\n * Free public API, no authentication required.\n * Rate limit: 2400 req/min (generous).\n */\n\nconst BINANCE_BASE = 'https://fapi.binance.com';\n\n// Map CoinGecko token IDs to Binance perp symbols\nconst TOKEN_TO_SYMBOL: Record<string, string> = {\n bitcoin: 'BTCUSDT',\n ethereum: 'ETHUSDT',\n solana: 'SOLUSDT',\n avalanche: 'AVAXUSDT',\n cardano: 'ADAUSDT',\n polkadot: 'DOTUSDT',\n near: 'NEARUSDT',\n cosmos: 'ATOMUSDT',\n sui: 'SUIUSDT',\n aptos: 'APTUSDT',\n uniswap: 'UNIUSDT',\n aave: 'AAVEUSDT',\n maker: 'MKRUSDT',\n chainlink: 'LINKUSDT',\n arbitrum: 'ARBUSDT',\n optimism: 'OPUSDT',\n polygon: 'MATICUSDT',\n dogecoin: 'DOGEUSDT',\n litecoin: 'LTCUSDT',\n filecoin: 'FILUSDT',\n render: 'RENDERUSDT',\n injective: 'INJUSDT',\n jupiter: 'JUPUSDT',\n pendle: 'PENDLEUSDT',\n pepe: 'PEPEUSDT',\n};\n\nexport interface FundingRateData {\n rate8h: number; // Current 8h funding rate (e.g., 0.0001 = 0.01%)\n annualizedRate: number; // Annualized funding rate\n exchange: string;\n symbol: string;\n timestamp: number;\n}\n\nexport class FundingRateProvider {\n /** Get the current funding rate for a token. Returns null if token has no perp market. */\n async getFundingRate(tokenId: string): Promise<FundingRateData | null> {\n const symbol = TOKEN_TO_SYMBOL[tokenId];\n if (!symbol) return null;\n\n try {\n const res = await fetch(`${BINANCE_BASE}/fapi/v1/fundingRate?symbol=${symbol}&limit=1`);\n if (!res.ok) return null;\n\n const data = await res.json() as Array<{ fundingRate: string; fundingTime: number }>;\n if (!data || data.length === 0) return null;\n\n const rate8h = parseFloat(data[0]!.fundingRate);\n if (!Number.isFinite(rate8h)) return null;\n // Annualize: 3 funding periods per day × 365 days\n const annualizedRate = rate8h * 3 * 365;\n\n return {\n rate8h,\n annualizedRate,\n exchange: 'binance',\n symbol,\n timestamp: data[0]!.fundingTime,\n };\n } catch {\n return null;\n }\n }\n}\n","/**\n * Token unlock provider — fetches upcoming vesting/unlock events.\n * Uses DefiLlama's free unlocks API (no authentication required).\n * Fallback: CoinGecko status_updates for governance events.\n */\n\nconst DEFILLAMA_UNLOCKS = 'https://api.llama.fi/protocol';\n\n// Map CoinGecko IDs to DefiLlama protocol slugs\nconst TOKEN_TO_PROTOCOL: Record<string, string> = {\n uniswap: 'uniswap',\n aave: 'aave',\n maker: 'makerdao',\n compound: 'compound-finance',\n curve: 'curve-dex',\n lido: 'lido',\n arbitrum: 'arbitrum',\n optimism: 'optimism',\n polygon: 'polygon',\n sushi: 'sushi',\n jupiter: 'jupiter',\n pendle: 'pendle',\n injective: 'injective-protocol',\n render: 'render-network',\n};\n\nexport interface UnlockEvent {\n /** Percentage of circulating supply being unlocked */\n percentOfSupply: number;\n /** Days until the unlock */\n daysUntil: number;\n /** Description of the unlock */\n description: string;\n}\n\nexport interface TokenUnlockData {\n upcomingUnlocks: UnlockEvent[];\n totalUpcomingPercent: number;\n}\n\nexport class TokenUnlocksProvider {\n /** Get upcoming unlock events for a token. Returns null if no data available. */\n async getUnlocks(tokenId: string): Promise<TokenUnlockData | null> {\n const slug = TOKEN_TO_PROTOCOL[tokenId];\n if (!slug) return null;\n\n try {\n const res = await fetch(`${DEFILLAMA_UNLOCKS}/${slug}`);\n if (!res.ok) return null;\n\n const data = await res.json() as any;\n\n // DefiLlama protocol endpoint includes mcap and token info\n // We estimate unlock pressure from token allocation data\n const mcap = data?.mcap;\n const fdv = data?.fdvData;\n\n if (!mcap || !fdv || fdv <= 0) return null;\n\n // Ratio of circulating to fully-diluted gives us how much is still locked\n const circulatingRatio = mcap / fdv;\n const lockedPercent = (1 - circulatingRatio) * 100;\n\n if (lockedPercent < 1) {\n // Fully circulating — no unlock pressure\n return { upcomingUnlocks: [], totalUpcomingPercent: 0 };\n }\n\n // Estimate: assume locked tokens vest linearly over 2 years\n // This is an approximation — real schedules vary\n const monthlyUnlockPercent = lockedPercent / 24;\n const weeklyUnlockPercent = monthlyUnlockPercent / 4;\n\n const unlocks: UnlockEvent[] = [];\n\n if (weeklyUnlockPercent > 0.5) {\n unlocks.push({\n percentOfSupply: weeklyUnlockPercent,\n daysUntil: 7,\n description: `Estimated ~${weeklyUnlockPercent.toFixed(1)}% weekly vesting (${lockedPercent.toFixed(0)}% still locked)`,\n });\n }\n\n if (monthlyUnlockPercent > 1) {\n unlocks.push({\n percentOfSupply: monthlyUnlockPercent,\n daysUntil: 30,\n description: `Estimated ~${monthlyUnlockPercent.toFixed(1)}% monthly vesting`,\n });\n }\n\n return {\n upcomingUnlocks: unlocks,\n totalUpcomingPercent: weeklyUnlockPercent,\n };\n } catch {\n return null;\n }\n }\n}\n","/**\n * Twitter Sentiment Provider — fetches token-specific sentiment data from Twitter API v2.\n * Uses OAuth 1.0a for user-context authentication (higher rate limits than app-only).\n * Rate limit: 10 requests/min, 100 tweets/request (Recent Search free tier).\n */\n\nimport { createHmac, randomBytes } from 'node:crypto';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\n\nexport interface TwitterSentimentData {\n mentionVolume: number; // Total tweets in last hour vs 24h hourly average\n sentimentScore: number; // Simple keyword-based sentiment (-1 to +1)\n engagementWeightedSentiment: number; // Sentiment weighted by engagement\n volumeSpike: number; // Ratio of last-hour volume to 24h hourly average\n tweetCount: number; // Total tweets analyzed\n llmSentiment?: number; // LLM-analyzed sentiment (-1 to +1)\n llmConfidence?: number; // average LLM confidence\n llmBullishPercent?: number; // % of tweets classified bullish\n llmBearishPercent?: number; // % classified bearish\n}\n\ninterface TwitterTweet {\n id: string;\n text: string;\n created_at: string;\n public_metrics: {\n like_count: number;\n retweet_count: number;\n reply_count: number;\n };\n author_id: string;\n}\n\ninterface TwitterApiResponse {\n data: TwitterTweet[];\n meta: {\n result_count: number;\n next_token?: string;\n };\n}\n\ninterface OpenAISentimentResponse {\n sentiment: 'BULLISH' | 'BEARISH' | 'NEUTRAL';\n confidence: number;\n}\n\ninterface TweetWithEngagement {\n text: string;\n engagement: number;\n}\n\nconst TWITTER_BASE = 'https://api.twitter.com/2';\n\n// Map CoinGecko token IDs to Twitter search queries\nconst TOKEN_TO_SEARCH: Record<string, string> = {\n bitcoin: '$BTC OR #bitcoin',\n ethereum: '$ETH OR #ethereum',\n solana: '$SOL OR #solana',\n arbitrum: '$ARB OR #arbitrum',\n uniswap: '$UNI OR #uniswap',\n aave: '$AAVE OR #aave',\n chainlink: '$LINK OR #chainlink',\n cardano: '$ADA OR #cardano',\n polkadot: '$DOT OR #polkadot',\n avalanche: '$AVAX OR #avalanche',\n near: '$NEAR OR #near',\n cosmos: '$ATOM OR #cosmos',\n sui: '$SUI OR #sui',\n aptos: '$APT OR #aptos',\n maker: '$MKR OR #maker',\n optimism: '$OP OR #optimism',\n polygon: '$MATIC OR #polygon',\n dogecoin: '$DOGE OR #dogecoin',\n litecoin: '$LTC OR #litecoin',\n filecoin: '$FIL OR #filecoin',\n render: '$RENDER OR #render',\n injective: '$INJ OR #injective',\n jupiter: '$JUP OR #jupiter',\n pendle: '$PENDLE OR #pendle',\n pepe: '$PEPE OR #pepe',\n};\n\n// Sentiment keywords\nconst BULLISH_WORDS = [\n 'bullish', 'moon', 'pump', 'buy', 'long', 'breakout', 'ath', 'send it', 'lfg', 'wagmi', 'undervalued',\n 'hodl', 'diamond hands', 'to the moon', 'rocket', 'bull run', 'green', 'gainz', 'surge', 'rally'\n];\n\nconst BEARISH_WORDS = [\n 'bearish', 'dump', 'sell', 'short', 'crash', 'dead', 'rekt', 'ngmi', 'overvalued', 'scam',\n 'paper hands', 'bear market', 'red', 'dip', 'correction', 'bubble', 'rugpull', 'bloodbath'\n];\n\nexport class TwitterSentimentProvider {\n private cacheDir: string;\n private cacheTTL = 5 * 60 * 1000; // 5 minutes\n\n constructor() {\n this.cacheDir = join(homedir(), '.sherwood', 'agent', 'cache');\n }\n\n /** Get Twitter sentiment data for a token. Returns null if no data or API failure. */\n async getSentiment(tokenId: string): Promise<TwitterSentimentData | null> {\n const query = TOKEN_TO_SEARCH[tokenId];\n if (!query) {\n // For unknown tokens, try to use the token symbol if available\n return null;\n }\n\n // Check cache first\n const cached = await this.readCache(tokenId);\n if (cached) return cached;\n\n try {\n // Fetch recent tweets (last 24 hours for volume analysis)\n const tweets = await this.fetchTweets(query, new Date(Date.now() - 24 * 60 * 60 * 1000));\n if (!tweets || tweets.length === 0) return null;\n\n // Calculate metrics\n const data = await this.analyzeTweets(tweets);\n\n // Cache results\n await this.writeCache(tokenId, data);\n\n return data;\n } catch (err) {\n console.error(`Twitter API error for ${tokenId}: ${(err as Error).message}`);\n return null;\n }\n }\n\n /** Fetch tweets using Twitter API v2 Recent Search with OAuth 1.0a. */\n private async fetchTweets(query: string, startTime: Date): Promise<TwitterTweet[] | null> {\n const apiKey = process.env.TWITTER_API_KEY;\n const apiSecret = process.env.TWITTER_API_SECRET;\n const accessToken = process.env.TWITTER_ACCESS_TOKEN;\n const accessTokenSecret = process.env.TWITTER_ACCESS_TOKEN_SECRET;\n\n if (!apiKey || !apiSecret || !accessToken || !accessTokenSecret) {\n throw new Error('Twitter API credentials not found in environment variables');\n }\n\n const url = new URL(`${TWITTER_BASE}/tweets/search/recent`);\n url.searchParams.set('query', query);\n url.searchParams.set('start_time', startTime.toISOString());\n url.searchParams.set('max_results', '100');\n url.searchParams.set('tweet.fields', 'created_at,public_metrics,author_id');\n\n // Generate OAuth 1.0a signature\n const oauthParams: Record<string, string> = {\n oauth_consumer_key: apiKey,\n oauth_token: accessToken,\n oauth_signature_method: 'HMAC-SHA1',\n oauth_timestamp: Math.floor(Date.now() / 1000).toString(),\n oauth_nonce: randomBytes(16).toString('hex'),\n oauth_version: '1.0',\n };\n\n // Create signature base string\n const params = { ...oauthParams, ...Object.fromEntries(url.searchParams) };\n const sortedParams = Object.keys(params)\n .sort()\n .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)\n .join('&');\n\n const signatureBaseString = `GET&${encodeURIComponent(url.origin + url.pathname)}&${encodeURIComponent(sortedParams)}`;\n\n // Create signing key\n const signingKey = `${encodeURIComponent(apiSecret)}&${encodeURIComponent(accessTokenSecret)}`;\n\n // Generate signature\n const signature = createHmac('sha1', signingKey).update(signatureBaseString).digest('base64');\n\n // Create Authorization header\n const authHeader = 'OAuth ' + Object.entries({ ...oauthParams, oauth_signature: signature })\n .map(([key, value]) => `${encodeURIComponent(key)}=\"${encodeURIComponent(value)}\"`)\n .join(', ');\n\n const response = await fetch(url.toString(), {\n headers: {\n 'Authorization': authHeader,\n 'User-Agent': 'Sherwood-Agent/1.0'\n }\n });\n\n if (!response.ok) {\n if (response.status === 429) {\n // Rate limited\n return null;\n }\n throw new Error(`Twitter API error: ${response.status} ${response.statusText}`);\n }\n\n const data = await response.json() as TwitterApiResponse;\n return data.data || [];\n }\n\n /** Analyze sentiment using OpenAI GPT-4o-mini. */\n private async analyzeSentimentWithLLM(tweets: TwitterTweet[]): Promise<{\n llmSentiment: number;\n llmConfidence: number;\n llmBullishPercent: number;\n llmBearishPercent: number;\n } | null> {\n const openaiKey = process.env.OPENAI_API_KEY;\n if (!openaiKey) {\n return null;\n }\n\n try {\n // Prepare tweets with engagement data\n const tweetsWithEngagement: TweetWithEngagement[] = tweets.map(tweet => ({\n text: tweet.text,\n engagement: tweet.public_metrics.like_count +\n tweet.public_metrics.retweet_count +\n tweet.public_metrics.reply_count,\n }));\n\n // Keep LLM pass bounded so full scanner doesn't timeout under 10-token auto mode.\n // Use top-engagement tweets first because they carry the most signal.\n const llmTweets = [...tweetsWithEngagement]\n .sort((a, b) => b.engagement - a.engagement)\n .slice(0, 40);\n if (llmTweets.length === 0) {\n return null;\n }\n\n // Batch tweets into groups of 20\n const batchSize = 20;\n const batches: TweetWithEngagement[][] = [];\n for (let i = 0; i < llmTweets.length; i += batchSize) {\n batches.push(llmTweets.slice(i, i + batchSize));\n }\n\n const allResults: (OpenAISentimentResponse & { engagement: number })[] = [];\n let consecutiveBatchFailures = 0;\n const MAX_CONSECUTIVE_BATCH_FAILURES = 2;\n\n // Process each batch\n for (const batch of batches) {\n // Sanitize tweet text to prevent prompt injection:\n // 1. Strip dangerous chars first (bidi overrides, zero-width, control chars)\n // 2. Flatten newlines\n // 3. Escape quotes/backslashes\n // 4. Truncate\n const sanitizeTweet = (text: string): string => {\n return text\n .replace(/[\\x00-\\x1f\\x7f]/g, '') // strip control chars\n .replace(/[\\u200B-\\u200D\\u202A-\\u202E\\u2066-\\u2069\\u00AD\\uFEFF]/g, '') // strip bidi overrides + zero-width chars\n .replace(/\\n/g, ' ') // flatten newlines\n .replace(/[\"\\\\]/g, (c) => `\\\\${c}`) // escape quotes/backslashes\n .slice(0, 280); // truncate last (after stripping, not before)\n };\n\n const tweetTexts = batch.map((tweet, idx) => `${idx + 1}. \"${sanitizeTweet(tweet.text)}\"`).join('\\n');\n\n const systemPrompt = `You are a crypto market sentiment analyzer. For each tweet, classify sentiment as BULLISH, BEARISH, or NEUTRAL with confidence 0-100. Consider sarcasm, irony, and CT slang.\nReturn ONLY valid JSON with this exact shape:\n{\"results\":[{\"sentiment\":\"BULLISH|BEARISH|NEUTRAL\",\"confidence\":0-100}]}`;\n\n const userPrompt = `Analyze these ${batch.length} tweets:\\n${tweetTexts}`;\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 10_000);\n const response = await fetch('https://api.openai.com/v1/chat/completions', {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${openaiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n model: 'gpt-4o-mini',\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt },\n ],\n response_format: { type: 'json_object' },\n temperature: 0,\n }),\n signal: controller.signal,\n });\n clearTimeout(timeout);\n\n if (!response.ok) {\n console.warn(`OpenAI API error: ${response.status} ${response.statusText}`);\n consecutiveBatchFailures++;\n if (consecutiveBatchFailures >= MAX_CONSECUTIVE_BATCH_FAILURES) return null;\n continue;\n }\n\n const data = await response.json();\n const content = data.choices?.[0]?.message?.content;\n if (!content) {\n console.warn('OpenAI API returned no content');\n consecutiveBatchFailures++;\n if (consecutiveBatchFailures >= MAX_CONSECUTIVE_BATCH_FAILURES) return null;\n continue;\n }\n\n // Parse JSON response — strip markdown code blocks if present\n let batchResults: OpenAISentimentResponse[];\n try {\n let jsonStr = content.trim();\n // OpenAI sometimes wraps JSON in ```json ... ``` markdown blocks\n if (jsonStr.startsWith('```')) {\n jsonStr = jsonStr.replace(/^```(?:json)?\\s*\\n?/, '').replace(/\\n?```\\s*$/, '');\n }\n const parsed = JSON.parse(jsonStr) as unknown;\n let parsedResults: unknown[] | null = null;\n\n if (Array.isArray(parsed)) {\n parsedResults = parsed;\n } else if (parsed && typeof parsed === 'object') {\n const obj = parsed as Record<string, unknown>;\n if (Array.isArray(obj.results)) {\n parsedResults = obj.results;\n } else if (Array.isArray(obj.tweets)) {\n parsedResults = obj.tweets;\n } else if (Array.isArray(obj.sentiments)) {\n parsedResults = obj.sentiments;\n }\n }\n\n if (!parsedResults) {\n const arrMatch = jsonStr.match(/\\[[\\s\\S]*\\]/);\n if (arrMatch) {\n const recovered = JSON.parse(arrMatch[0]) as unknown;\n if (Array.isArray(recovered)) {\n parsedResults = recovered;\n }\n }\n }\n\n if (!parsedResults) {\n throw new Error('No parseable sentiment array');\n }\n\n batchResults = parsedResults.map((item) => {\n const row = (item && typeof item === 'object') ? item as Record<string, unknown> : {};\n const sentimentRaw = String(row.sentiment ?? row.label ?? row.classification ?? 'NEUTRAL').toUpperCase();\n const confidenceRaw = Number(row.confidence ?? row.score ?? 50);\n return {\n sentiment: sentimentRaw as OpenAISentimentResponse['sentiment'],\n confidence: confidenceRaw,\n };\n });\n\n if (batchResults.length > batch.length) {\n batchResults = batchResults.slice(0, batch.length);\n } else if (batchResults.length < batch.length) {\n while (batchResults.length < batch.length) {\n batchResults.push({ sentiment: 'NEUTRAL', confidence: 50 });\n }\n }\n\n // Validate each element to prevent NaN propagation from malformed responses\n const VALID_SENTIMENTS = new Set(['BULLISH', 'BEARISH', 'NEUTRAL']);\n for (const r of batchResults) {\n if (!VALID_SENTIMENTS.has(r.sentiment)) r.sentiment = 'NEUTRAL';\n if (typeof r.confidence !== 'number' || !Number.isFinite(r.confidence)) r.confidence = 50;\n r.confidence = Math.max(0, Math.min(100, r.confidence));\n }\n consecutiveBatchFailures = 0;\n } catch (parseErr) {\n console.warn(`Failed to parse OpenAI response: ${parseErr}`);\n consecutiveBatchFailures++;\n if (consecutiveBatchFailures >= MAX_CONSECUTIVE_BATCH_FAILURES) return null;\n // Continue using neutral placeholders for this batch instead of failing whole token scan.\n batchResults = batch.map(() => ({ sentiment: 'NEUTRAL', confidence: 50 }));\n }\n\n // Combine with engagement data\n for (let i = 0; i < batchResults.length; i++) {\n allResults.push({\n ...batchResults[i],\n engagement: batch[i].engagement,\n });\n }\n }\n\n // Calculate weighted sentiment metrics\n const bullishTweets = allResults.filter(r => r.sentiment === 'BULLISH');\n const bearishTweets = allResults.filter(r => r.sentiment === 'BEARISH');\n\n const totalTweets = allResults.length;\n const llmBullishPercent = (bullishTweets.length / totalTweets) * 100;\n const llmBearishPercent = (bearishTweets.length / totalTweets) * 100;\n\n // Calculate engagement-weighted sentiment score\n let totalWeightedScore = 0;\n let totalWeight = 0;\n let totalConfidence = 0;\n\n for (const result of allResults) {\n let sentimentMultiplier = 0;\n if (result.sentiment === 'BULLISH') {\n sentimentMultiplier = 1;\n } else if (result.sentiment === 'BEARISH') {\n sentimentMultiplier = -1;\n }\n\n const weight = (result.confidence / 100) * (1 + Math.log10(result.engagement + 1));\n totalWeightedScore += sentimentMultiplier * weight;\n totalWeight += weight;\n totalConfidence += result.confidence;\n }\n\n const llmSentiment = totalWeight > 0 ? Math.max(-1, Math.min(1, totalWeightedScore / totalWeight)) : 0;\n const llmConfidence = totalTweets > 0 ? totalConfidence / totalTweets : 0;\n\n return {\n llmSentiment,\n llmConfidence,\n llmBullishPercent,\n llmBearishPercent,\n };\n\n } catch (error) {\n console.warn(`LLM sentiment analysis failed: ${error}`);\n return null;\n }\n }\n\n /** Analyze tweets to calculate sentiment metrics. */\n private async analyzeTweets(tweets: TwitterTweet[]): Promise<TwitterSentimentData> {\n const now = Date.now();\n const oneHourAgo = now - 60 * 60 * 1000;\n\n // Separate recent (last hour) vs all tweets\n const recentTweets = tweets.filter(t => new Date(t.created_at).getTime() > oneHourAgo);\n const allTweets = tweets;\n\n // Calculate mention volume (recent vs average)\n const recentVolume = recentTweets.length;\n const avgHourlyVolume = allTweets.length / 24; // 24 hours of data\n const volumeSpike = avgHourlyVolume > 0 ? recentVolume / avgHourlyVolume : 1;\n\n // Analyze sentiment for all tweets (keyword-based)\n let bullishCount = 0;\n let bearishCount = 0;\n let totalEngagement = 0;\n let engagementWeightedBullish = 0;\n let engagementWeightedBearish = 0;\n\n for (const tweet of allTweets) {\n const text = tweet.text.toLowerCase();\n const engagement = tweet.public_metrics.like_count +\n tweet.public_metrics.retweet_count +\n tweet.public_metrics.reply_count;\n\n let tweetBullish = 0;\n let tweetBearish = 0;\n\n // Count sentiment words\n for (const word of BULLISH_WORDS) {\n if (text.includes(word)) tweetBullish++;\n }\n for (const word of BEARISH_WORDS) {\n if (text.includes(word)) tweetBearish++;\n }\n\n if (tweetBullish > tweetBearish) {\n bullishCount++;\n engagementWeightedBullish += engagement;\n } else if (tweetBearish > tweetBullish) {\n bearishCount++;\n engagementWeightedBearish += engagement;\n }\n\n totalEngagement += engagement;\n }\n\n // Calculate keyword-based sentiment scores\n const totalAnalyzed = bullishCount + bearishCount;\n const sentimentScore = totalAnalyzed > 0\n ? Math.max(-1, Math.min(1, (bullishCount - bearishCount) / totalAnalyzed))\n : 0;\n\n const engagementWeightedSentiment = totalEngagement > 0\n ? Math.max(-1, Math.min(1, (engagementWeightedBullish - engagementWeightedBearish) / totalEngagement))\n : sentimentScore;\n\n // Try LLM sentiment analysis\n const llmResult = await this.analyzeSentimentWithLLM(allTweets);\n\n const baseData: TwitterSentimentData = {\n mentionVolume: recentVolume,\n sentimentScore,\n engagementWeightedSentiment,\n volumeSpike,\n tweetCount: allTweets.length,\n };\n\n // Add LLM results if available\n if (llmResult) {\n return {\n ...baseData,\n llmSentiment: llmResult.llmSentiment,\n llmConfidence: llmResult.llmConfidence,\n llmBullishPercent: llmResult.llmBullishPercent,\n llmBearishPercent: llmResult.llmBearishPercent,\n };\n }\n\n return baseData;\n }\n\n /** Read cached sentiment data. */\n private async readCache(tokenId: string): Promise<TwitterSentimentData | null> {\n try {\n const cacheFile = join(this.cacheDir, `twitter-${tokenId}.json`);\n const raw = await readFile(cacheFile, 'utf-8');\n const cached = JSON.parse(raw) as { ts: number; data: TwitterSentimentData };\n\n if (Date.now() - cached.ts < this.cacheTTL) {\n return cached.data;\n }\n } catch {\n // No cache or invalid cache\n }\n return null;\n }\n\n /** Write sentiment data to cache. */\n private async writeCache(tokenId: string, data: TwitterSentimentData): Promise<void> {\n try {\n await mkdir(this.cacheDir, { recursive: true });\n const cacheFile = join(this.cacheDir, `twitter-${tokenId}.json`);\n await writeFile(cacheFile, JSON.stringify({ ts: Date.now(), data }), 'utf-8');\n } catch {\n // Cache write failure is non-fatal\n }\n }\n\n /** Get sentiment data with fallback to token symbol for unknown tokens. */\n async getSentimentWithSymbol(tokenId: string, tokenSymbol?: string): Promise<TwitterSentimentData | null> {\n // Try with known token mapping first\n let result = await this.getSentiment(tokenId);\n\n // If no result and we have a symbol, try searching with symbol\n if (!result && tokenSymbol) {\n const symbolQuery = `$${tokenSymbol.toUpperCase()} OR #${tokenSymbol.toLowerCase()}`;\n try {\n const tweets = await this.fetchTweets(symbolQuery, new Date(Date.now() - 24 * 60 * 60 * 1000));\n if (tweets && tweets.length > 0) {\n result = await this.analyzeTweets(tweets);\n await this.writeCache(tokenId, result);\n }\n } catch {\n // Symbol-based search failed\n }\n }\n\n return result;\n }\n}\n","/**\n * Generic Python subprocess bridge for Fincept scripts.\n *\n * Calls vendored Python scripts in cli/scripts/fincept/ as one-shot\n * subprocesses, parses their JSON stdout, and returns typed results.\n */\n\nimport { execFile } from \"node:child_process\";\nimport { join, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { existsSync } from \"node:fs\";\n\nconst __dirname = fileURLToPath(new URL(\".\", import.meta.url));\n\n/**\n * Resolve the fincept scripts directory. Works both in source mode\n * (cli/src/providers/fincept/ → ../../.. → cli/scripts/fincept) and\n * bundled mode (cli/dist/ → .. → cli/scripts/fincept).\n */\nfunction resolveScriptsDir(): string {\n // Try source layout first: cli/src/providers/fincept/ → cli/scripts/fincept\n const fromSource = join(__dirname, \"..\", \"..\", \"..\", \"scripts\", \"fincept\");\n if (existsSync(fromSource)) return fromSource;\n // Bundled layout: cli/dist/ → cli/scripts/fincept\n const fromDist = join(__dirname, \"..\", \"scripts\", \"fincept\");\n if (existsSync(fromDist)) return fromDist;\n // Fallback: relative to cwd (typically repo root or cli/)\n const fromCwd = join(process.cwd(), \"scripts\", \"fincept\");\n if (existsSync(fromCwd)) return fromCwd;\n const fromCwdCli = join(process.cwd(), \"cli\", \"scripts\", \"fincept\");\n if (existsSync(fromCwdCli)) return fromCwdCli;\n // Last resort\n return fromSource;\n}\n\n/** Absolute path to the vendored fincept scripts directory. */\nexport const FINCEPT_SCRIPTS_DIR = resolveScriptsDir();\n\nexport interface BridgeResult<T = unknown> {\n ok: boolean;\n data?: T;\n error?: string;\n latencyMs: number;\n}\n\n// ---------------------------------------------------------------------------\n// In-memory cache\n// ---------------------------------------------------------------------------\n\ninterface CacheEntry {\n ts: number;\n data: unknown;\n}\n\nconst cache = new Map<string, CacheEntry>();\n\n/** Clear the in-memory bridge cache (useful in tests). */\nexport function clearFinceptCache(): void {\n cache.clear();\n}\n\n// ---------------------------------------------------------------------------\n// Core bridge function\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\nconst MAX_BUFFER = 10 * 1024 * 1024; // 10 MB\n\n/**\n * Run a Fincept Python script and return its parsed JSON output.\n *\n * @param script - Filename of the script inside FINCEPT_SCRIPTS_DIR (e.g. \"blockchain_com_data.py\")\n * @param args - CLI arguments to pass after the script path\n * @param timeoutMs - Subprocess timeout in milliseconds (default 30 s)\n * @param cacheTtlMs - How long to cache results in memory (0 = no cache)\n */\nexport async function callFincept<T = unknown>(\n script: string,\n args: string[] = [],\n timeoutMs: number = DEFAULT_TIMEOUT_MS,\n cacheTtlMs: number = 0,\n): Promise<BridgeResult<T>> {\n const cacheKey = `${script}:${args.join(\":\")}`;\n\n // Check cache\n if (cacheTtlMs > 0) {\n const entry = cache.get(cacheKey);\n if (entry && Date.now() - entry.ts < cacheTtlMs) {\n return { ok: true, data: entry.data as T, latencyMs: 0 };\n }\n }\n\n // Validate script name — prevent path traversal\n if (script.includes('/') || script.includes('\\\\') || !script.endsWith('.py')) {\n return { ok: false, error: `Invalid script name: ${script}`, latencyMs: 0 };\n }\n const scriptPath = join(FINCEPT_SCRIPTS_DIR, script);\n const t0 = Date.now();\n\n return new Promise<BridgeResult<T>>((resolve) => {\n execFile(\n \"python3\",\n [scriptPath, ...args],\n {\n timeout: timeoutMs,\n maxBuffer: MAX_BUFFER,\n env: process.env,\n },\n (error, stdout, _stderr) => {\n const latencyMs = Date.now() - t0;\n\n // Subprocess was killed (timeout)\n if (error && \"killed\" in error && error.killed) {\n resolve({\n ok: false,\n error: `Script \"${script}\" timed out after ${timeoutMs}ms`,\n latencyMs,\n });\n return;\n }\n\n // Non-zero exit or other spawn error\n if (error) {\n resolve({\n ok: false,\n error: `Script \"${script}\" failed: ${error.message}`,\n latencyMs,\n });\n return;\n }\n\n // Empty stdout\n const raw = stdout.trim();\n if (!raw) {\n resolve({\n ok: false,\n error: `Script \"${script}\" returned empty output`,\n latencyMs,\n });\n return;\n }\n\n // Parse JSON\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n resolve({\n ok: false,\n error: `Script \"${script}\" returned invalid JSON: ${raw.slice(0, 200)}`,\n latencyMs,\n });\n return;\n }\n\n // Check for error field in response\n if (\n parsed !== null &&\n typeof parsed === \"object\" &&\n \"error\" in parsed &&\n typeof (parsed as Record<string, unknown>).error === \"string\"\n ) {\n resolve({\n ok: false,\n error: (parsed as Record<string, string>).error,\n latencyMs,\n });\n return;\n }\n\n // Success — cache if requested\n if (cacheTtlMs > 0) {\n cache.set(cacheKey, { ts: Date.now(), data: parsed });\n }\n\n resolve({ ok: true, data: parsed as T, latencyMs });\n },\n );\n });\n}\n","/**\n * Messari fundamentals wrapper via Fincept bridge.\n *\n * Provides supply metrics, revenue, and developer activity for tokens.\n */\n\nimport { callFincept } from \"./bridge.js\";\n\nconst CACHE_TTL = 60 * 60 * 1_000; // 1 hour\n\nexport interface MessariFundamentals {\n marketCap: number;\n supply: {\n circulating: number;\n max: number;\n percentCirculating: number;\n };\n revenueUsd24h: number;\n revenueGrowth7d: number;\n developerActivity: number;\n}\n\n/**\n * Map CoinGecko IDs to Messari slugs where they differ.\n * Most IDs are identical — only overrides listed here.\n */\nconst CG_TO_MESSARI: Record<string, string> = {\n \"avalanche-2\": \"avalanche\",\n \"worldcoin-wld\": \"worldcoin\",\n \"pudgy-penguins\": \"pudgy-penguins\",\n \"fetch-ai\": \"fetch-ai\",\n};\n\n/** Messari API response shape (subset we care about). */\ninterface MessariResponse {\n data: {\n id: string;\n metrics: {\n market_data: {\n price_usd: number;\n market_cap: { current_marketcap_usd: number };\n };\n supply: {\n circulating: number;\n max: number;\n y_2050: number;\n };\n blockchain_stats_24_hours: {\n revenue_usd: number;\n };\n developer_activity: {\n commits_last_3_months: number;\n };\n };\n };\n}\n\n/**\n * Fetch Messari fundamentals for a token via the Fincept bridge.\n *\n * @param tokenId - CoinGecko token ID (e.g. \"ethereum\", \"avalanche-2\")\n * @returns Parsed fundamentals or null if unavailable\n */\nexport async function getMessariFundamentals(\n tokenId: string,\n): Promise<MessariFundamentals | null> {\n const slug = CG_TO_MESSARI[tokenId] ?? tokenId;\n\n const result = await callFincept<MessariResponse>(\n \"messari_data.py\",\n [\"metrics\", slug],\n 30_000,\n CACHE_TTL,\n );\n\n if (!result.ok || !result.data?.data?.metrics) {\n return null;\n }\n\n const m = result.data.data.metrics;\n const circulating = m.supply?.circulating ?? 0;\n const max = m.supply?.max ?? 0;\n\n return {\n marketCap: m.market_data?.market_cap?.current_marketcap_usd ?? 0,\n supply: {\n circulating,\n max,\n percentCirculating: max > 0 ? circulating / max : 0,\n },\n revenueUsd24h: m.blockchain_stats_24_hours?.revenue_usd ?? 0,\n revenueGrowth7d: 0, // Messari doesn't expose this directly\n developerActivity: m.developer_activity?.commits_last_3_months ?? 0,\n };\n}\n","/**\n * Blockchain.com BTC network stats wrapper.\n *\n * Fetches hash rate, difficulty, mempool size, miner revenue, market price,\n * and transaction count via the Fincept Python bridge.\n */\n\nimport { callFincept } from \"./bridge.js\";\n\nconst CACHE_TTL = 30 * 60 * 1000; // 30 minutes\n\nexport interface BtcNetworkStats {\n hashRate: number;\n difficulty: number;\n mempoolSize: number;\n minerRevenueBtc: number;\n marketPriceUsd: number;\n transactionCount: number;\n}\n\ninterface RawStats {\n hash_rate: number;\n difficulty: number;\n mempool_size: number;\n miners_revenue_btc: number;\n market_price_usd: number;\n n_tx: number;\n}\n\n/**\n * Fetch BTC network statistics from Blockchain.com via the Fincept bridge.\n * Returns null if the script fails or returns invalid data.\n */\nexport async function getBtcNetworkStats(): Promise<BtcNetworkStats | null> {\n const result = await callFincept<RawStats>(\n \"blockchain_com_data.py\",\n [\"stats\"],\n 30_000,\n CACHE_TTL,\n );\n\n if (!result.ok || !result.data) {\n return null;\n }\n\n const raw = result.data;\n return {\n hashRate: raw.hash_rate ?? 0,\n difficulty: raw.difficulty ?? 0,\n mempoolSize: raw.mempool_size ?? 0,\n minerRevenueBtc: raw.miners_revenue_btc ?? 0,\n marketPriceUsd: raw.market_price_usd ?? 0,\n transactionCount: raw.n_tx ?? 0,\n };\n}\n","/**\n * Polymarket/Manifold prediction market wrapper.\n *\n * Fetches crypto-relevant prediction markets via the Fincept Python bridge\n * and exposes them as typed PredictionMarket objects.\n */\n\nimport { callFincept } from \"./bridge.js\";\n\nconst CACHE_TTL = 5 * 60 * 1000; // 5 minutes\n\nexport interface PredictionMarket {\n question: string;\n probability: number;\n volume: number;\n}\n\nconst CRYPTO_KEYWORDS = [\n \"bitcoin\",\n \"btc\",\n \"ethereum\",\n \"eth\",\n \"crypto\",\n \"sec\",\n \"etf\",\n \"fed\",\n \"rate\",\n \"inflation\",\n \"regulation\",\n \"stablecoin\",\n \"defi\",\n];\n\ninterface RawMarket {\n question: string;\n outcomePrices: string[];\n probability: number;\n volume: number;\n active: boolean;\n closed: boolean;\n}\n\n/**\n * Fetch crypto-relevant prediction markets from Polymarket/Manifold.\n * Returns up to 10 active markets whose question matches a crypto keyword.\n */\nexport async function getCryptoPredictions(): Promise<PredictionMarket[]> {\n try {\n const result = await callFincept<RawMarket[]>(\n \"polymarket.py\",\n [\"get_markets\", \"50\"],\n 30_000,\n CACHE_TTL,\n );\n\n if (!result.ok || !result.data) {\n return [];\n }\n\n const markets = result.data\n .filter((m) => {\n if (!m.active || m.closed) return false;\n const q = m.question.toLowerCase();\n return CRYPTO_KEYWORDS.some((kw) => q.includes(kw));\n })\n .map((m): PredictionMarket => {\n const probability =\n m.outcomePrices && m.outcomePrices.length > 0\n ? parseFloat(m.outcomePrices[0])\n : m.probability;\n return {\n question: m.question,\n probability: isNaN(probability) ? m.probability : probability,\n volume: m.volume,\n };\n })\n .slice(0, 10);\n\n return markets;\n } catch {\n return [];\n }\n}\n","/**\n * CryptoCompare social / news wrapper via Fincept bridge.\n */\n\nimport { callFincept } from './bridge.js';\n\nconst NEWS_CACHE_TTL = 10 * 60 * 1_000; // 10 minutes\n\n/** Map CoinGecko token IDs → CryptoCompare symbols. */\nconst CC_SYMBOL_MAP: Record<string, string> = {\n bitcoin: 'BTC',\n ethereum: 'ETH',\n solana: 'SOL',\n aave: 'AAVE',\n uniswap: 'UNI',\n chainlink: 'LINK',\n ripple: 'XRP',\n dogecoin: 'DOGE',\n polkadot: 'DOT',\n avalanche: 'AVAX',\n arbitrum: 'ARB',\n hyperliquid: 'HYPE',\n zcash: 'ZEC',\n fartcoin: 'FARTCOIN',\n pepe: 'PEPE',\n cardano: 'ADA',\n ethena: 'ENA',\n 'worldcoin-wld': 'WLD',\n bittensor: 'TAO',\n sui: 'SUI',\n near: 'NEAR',\n aptos: 'APT',\n};\n\nexport interface SocialData {\n socialVolume24h: number;\n socialVolumeSpike: number;\n newsCount24h: number;\n topNewsSentiment: number;\n}\n\n/**\n * Fetch CryptoCompare news and derive social-data metrics for a token.\n * Returns null when news data is unavailable.\n */\nexport async function getSocialData(tokenId: string): Promise<SocialData | null> {\n const symbol = CC_SYMBOL_MAP[tokenId] ?? tokenId.toUpperCase();\n\n try {\n const newsResult = await callFincept(\n 'cryptocompare_data.py',\n ['news'],\n 30_000,\n NEWS_CACHE_TTL,\n );\n\n const rawData = newsResult.ok ? (newsResult.data as Record<string, unknown>) : undefined;\n // Script returns { articles: [...] } (lowercase), raw API returns { Data: [...] } (uppercase)\n const articles: Array<{ title?: string; categories?: string; tags?: string }> =\n (rawData?.articles as Array<{ title?: string; categories?: string; tags?: string }>)\n ?? (rawData?.Data as Array<{ title?: string; categories?: string; tags?: string }>)\n ?? [];\n\n const lowerSymbol = symbol.toLowerCase();\n const lowerTokenId = tokenId.toLowerCase();\n\n const matchingArticles = articles.filter((a) => {\n const haystack = [a.title, a.categories, a.tags]\n .filter(Boolean)\n .join(' ')\n .toLowerCase();\n return haystack.includes(lowerSymbol) || haystack.includes(lowerTokenId);\n });\n\n return {\n socialVolume24h: matchingArticles.length,\n socialVolumeSpike: 1.0, // baseline — no historical comparison available\n newsCount24h: matchingArticles.length,\n topNewsSentiment: 0, // CryptoCompare doesn't have sentiment scores\n };\n } catch {\n return null;\n }\n}\n","/**\n * CryptoCompare candle wrapper — fallback candle source replacing CoinGecko OHLC.\n * Hyperliquid remains the primary candle source.\n */\n\nimport { callFincept } from \"./bridge.js\";\nimport type { Candle } from \"../../agent/technical.js\";\n\nconst OHLCV_CACHE_TTL = 2 * 60 * 60 * 1000; // 2 hours\n\n/** CoinGecko token ID → CryptoCompare symbol */\nconst CC_SYMBOL: Record<string, string> = {\n bitcoin: \"BTC\",\n ethereum: \"ETH\",\n solana: \"SOL\",\n aave: \"AAVE\",\n uniswap: \"UNI\",\n chainlink: \"LINK\",\n ripple: \"XRP\",\n dogecoin: \"DOGE\",\n polkadot: \"DOT\",\n avalanche: \"AVAX\",\n arbitrum: \"ARB\",\n hyperliquid: \"HYPE\",\n zcash: \"ZEC\",\n fartcoin: \"FARTCOIN\",\n pepe: \"PEPE\",\n cardano: \"ADA\",\n ethena: \"ENA\",\n \"worldcoin-wld\": \"WLD\",\n bittensor: \"TAO\",\n sui: \"SUI\",\n near: \"NEAR\",\n aptos: \"APT\",\n \"pudgy-penguins\": \"PENGU\",\n blur: \"BLUR\",\n \"fetch-ai\": \"FET\",\n};\n\ninterface CCBar {\n time: number;\n open: number;\n high: number;\n low: number;\n close: number;\n volumefrom: number;\n volumeto: number;\n}\n\ninterface CCResponse {\n ohlcv: CCBar[];\n}\n\n/**\n * Fetch hourly candles from CryptoCompare via Fincept bridge and downsample to 4h.\n * Returns null if the token is unmapped or data is insufficient.\n */\nexport async function getCryptoCompareCandles(\n tokenId: string,\n limit: number = 180,\n): Promise<Candle[] | null> {\n const symbol = CC_SYMBOL[tokenId];\n if (!symbol) return null;\n\n const result = await callFincept<CCResponse>(\n \"cryptocompare_data.py\",\n [\"hourly\", symbol, \"USD\", String(limit * 4)],\n 30_000,\n OHLCV_CACHE_TTL,\n );\n\n if (!result.ok || !result.data) return null;\n\n const bars = result.data.ohlcv;\n if (!bars || bars.length < 10) return null;\n\n // Downsample hourly bars to 4h candles\n const candles: Candle[] = [];\n for (let i = 0; i + 3 < bars.length; i += 4) {\n const chunk = bars.slice(i, i + 4);\n candles.push({\n timestamp: chunk[0]!.time * 1000,\n open: chunk[0]!.open,\n high: Math.max(...chunk.map((b) => b.high)),\n low: Math.min(...chunk.map((b) => b.low)),\n close: chunk[chunk.length - 1]!.close,\n volume: chunk.reduce((sum, b) => sum + b.volumeto, 0),\n });\n }\n\n return candles.length > 0 ? candles : null;\n}\n","/**\n * Kronos volatility forecaster via Python subprocess.\n *\n * Runs the Kronos-mini foundation model (4.1M params) on CPU to predict\n * N future price paths from OHLCV candles, then computes volatility metrics\n * from the path spread.\n *\n * Latency: ~2.3s for 5 paths on CPU (tested on 2-core VPS).\n * Memory: ~350MB peak (model + PyTorch).\n * Cache: 1 hour (vol forecasts don't need per-cycle updates).\n */\n\nimport { execFile } from \"node:child_process\";\nimport { join } from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { FINCEPT_SCRIPTS_DIR } from \"./bridge.js\";\nimport type { Candle } from \"../../agent/technical.js\";\n\n/** Kronos needs PyTorch — use the dedicated venv if it exists,\n * otherwise fall back to system python3. */\nconst KRONOS_VENV_PYTHON = join(process.env.HOME ?? \"/home/ana\", \".sherwood\", \"kronos-venv\", \"bin\", \"python3\");\nconst KRONOS_PYTHON = existsSync(KRONOS_VENV_PYTHON) ? KRONOS_VENV_PYTHON : \"python3\";\n\nconst CACHE_TTL = 60 * 60 * 1000; // 1 hour\nconst TIMEOUT_MS = 30_000;\n\nexport interface KronosVolForecast {\n /** Annualized volatility from Monte Carlo path spread. */\n predictedVolatility: number;\n /** Per-candle (4h) volatility as a fraction. */\n predictedVol4h: number;\n /** Directional bias from mean path: -1 (bearish) to +1 (bullish). */\n directionalBias: number;\n /** % spread between worst and best path at prediction horizon. */\n pathSpreadPct: number;\n /** Number of candles predicted. */\n predictionHorizon: number;\n /** Number of Monte Carlo paths generated. */\n sampleCount: number;\n /** Last input close price. */\n lastClose: number;\n /** Mean predicted close at horizon. */\n meanPredictedClose: number;\n /** Inference time in milliseconds. */\n inferenceTimeMs: number;\n}\n\ninterface CacheEntry {\n ts: number;\n data: KronosVolForecast;\n}\n\nconst cache = new Map<string, CacheEntry>();\n\n/**\n * Run Kronos volatility forecast on OHLCV candles.\n *\n * @param tokenId - Token identifier (used for caching)\n * @param candles - Historical OHLCV candles (need at least 30, ideally 200)\n * @param samples - Number of Monte Carlo paths (default 5)\n * @param predLen - Number of future candles to predict (default 24)\n * @returns Volatility forecast or null on failure\n */\nexport async function getKronosVolForecast(\n tokenId: string,\n candles: Candle[],\n samples: number = 5,\n predLen: number = 24,\n): Promise<KronosVolForecast | null> {\n // Check cache\n const cacheKey = `kronos:${tokenId}`;\n const cached = cache.get(cacheKey);\n if (cached && Date.now() - cached.ts < CACHE_TTL) {\n return cached.data;\n }\n\n if (!candles || candles.length < 30) return null;\n\n // Prepare input JSON\n const input = JSON.stringify({\n candles: candles.map((c) => ({\n timestamp: c.timestamp,\n open: c.open,\n high: c.high,\n low: c.low,\n close: c.close,\n volume: c.volume,\n })),\n });\n\n const scriptPath = join(FINCEPT_SCRIPTS_DIR, \"kronos_predict.py\");\n\n return new Promise<KronosVolForecast | null>((resolve) => {\n const proc = execFile(\n KRONOS_PYTHON,\n [scriptPath, \"--samples\", String(samples), \"--pred-len\", String(predLen)],\n {\n timeout: TIMEOUT_MS,\n maxBuffer: 5 * 1024 * 1024,\n env: { ...process.env },\n },\n (error, stdout, stderr) => {\n if (error) {\n console.error(` [kronos] Inference failed: ${error.message}`);\n resolve(null);\n return;\n }\n\n try {\n const result = JSON.parse(stdout.trim());\n if (result.error) {\n console.error(` [kronos] ${result.error}`);\n resolve(null);\n return;\n }\n\n const forecast = result as KronosVolForecast;\n cache.set(cacheKey, { ts: Date.now(), data: forecast });\n resolve(forecast);\n } catch {\n console.error(` [kronos] Invalid JSON output`);\n resolve(null);\n }\n },\n );\n\n // Write candle data to stdin\n if (proc.stdin) {\n proc.stdin.write(input);\n proc.stdin.end();\n }\n });\n}\n","/**\n * DefiLlama data wrapper.\n *\n * Fetches protocol TVL and yield pool data via the Fincept Python bridge\n * (defillama_data.py).\n */\n\nimport { callFincept } from \"./bridge.js\";\n\nconst TVL_CACHE_TTL = 30 * 60 * 1000; // 30 minutes\n\ninterface DefiLlamaTvlResponse {\n tvl?: number;\n name?: string;\n [key: string]: unknown;\n}\n\ninterface DefiLlamaYieldsResponse {\n data?: Array<Record<string, unknown>>;\n}\n\nexport interface YieldPool {\n pool: string;\n chain: string;\n project: string;\n symbol: string;\n tvlUsd: number;\n apy: number;\n}\n\n/**\n * Fetch protocol TVL from DefiLlama.\n *\n * @param tokenId - CoinGecko-style token ID (e.g. \"aave\", \"uniswap\")\n * @returns TVL in USD, or null on failure\n */\nexport async function getProtocolTvl(\n tokenId: string,\n): Promise<number | null> {\n const res = await callFincept<DefiLlamaTvlResponse>(\n \"defillama_data.py\",\n [\"protocol\", tokenId],\n 30_000,\n TVL_CACHE_TTL,\n );\n\n if (!res.ok || !res.data) return null;\n return res.data.tvl ?? null;\n}\n\n/**\n * Fetch top yield pools from DefiLlama.\n *\n * @param limit - Maximum number of pools to return (default 20)\n * @returns Array of yield pool entries, or null on failure\n */\nexport async function getYieldPools(\n limit: number = 20,\n): Promise<YieldPool[] | null> {\n const res = await callFincept<DefiLlamaYieldsResponse>(\n \"defillama_data.py\",\n [\"yields\"],\n 30_000,\n 60 * 60 * 1000, // 1 hour cache\n );\n\n if (!res.ok || !res.data?.data) return null;\n\n return res.data.data\n .slice(0, limit)\n .map((entry) => ({\n pool: String(entry.pool ?? \"\"),\n chain: String(entry.chain ?? \"\"),\n project: String(entry.project ?? \"\"),\n symbol: String(entry.symbol ?? \"\"),\n tvlUsd: Number(entry.tvlUsd ?? 0),\n apy: Number(entry.apy ?? 0),\n }));\n}\n","/**\n * DexScreener data wrapper.\n *\n * Searches DEX pairs and fetches boosted tokens via the Fincept Python\n * bridge (dexscreener_data.py).\n */\n\nimport { callFincept } from \"./bridge.js\";\n\nconst CACHE_TTL = 5 * 60 * 1000; // 5 minutes\n\nexport interface FinceptDexPair {\n chainId: string;\n baseToken: { address: string; name: string; symbol: string };\n quoteToken: { address: string; name: string; symbol: string };\n priceUsd: string;\n volume: { h24: number };\n liquidity: { usd: number };\n priceChange: { h24: number };\n txns: { h24: { buys: number; sells: number } };\n}\n\ninterface DexSearchResponse {\n pairs?: FinceptDexPair[];\n}\n\ninterface TokenBoost {\n tokenAddress: string;\n chainId: string;\n amount: number;\n}\n\ninterface DexBoostedResponse {\n data?: Array<Record<string, unknown>>;\n}\n\n/**\n * Search DexScreener for trading pairs matching a query.\n *\n * @param query - Search string (token name, symbol, or address)\n * @returns Array of matching pairs, or empty array on failure\n */\nexport async function searchDexPairs(\n query: string,\n): Promise<FinceptDexPair[]> {\n const res = await callFincept<DexSearchResponse>(\n \"dexscreener_data.py\",\n [\"search\", query],\n 30_000,\n CACHE_TTL,\n );\n\n if (!res.ok || !res.data?.pairs) return [];\n return res.data.pairs;\n}\n\n/**\n * Fetch currently boosted tokens from DexScreener.\n *\n * @returns Array of boosted token entries, or empty array on failure\n */\nexport async function getTokenBoosts(): Promise<TokenBoost[]> {\n const res = await callFincept<DexBoostedResponse>(\n \"dexscreener_data.py\",\n [\"boosted\"],\n 30_000,\n CACHE_TTL,\n );\n\n if (!res.ok || !res.data?.data) return [];\n\n return res.data.data.map((entry) => ({\n tokenAddress: String(entry.tokenAddress ?? \"\"),\n chainId: String(entry.chainId ?? \"\"),\n amount: Number(entry.amount ?? 0),\n }));\n}\n","/**\n * Signal logger — appends every token analysis to a JSONL file for tracking.\n * Fire-and-forget: never blocks analysis, never crashes on failure.\n */\n\nimport { appendFile, stat, rename, mkdir } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport type { TokenAnalysis } from './index.js';\nimport type { ScoringWeights } from './scoring.js';\nimport type { JudgeVerdict } from './judge.js';\n\nconst SIGNAL_DIR = join(homedir(), '.sherwood', 'agent');\nconst SIGNAL_FILE = join(SIGNAL_DIR, 'signal-history.jsonl');\nconst MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB\n\nexport interface SignalLogEntry {\n timestamp: string;\n tokenId: string;\n tokenSymbol: string;\n price: number;\n decision: 'STRONG_BUY' | 'BUY' | 'HOLD' | 'SELL' | 'STRONG_SELL';\n score: number;\n confidence: number;\n signals: { name: string; value: number; confidence: number }[];\n regime: string;\n btcCorrelation: number;\n weights: { smartMoney: number; technical: number; sentiment: number; onchain: number; fundamental: number; event: number };\n // LLM judge fields (v1 — additive, ignored by older consumers)\n judgeVerdict?: \"confirm\" | \"veto\";\n judgeReasoning?: string;\n judgeConfidence?: number;\n preJudgeAction?: string;\n preJudgeScore?: number;\n judgeLatencyMs?: number;\n judgeCached?: boolean;\n}\n\nexport interface JudgeLogData {\n verdict: JudgeVerdict;\n preJudgeAction: string;\n preJudgeScore: number;\n latencyMs: number;\n cached: boolean;\n}\n\n/**\n * Log a signal entry after token analysis completes.\n * Fire-and-forget — call without await. Errors are swallowed with a warning.\n */\nexport function logSignal(\n analysis: TokenAnalysis,\n price: number,\n weights: ScoringWeights,\n judgeData?: JudgeLogData,\n): void {\n // Fire-and-forget — do not await\n _writeSignal(analysis, price, weights, judgeData).catch((err) => {\n console.error(`[signal-logger] Warning: failed to log signal: ${(err as Error).message}`);\n });\n}\n\nasync function _writeSignal(\n analysis: TokenAnalysis,\n price: number,\n weights: ScoringWeights,\n judgeData?: JudgeLogData,\n): Promise<void> {\n await mkdir(SIGNAL_DIR, { recursive: true });\n\n // Auto-rotate if file exceeds 10MB\n try {\n const stats = await stat(SIGNAL_FILE);\n if (stats.size > MAX_FILE_SIZE) {\n const dateStr = new Date().toISOString().slice(0, 10);\n const rotatedName = join(SIGNAL_DIR, `signal-history-${dateStr}.jsonl`);\n await rename(SIGNAL_FILE, rotatedName);\n }\n } catch {\n // File doesn't exist yet, that's fine\n }\n\n const entry: SignalLogEntry = {\n timestamp: new Date().toISOString(),\n tokenId: analysis.token,\n tokenSymbol: analysis.token.toUpperCase().slice(0, 6),\n price,\n decision: analysis.decision.action,\n score: analysis.decision.score,\n confidence: analysis.decision.confidence,\n signals: analysis.decision.signals.map((s) => ({\n name: s.name,\n value: s.value,\n confidence: s.confidence,\n })),\n regime: analysis.regime?.regime ?? 'unknown',\n btcCorrelation: analysis.correlation?.btcScore ?? 0,\n weights: {\n smartMoney: weights.smartMoney,\n technical: weights.technical,\n sentiment: weights.sentiment,\n onchain: weights.onchain,\n fundamental: weights.fundamental,\n event: weights.event,\n },\n // Judge fields (additive — omitted when judge is off)\n ...(judgeData ? {\n judgeVerdict: judgeData.verdict.verdict,\n judgeReasoning: judgeData.verdict.reasoning,\n judgeConfidence: judgeData.verdict.confidence,\n preJudgeAction: judgeData.preJudgeAction,\n preJudgeScore: judgeData.preJudgeScore,\n judgeLatencyMs: judgeData.latencyMs,\n judgeCached: judgeData.cached,\n } : {}),\n };\n\n const line = JSON.stringify(entry) + '\\n';\n await appendFile(SIGNAL_FILE, line, 'utf-8');\n}\n\n/** Get the path to the signal history file. */\nexport function getSignalFilePath(): string {\n return SIGNAL_FILE;\n}\n\n/** Get the signal directory path. */\nexport function getSignalDir(): string {\n return SIGNAL_DIR;\n}\n","/**\n * LLM Judge — confirm or veto borderline trade decisions using Claude.\n *\n * V1 scope: confirm + veto only. No score overrides, no direction flips.\n * Fallback is always pass-through — judge can never block a cycle.\n */\n\nimport { createHash } from \"node:crypto\";\nimport { readFile, writeFile, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport chalk from \"chalk\";\nimport { chatCompletion } from \"../lib/venice.js\";\nimport { getVeniceApiKey } from \"../lib/config.js\";\nimport type { TradeDecision } from \"./scoring.js\";\nimport type { TechnicalSignals } from \"./technical.js\";\nimport type { PortfolioState } from \"./risk.js\";\n\n// ── Types ──\n\nexport interface JudgeVerdict {\n verdict: \"confirm\" | \"veto\";\n reasoning: string;\n risks: string[];\n confidence: number;\n}\n\nexport interface JudgeConfig {\n enabled: boolean;\n model: string;\n topN: number;\n scoreBand: [number, number];\n timeoutMs: number;\n cacheTtlMs: number;\n}\n\nexport const DEFAULT_JUDGE_CONFIG: JudgeConfig = {\n enabled: false,\n model: \"llama-3.3-70b\",\n topN: 3,\n scoreBand: [0.10, 0.50],\n timeoutMs: 8_000,\n cacheTtlMs: 900_000, // 15 min\n};\n\n/** Context provided to the judge for a single token decision. */\nexport interface JudgeContext {\n tokenId: string;\n currentPrice: number;\n decision: TradeDecision;\n technicalSignals?: TechnicalSignals;\n fearAndGreed?: number;\n sentimentZScore?: number;\n fundingRate?: number;\n regime?: string;\n btcRegime?: string;\n btcBias?: string;\n suppressionFactor?: number;\n portfolio: {\n openCount: number;\n cashPct: number;\n hasPositionThisToken: boolean;\n inStopCooldown: boolean;\n dailyPnlPct: number;\n };\n recentCloses?: number[];\n}\n\n// ── Fallback ──\n\nconst FALLBACK_VERDICT: JudgeVerdict = {\n verdict: \"confirm\",\n reasoning: \"fallback\",\n risks: [],\n confidence: 0,\n};\n\n// ── Cache ──\n\nconst CACHE_DIR = join(homedir(), \".sherwood\", \"agent\", \"cache\");\n\nfunction cacheKey(ctx: JudgeContext): string {\n const priceBucket = Math.round(ctx.currentPrice / (ctx.currentPrice * 0.005));\n const signalTuples = ctx.decision.signals\n .map((s) => `${s.name}:${Math.sign(s.value)}`)\n .sort()\n .join(\",\");\n const raw = `${ctx.tokenId}:${priceBucket}:${signalTuples}:${ctx.regime ?? \"unknown\"}`;\n return createHash(\"sha256\").update(raw).digest(\"hex\").slice(0, 16);\n}\n\nasync function readCache(key: string, ttlMs: number): Promise<JudgeVerdict | undefined> {\n try {\n const path = join(CACHE_DIR, `judge-${key}.json`);\n const data = JSON.parse(await readFile(path, \"utf-8\"));\n if (Date.now() - (data.cachedAt ?? 0) < ttlMs) {\n return data.verdict as JudgeVerdict;\n }\n } catch {\n // Cache miss or read error — fine\n }\n return undefined;\n}\n\nasync function writeCache(key: string, verdict: JudgeVerdict): Promise<void> {\n try {\n await mkdir(CACHE_DIR, { recursive: true });\n const path = join(CACHE_DIR, `judge-${key}.json`);\n await writeFile(path, JSON.stringify({ cachedAt: Date.now(), verdict }), \"utf-8\");\n } catch {\n // Write failure is non-fatal\n }\n}\n\n// ── Prompt ──\n\nconst SYSTEM_PROMPT = `You are a crypto trade-decision reviewer. Your job is to confirm or veto a proposed trade entry.\n\nReturn ONLY valid JSON matching this schema:\n{\"verdict\":\"confirm\"|\"veto\",\"reasoning\":\"<max 240 chars>\",\"risks\":[\"<max 80 chars each, 0-4 items>\"],\"confidence\":<0-1>}\n\nVETO conditions (veto if ANY apply):\n- Regime contradicts direction (e.g., trending-down regime for a long entry)\n- Token is in stop cooldown (recently stopped out)\n- Daily portfolio loss exceeds -2% and this is a new entry\n- Insufficient data quality (too many signals missing/zero)\n- BTC bearish bias with high suppression for a long entry on an alt\n\nCONFIRM otherwise. Default to confirm — only veto with clear justification.\nNo prose outside the JSON. No markdown fences.`;\n\nfunction buildUserPrompt(ctx: JudgeContext): string {\n const d = ctx.decision;\n const topSignals = [...d.signals]\n .sort((a, b) => Math.abs(b.value) - Math.abs(a.value))\n .slice(0, 3)\n .map((s) => `${s.name}=${s.value.toFixed(3)}(c=${s.confidence.toFixed(2)})`)\n .join(\", \");\n\n const lines: string[] = [\n `Token: ${ctx.tokenId} | Price: $${ctx.currentPrice.toPrecision(4)}`,\n `Proposed: ${d.action} | Score: ${d.score.toFixed(3)} | Confidence: ${d.confidence.toFixed(2)}`,\n `Top signals: ${topSignals}`,\n `Regime: ${ctx.regime ?? \"unknown\"} | BTC regime: ${ctx.btcRegime ?? \"unknown\"}`,\n `BTC bias: ${ctx.btcBias ?? \"neutral\"} | Suppression: ${(ctx.suppressionFactor ?? 1).toFixed(2)}`,\n ];\n\n if (ctx.technicalSignals) {\n const t = ctx.technicalSignals;\n lines.push(`RSI: ${isNaN(t.rsi) ? \"N/A\" : t.rsi.toFixed(1)} | MACD hist: ${isNaN(t.macd.histogram) ? \"N/A\" : t.macd.histogram.toFixed(4)} | ATR: ${isNaN(t.atr) ? \"N/A\" : t.atr.toFixed(4)}`);\n }\n\n if (ctx.fearAndGreed !== undefined) {\n lines.push(`F&G: ${ctx.fearAndGreed}${ctx.sentimentZScore !== undefined ? ` (z=${ctx.sentimentZScore.toFixed(2)})` : \"\"}`);\n }\n\n if (ctx.fundingRate !== undefined) {\n lines.push(`Funding rate (8h): ${(ctx.fundingRate * 100).toFixed(4)}%`);\n }\n\n const p = ctx.portfolio;\n lines.push(`Portfolio: ${p.openCount} open | ${(p.cashPct * 100).toFixed(1)}% cash | dailyPnL: ${(p.dailyPnlPct * 100).toFixed(2)}%`);\n lines.push(`This token: ${p.hasPositionThisToken ? \"HAS POSITION\" : \"no position\"} | Stop cooldown: ${p.inStopCooldown ? \"YES\" : \"no\"}`);\n\n if (ctx.recentCloses?.length) {\n lines.push(`Recent closes (10d): ${ctx.recentCloses.map((c) => c.toPrecision(4)).join(\", \")}`);\n }\n\n return lines.join(\"\\n\");\n}\n\n// ── Main entry point ──\n\n/**\n * Judge a trade decision. Returns confirm/veto verdict.\n * NEVER throws — all errors return fallback confirm.\n */\nexport async function judge(\n ctx: JudgeContext,\n config: JudgeConfig = DEFAULT_JUDGE_CONFIG,\n): Promise<{ verdict: JudgeVerdict; cached: boolean; latencyMs: number }> {\n const start = Date.now();\n\n try {\n // Gate: judge disabled or no Venice API key\n if (!config.enabled || !getVeniceApiKey()) {\n return { verdict: FALLBACK_VERDICT, cached: false, latencyMs: 0 };\n }\n\n // Gate: only judge non-HOLD actions in the score band\n const absScore = Math.abs(ctx.decision.score);\n if (ctx.decision.action === \"HOLD\" || absScore < config.scoreBand[0] || absScore > config.scoreBand[1]) {\n return { verdict: FALLBACK_VERDICT, cached: false, latencyMs: 0 };\n }\n\n // Check cache\n const key = cacheKey(ctx);\n const cached = await readCache(key, config.cacheTtlMs);\n if (cached) {\n return { verdict: cached, cached: true, latencyMs: Date.now() - start };\n }\n\n // Call Venice (OpenAI-compatible chat completions)\n const result = await Promise.race([\n chatCompletion({\n model: config.model,\n messages: [\n { role: \"system\", content: SYSTEM_PROMPT },\n { role: \"user\", content: buildUserPrompt(ctx) },\n ],\n maxTokens: 400,\n temperature: 0.1,\n disableThinking: true,\n }),\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error(\"Judge timeout\")), config.timeoutMs),\n ),\n ]);\n\n // Parse response\n const parsed = parseVerdict(result.content);\n await writeCache(key, parsed);\n\n return { verdict: parsed, cached: false, latencyMs: Date.now() - start };\n } catch (err) {\n console.error(chalk.dim(` [judge] Error: ${(err as Error).message} — falling back to confirm`));\n return { verdict: FALLBACK_VERDICT, cached: false, latencyMs: Date.now() - start };\n }\n}\n\n/**\n * Determine which tokens should be judged (budget gate).\n * Returns token IDs sorted by |score| descending, capped at topN.\n */\nexport function selectJudgeCandidates(\n results: Array<{ token: string; score: number; action: string }>,\n config: JudgeConfig,\n): Set<string> {\n const candidates = results\n .filter((r) => {\n if (r.action === \"HOLD\") return false;\n const abs = Math.abs(r.score);\n // Upper bound is exclusive to match `judge()` gate at line 192\n // (`absScore > config.scoreBand[1]` rejects). Inclusive here would\n // pick a candidate that `judge()` then rejects → fallback confirm.\n return abs >= config.scoreBand[0] && abs < config.scoreBand[1];\n })\n .sort((a, b) => Math.abs(b.score) - Math.abs(a.score))\n .slice(0, config.topN);\n\n return new Set(candidates.map((c) => c.token));\n}\n\n// ── Parsing ──\n\nfunction parseVerdict(raw: string): JudgeVerdict {\n // Strip markdown fences if model wraps in ```json\n let cleaned = raw.trim();\n if (cleaned.startsWith(\"```\")) {\n cleaned = cleaned.replace(/^```(?:json)?\\s*/, \"\").replace(/\\s*```$/, \"\");\n }\n\n const obj = JSON.parse(cleaned);\n\n // Validate required fields\n if (obj.verdict !== \"confirm\" && obj.verdict !== \"veto\") {\n throw new Error(`Invalid verdict: ${obj.verdict}`);\n }\n\n return {\n verdict: obj.verdict,\n reasoning: typeof obj.reasoning === \"string\" ? obj.reasoning.slice(0, 240) : \"\",\n risks: Array.isArray(obj.risks) ? obj.risks.slice(0, 4).map((r: unknown) => String(r).slice(0, 80)) : [],\n confidence: typeof obj.confidence === \"number\" ? Math.max(0, Math.min(1, obj.confidence)) : 0.5,\n };\n}\n","/**\n * Signal smoother — rolling-window average for fast/noisy signals.\n *\n * Some signals (HL orderbook + whale flow, Nansen smart money, DEX 1h flow,\n * funding rate) are measured at a granularity finer than their actual\n * information content. Per-scan readings can flip 100% direction within\n * minutes when the underlying state hasn't meaningfully changed.\n *\n * This module wraps the per-scan reading in a rolling N-reading average\n * before it enters the scoring engine. Disagreement across the window\n * also reduces the signal's confidence — a smoothed value of 0.16 from\n * [+0.5, -0.5, +0.5] reports lower confidence than 0.16 from\n * [0.16, 0.16, 0.17].\n *\n * Slow signals (RSI/MACD/EMA — already smoothed by their indicators,\n * F&G — daily index, multi-timeframe — uses 7d candles) are passed\n * through unchanged.\n */\n\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport type { Signal } from './scoring.js';\n\n/** Signal names whose values benefit from rolling-window smoothing. */\nexport const FAST_SIGNAL_NAMES = new Set<string>([\n 'hyperliquidFlow',\n 'smartMoney',\n 'onchain', // legacy direct on-chain signal name\n 'dexFlow',\n 'fundingRate',\n]);\n\ninterface SignalReading {\n ts: number; // epoch ms\n value: number; // -1 to +1\n confidence: number;\n}\n\n/** Per-token, per-signal rolling buffer. */\ntype Cache = Record<string, Record<string, SignalReading[]>>;\n\nexport interface SmootherConfig {\n /** How many readings to retain per (token, signal) buffer. */\n windowSize: number;\n /** Maximum age (ms) for a reading to count toward the average. */\n maxAgeMs: number;\n /** Confidence multiplier when the window has zero std-dev (perfect agreement). */\n agreementBoost: number;\n /** Cap on confidence reduction from disagreement. */\n maxConfidencePenalty: number;\n}\n\nexport const DEFAULT_SMOOTHER_CONFIG: SmootherConfig = {\n windowSize: 3,\n maxAgeMs: 6 * 60 * 60 * 1000, // 6 hours — drop stale readings\n agreementBoost: 1.0,\n maxConfidencePenalty: 0.5,\n};\n\n/** Storage backend interface — disk for live, in-memory map for backtest. */\nexport interface SmootherStorage {\n load(): Promise<Cache>;\n save(cache: Cache): Promise<void>;\n}\n\n/** Disk-backed storage. Atomic write via temp file + rename. */\nexport class FileSmootherStorage implements SmootherStorage {\n constructor(private filePath: string) {}\n\n async load(): Promise<Cache> {\n try {\n const raw = await readFile(this.filePath, 'utf-8');\n return JSON.parse(raw) as Cache;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return {};\n throw err;\n }\n }\n\n async save(cache: Cache): Promise<void> {\n await mkdir(dirname(this.filePath), { recursive: true });\n const tmp = this.filePath + '.tmp';\n await writeFile(tmp, JSON.stringify(cache), 'utf-8');\n const { rename } = await import('node:fs/promises');\n await rename(tmp, this.filePath);\n }\n}\n\n/** In-memory storage — for backtest where state is per-simulation. */\nexport class MemorySmootherStorage implements SmootherStorage {\n private cache: Cache = {};\n async load(): Promise<Cache> { return this.cache; }\n async save(cache: Cache): Promise<void> { this.cache = cache; }\n}\n\nexport class SignalSmoother {\n /** In-process mutex chain — serializes load→mutate→save across tokens in\n * the same process. Does NOT protect against multiple processes racing\n * the same file; for that, the disk storage does atomic replace and\n * re-reads the freshest cache inside the mutex. See README / H1 below. */\n private writeLock: Promise<void> = Promise.resolve();\n\n constructor(\n private storage: SmootherStorage,\n private config: SmootherConfig = DEFAULT_SMOOTHER_CONFIG,\n ) {}\n\n /**\n * Smooth the given signals for a token. Fast signals get rolling-window\n * averages with confidence penalties for disagreement. Slow signals\n * pass through unchanged. Side effect: appends current readings to the\n * cache and persists.\n *\n * Serialized via writeLock: concurrent calls from within the same\n * process (e.g. parallel per-token smooths) queue on the shared mutex\n * so each sees the freshest cache. Cross-process concurrency (two agent\n * processes on the same host) is NOT fully safe — the second-to-save\n * wins. Document: run one agent per host, or use MemorySmootherStorage\n * in contexts with multiple simulation runs.\n */\n async smooth(tokenId: string, signals: Signal[], nowMs: number = Date.now()): Promise<Signal[]> {\n // Queue on the shared mutex\n const prev = this.writeLock;\n let release: () => void = () => {};\n this.writeLock = new Promise<void>((resolve) => { release = resolve; });\n await prev;\n\n try {\n return await this.smoothUnlocked(tokenId, signals, nowMs);\n } finally {\n release();\n }\n }\n\n private async smoothUnlocked(tokenId: string, signals: Signal[], nowMs: number): Promise<Signal[]> {\n const cache = await this.storage.load();\n // M2: sweep stale tokens out of the cache entirely so rotated-out\n // tokens don't leak JSON size indefinitely. A token is \"stale\" if\n // every signal buffer's newest reading is older than maxAgeMs.\n const cutoff = nowMs - this.config.maxAgeMs;\n for (const t of Object.keys(cache)) {\n if (t === tokenId) continue;\n const buffers = cache[t];\n if (!buffers) continue;\n const bufferList = Object.values(buffers);\n const anyFresh = bufferList.some((b) => b.length > 0 && b[b.length - 1]!.ts >= cutoff);\n if (!anyFresh) {\n delete cache[t];\n }\n }\n if (!cache[tokenId]) cache[tokenId] = {};\n\n const out: Signal[] = [];\n for (const sig of signals) {\n if (!FAST_SIGNAL_NAMES.has(sig.name)) {\n out.push(sig);\n continue;\n }\n\n const buffer = cache[tokenId][sig.name] ?? [];\n\n // Append current reading\n buffer.push({ ts: nowMs, value: sig.value, confidence: sig.confidence });\n\n // Drop stale readings\n const cutoff = nowMs - this.config.maxAgeMs;\n const fresh = buffer.filter((r) => r.ts >= cutoff);\n\n // Trim to window size — keep newest N\n const trimmed = fresh.slice(-this.config.windowSize);\n cache[tokenId][sig.name] = trimmed;\n\n // Compute smoothed value + confidence\n const values = trimmed.map((r) => r.value);\n const meanValue = values.reduce((a, b) => a + b, 0) / values.length;\n const meanConfidence = trimmed.reduce((a, r) => a + r.confidence, 0) / trimmed.length;\n\n // Std-dev in [-1,1] domain — max possible is 1.0 (alternating +1/-1)\n let smoothedConfidence = meanConfidence;\n if (trimmed.length > 1) {\n const variance = values.reduce((sum, v) => sum + (v - meanValue) ** 2, 0) / values.length;\n const stdDev = Math.sqrt(variance);\n // Penalize confidence proportional to std-dev (clamped)\n const penalty = Math.min(this.config.maxConfidencePenalty, stdDev);\n smoothedConfidence = Math.max(0.05, meanConfidence * (1 - penalty));\n }\n\n out.push({\n ...sig,\n value: meanValue,\n confidence: smoothedConfidence,\n details: `${sig.details} [smoothed n=${trimmed.length}, raw=${sig.value.toFixed(2)}]`,\n });\n }\n\n await this.storage.save(cache);\n return out;\n }\n}\n","/**\n * Entry gates — post-scoring filters that can downgrade an action before the\n * executor sees it. Adjacent in spirit to the LLM judge: the score + action\n * can still fire, but a cross-check against fresh market data may veto it.\n *\n * ── Velocity gate (Orca-inspired \"signal-velocity freshness\") ──\n *\n * Multi-signal stacks can fire BUY/SELL the moment the composite score crosses\n * a threshold — but if the underlying price move is already exhausted (flat or\n * reversing over the last ~1h), we're buying local tops (or shorting bottoms).\n * Orca's fix: refuse the entry if the most recent short-term velocity is not\n * aligned with the direction.\n *\n * - BUY rejected if 1h velocity < BUY_MIN_PCT (default -1.0%)\n * - SELL rejected if 1h velocity > SELL_MAX_PCT (default +1.0%)\n * - HOLD never gated.\n *\n * When velocity data is unavailable (no Hyperliquid fields, insufficient candle\n * history), the gate skips — never reject blindly.\n */\nimport type { Candle } from \"./technical.js\";\nimport type { TokenAnalysis } from \"./index.js\";\nimport type { MarketRegime } from \"./regime.js\";\n\n// ── Configurable constants (easy to calibrate) ──\n\n/** BUY rejected if 1h velocity is below this (fraction, not percent).\n * Relaxed from ±0.3% to ±1.0% — on 4h candles the velocity proxy covers\n * a 4h window, where ±0.3% is normal noise even in trending markets. */\nexport const VELOCITY_GATE_BUY_MIN_PCT = -0.01;\n\n/** SELL rejected if 1h velocity is above this (fraction, not percent). */\nexport const VELOCITY_GATE_SELL_MAX_PCT = 0.01;\n\n/** Regimes where short entries are allowed. Shorts are blocked in all other\n * regimes to prevent counter-trend fading — trade log analysis showed 25%\n * short WR in non-bearish regimes vs 67% long WR. */\nexport const SHORT_ALLOWED_REGIMES: Set<MarketRegime> = new Set([\n \"trending-down\",\n \"high-volatility\",\n]);\n\n/** EntryGate configuration — exposed on AgentConfig for easy calibration. */\nexport interface EntryGateConfig {\n /** Master toggle. When false, the gate is skipped entirely. Default true. */\n velocityGateEnabled: boolean;\n /** BUY is rejected when 1h velocity is strictly less than this. Default -0.01 (-1%). */\n velocityGateBuyMinPct: number;\n /** SELL is rejected when 1h velocity is strictly greater than this. Default +0.01 (+1%). */\n velocityGateSellMaxPct: number;\n /** When true, SELL/STRONG_SELL signals are blocked in non-bearish regimes\n * (trending-up, ranging, low-volatility). Default true. */\n regimeGateEnabled: boolean;\n /** When true, actionable entries must be backed by at least one aligned\n * high-quality signal, not just noisy/lagging components. Default true. */\n realAlphaGateEnabled: boolean;\n}\n\nexport const DEFAULT_ENTRY_GATE_CONFIG: EntryGateConfig = {\n velocityGateEnabled: true,\n velocityGateBuyMinPct: VELOCITY_GATE_BUY_MIN_PCT, // -1.0%\n velocityGateSellMaxPct: VELOCITY_GATE_SELL_MAX_PCT, // +1.0%\n regimeGateEnabled: true,\n realAlphaGateEnabled: true,\n};\n\n/** Signals allowed to justify an entry after the noisy-signal score filter.\n * Only includes signals that are actually populated by active strategies —\n * dexFlow / meanReversion / sentimentContrarian were removed when their\n * strategies were disabled. */\nconst REAL_ALPHA_THRESHOLDS: Record<string, number> = {\n smartMoney: 0.15,\n whaleIntent: 0.15,\n fundamental: 0.15,\n narrativeVacuum: 0.25,\n};\n\nfunction hasAlignedRealAlpha(result: TokenAnalysis, direction: 1 | -1): boolean {\n for (const signal of result.decision.signals) {\n const threshold = REAL_ALPHA_THRESHOLDS[signal.name];\n if (threshold === undefined) continue;\n if (direction > 0 && signal.value >= threshold) return true;\n if (direction < 0 && signal.value <= -threshold) return true;\n }\n return false;\n}\n\n/**\n * Derive a short-term velocity (fractional change) from candles.\n *\n * CoinGecko's /ohlc endpoint at days=30 returns 4h candles; at days=1 returns\n * 30m candles. Since the agent currently fetches days=30, the best proxy for\n * \"recent 1h-ish velocity\" is the last candle's close-over-previous-close\n * change (a 4h window — tight enough to capture fresh moves while respecting\n * the data we actually have).\n *\n * Returns undefined if we don't have enough data.\n */\nexport function deriveVelocityFromCandles(candles: Candle[] | undefined): number | undefined {\n if (!candles || candles.length < 2) return undefined;\n const last = candles[candles.length - 1]!;\n const prev = candles[candles.length - 2]!;\n if (!prev.close || prev.close <= 0) return undefined;\n return last.close / prev.close - 1;\n}\n\n/**\n * Resolve the velocity to use for the gate:\n * 1. Prefer an explicit Hyperliquid 1h price change when available.\n * (Units: decimal fraction, e.g. 0.012 for +1.2%. If the provider returns\n * a percent instead, callers must divide by 100 before passing it in.)\n * 2. Fall back to the most-recent candle-over-candle change.\n * 3. Otherwise undefined — gate will skip.\n */\nexport function resolveVelocity(\n priceChg1h: number | undefined,\n candles: Candle[] | undefined,\n): number | undefined {\n if (typeof priceChg1h === \"number\" && Number.isFinite(priceChg1h)) {\n return priceChg1h;\n }\n return deriveVelocityFromCandles(candles);\n}\n\n/**\n * Post-scoring hook that downgrades BUY/SELL → HOLD when recent price\n * velocity is not aligned with the proposed direction. Preserves the original\n * action/score on `result.preVelocity` (mirrors the `preJudge` pattern).\n *\n * Rules:\n * - BUY / STRONG_BUY: downgrade if velocity <= buyMinPct (flat or falling)\n * - SELL / STRONG_SELL: downgrade if velocity >= sellMaxPct (flat or rising)\n * - HOLD: never gated\n * - If velocity is undefined (no data): skip gate, return unchanged\n * - If !config.velocityGateEnabled: skip gate, return unchanged\n *\n * Edge case: exactly 0 velocity on a BUY downgrades (strict <=). Same on SELL\n * for >=. This is intentional — \"flat\" price action is not confirmation.\n */\nexport function applyVelocityGate(\n result: TokenAnalysis,\n velocity: number | undefined,\n config: EntryGateConfig = DEFAULT_ENTRY_GATE_CONFIG,\n logger: (msg: string) => void = () => {},\n): TokenAnalysis {\n if (!config.velocityGateEnabled) return result;\n if (velocity === undefined || !Number.isFinite(velocity)) return result;\n\n const action = result.decision.action;\n const isBuy = action === \"BUY\" || action === \"STRONG_BUY\";\n const isSell = action === \"SELL\" || action === \"STRONG_SELL\";\n\n if (!isBuy && !isSell) return result;\n\n const rejectBuy = isBuy && velocity <= config.velocityGateBuyMinPct;\n const rejectSell = isSell && velocity >= config.velocityGateSellMaxPct;\n\n if (!rejectBuy && !rejectSell) return result;\n\n const direction = isBuy ? \"BUY\" : \"SELL\";\n const threshold = isBuy ? config.velocityGateBuyMinPct : config.velocityGateSellMaxPct;\n const pct = (velocity * 100).toFixed(2);\n const thresholdPct = (threshold * 100).toFixed(2);\n logger(\n ` [velocity] DOWNGRADE ${result.token}: ${direction} ${action} blocked — 1h velocity ${pct}% ` +\n `vs threshold ${thresholdPct}% (signal stale / exhausted)`,\n );\n\n return {\n ...result,\n preVelocity: { action: result.decision.action, score: result.decision.score },\n decision: { ...result.decision, action: \"HOLD\" },\n };\n}\n\n/**\n * Post-scoring gate that blocks SELL/STRONG_SELL when the market regime does\n * not support shorting. Trade log analysis (32 trades): shorts had 25% WR\n * (-$194) while longs had 67% WR (+$279). Most short losses came from fading\n * a recovery trend — the regime was ranging or trending-up, not bearish.\n *\n * Only allows shorts in:\n * - trending-down: confirmed bearish regime\n * - high-volatility: directional moves can go either way\n *\n * Blocks shorts in:\n * - trending-up: counter-trend fade, historically destructive\n * - ranging: choppy, no directional edge for shorts\n * - low-volatility: not enough movement to profit from shorts\n *\n * When regime is undefined (no BTC data), the gate skips — never reject blindly.\n */\nexport function applyRegimeGate(\n result: TokenAnalysis,\n regime: MarketRegime | undefined,\n config: EntryGateConfig = DEFAULT_ENTRY_GATE_CONFIG,\n logger: (msg: string) => void = () => {},\n): TokenAnalysis {\n if (!config.regimeGateEnabled) return result;\n if (regime === undefined) return result;\n\n const action = result.decision.action;\n const isSell = action === \"SELL\" || action === \"STRONG_SELL\";\n\n if (!isSell) return result;\n\n if (SHORT_ALLOWED_REGIMES.has(regime)) return result;\n\n logger(\n ` [regime] DOWNGRADE ${result.token}: ${action} blocked — regime \"${regime}\" ` +\n `does not support shorts (allowed: ${[...SHORT_ALLOWED_REGIMES].join(\", \")})`,\n );\n\n return {\n ...result,\n preRegime: { action: result.decision.action, score: result.decision.score },\n decision: { ...result.decision, action: \"HOLD\" },\n };\n}\n\n/**\n * Blocks entries whose score is created only by noisy/lagging signals. Recent\n * signal analysis showed momentum, TradingView, funding, and HL flow were\n * negative-edge inputs; this gate requires a separate aligned alpha source.\n */\nexport function applyRealAlphaGate(\n result: TokenAnalysis,\n config: EntryGateConfig = DEFAULT_ENTRY_GATE_CONFIG,\n logger: (msg: string) => void = () => {},\n): TokenAnalysis {\n if (!config.realAlphaGateEnabled) return result;\n\n const action = result.decision.action;\n const isBuy = action === \"BUY\" || action === \"STRONG_BUY\";\n const isSell = action === \"SELL\" || action === \"STRONG_SELL\";\n if (!isBuy && !isSell) return result;\n\n const direction = isBuy ? 1 : -1;\n if (hasAlignedRealAlpha(result, direction)) return result;\n\n logger(\n ` [alpha] DOWNGRADE ${result.token}: ${action} blocked — no aligned real-alpha ` +\n `signal (requires smartMoney, fundamental, narrativeVacuum, or whaleIntent)`,\n );\n\n return {\n ...result,\n preAlpha: { action: result.decision.action, score: result.decision.score },\n decision: { ...result.decision, action: \"HOLD\" },\n };\n}\n","/**\n * Market regime detection module — analyzes BTC to classify market state.\n * Helps adjust strategy confidence based on current macro conditions.\n */\n\nimport { readFile, writeFile, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport type { Candle } from \"./technical.js\";\nimport { calculateEMA, calculateBollingerBands, calculateATR } from \"./technical.js\";\n\nexport type MarketRegime = \"trending-up\" | \"trending-down\" | \"ranging\" | \"high-volatility\" | \"low-volatility\";\n\nexport interface RegimeAnalysis {\n regime: MarketRegime;\n confidence: number; // 0-1 how sure we are about the regime\n btcTrend: \"up\" | \"down\" | \"neutral\";\n volatilityLevel: \"low\" | \"normal\" | \"high\" | \"extreme\";\n details: string;\n strategyAdjustments: Record<string, number>; // multiplier per strategy name (0.0 to 1.5)\n}\n\ninterface RegimeCache {\n timestamp: number;\n analysis: RegimeAnalysis;\n}\n\nexport class MarketRegimeDetector {\n private cacheDir: string;\n private cacheFile: string;\n\n constructor() {\n this.cacheDir = join(homedir(), '.sherwood', 'agent', 'cache');\n this.cacheFile = join(this.cacheDir, 'regime.json');\n }\n\n /**\n * Detect current market regime using BTC candles.\n * Returns cached result if less than 5 minutes old.\n */\n async detect(btcCandles: Candle[]): Promise<RegimeAnalysis> {\n // Check cache first\n try {\n const cached = await this.loadCache();\n const now = Date.now();\n const cacheAge = now - cached.timestamp;\n // 5-minute TTL — cycles run every ~25min, but a 15min cache lagged real\n // regime shifts by up to a full cycle. 5min keeps the cache useful for\n // back-to-back short scans while staying close to live state.\n const CACHE_DURATION = 5 * 60 * 1000;\n\n if (cacheAge < CACHE_DURATION) {\n return cached.analysis;\n }\n } catch {\n // Cache miss or invalid, continue to fresh analysis\n }\n\n const analysis = await this.analyzeBtc(btcCandles);\n\n // Save to cache\n try {\n await this.saveCache(analysis);\n } catch {\n // Non-critical, continue without caching\n }\n\n return analysis;\n }\n\n /**\n * Pure regime classification from a candle window — no network calls,\n * no cache, no BTC dominance lookup. Use this from the backtester to\n * compute regime per-candle without look-ahead bias or external IO.\n */\n static classifyFromCandles(candles: Candle[]): RegimeAnalysis {\n const detector = new MarketRegimeDetector();\n return detector.classifySync(candles);\n }\n\n /** Synchronous candle-only analysis — same logic as analyzeBtc minus dominance/IO. */\n classifySync(candles: Candle[]): RegimeAnalysis {\n if (candles.length < 60) {\n return this.fallbackAnalysis(\"Insufficient BTC data for regime analysis\");\n }\n\n const closes = candles.map((c) => c.close);\n const currentPrice = closes[closes.length - 1]!;\n\n // ── Fast momentum override — shared with analyzeBtc ──\n const momentumResult = this.checkMomentumOverride(candles, currentPrice);\n if (momentumResult) return momentumResult;\n\n // ── Standard EMA/ADX regime classification ──\n // Apr 2026 audit: switched from EMA(50,200) + ADX(14) to EMA(21,50) + ADX(7).\n // On 4h candles EMA(50,200) lagged 200+ hours — missed the Apr 13-14 BTC rally\n // entirely. Faster EMAs + ADX(7) respond in ~28-50 hours instead.\n const ema21 = calculateEMA(closes, 21);\n const ema50 = calculateEMA(closes, 50);\n const currentEma21 = this.getLastValidValue(ema21);\n const currentEma50 = this.getLastValidValue(ema50);\n\n const bb = calculateBollingerBands(candles, 20, 2.0);\n const currentBbWidth = this.getLastValidValue(bb.width);\n\n const atr = calculateATR(candles, 14);\n const currentAtr = this.getLastValidValue(atr);\n const atrRatio = currentAtr / currentPrice;\n\n const adx = this.calculateADX(candles, 7);\n const currentAdx = this.getLastValidValue(adx);\n\n let btcTrend: RegimeAnalysis[\"btcTrend\"] = \"neutral\";\n if (!isNaN(currentEma21) && !isNaN(currentEma50)) {\n if (currentPrice > currentEma21 && currentEma21 > currentEma50) {\n btcTrend = \"up\";\n } else if (currentPrice < currentEma21 && currentEma21 < currentEma50) {\n btcTrend = \"down\";\n }\n }\n\n let volatilityLevel: RegimeAnalysis[\"volatilityLevel\"] = \"normal\";\n if (!isNaN(currentBbWidth)) {\n if (currentBbWidth < 0.04) volatilityLevel = \"low\";\n else if (currentBbWidth > 0.20) volatilityLevel = \"extreme\";\n else if (currentBbWidth > 0.10) volatilityLevel = \"high\";\n }\n\n let regime: MarketRegime;\n let confidence: number;\n let details: string;\n\n if (volatilityLevel === \"extreme\") {\n regime = \"high-volatility\";\n confidence = 0.9;\n details = `Extreme volatility (BB ${currentBbWidth.toFixed(3)}, ATR ${atrRatio.toFixed(3)})`;\n } else if (volatilityLevel === \"low\" && currentAdx < 15) {\n regime = \"low-volatility\";\n confidence = 0.8;\n details = `Low volatility (BB ${currentBbWidth.toFixed(3)}, ADX ${currentAdx.toFixed(1)})`;\n } else {\n if (currentAdx > 20 && btcTrend !== \"neutral\") {\n if (btcTrend === \"up\") {\n regime = \"trending-up\";\n confidence = Math.min(0.9, 0.6 + (currentAdx - 20) * 0.01);\n details = `Strong uptrend (ADX ${currentAdx.toFixed(1)})`;\n } else {\n regime = \"trending-down\";\n confidence = Math.min(0.9, 0.6 + (currentAdx - 20) * 0.01);\n details = `Strong downtrend (ADX ${currentAdx.toFixed(1)})`;\n }\n } else {\n regime = \"ranging\";\n confidence = currentAdx < 15 ? 0.7 : 0.5;\n details = `Ranging (ADX ${currentAdx.toFixed(1)})`;\n }\n }\n\n return {\n regime,\n confidence,\n btcTrend,\n volatilityLevel,\n details,\n strategyAdjustments: this.getStrategyAdjustments(regime),\n };\n }\n\n private async analyzeBtc(candles: Candle[]): Promise<RegimeAnalysis> {\n // Delegate to the synchronous classifier — the async BTC dominance\n // fetch was dead code (checked absolute level, not trend; result\n // only appended to details string, never used in classification).\n return this.classifySync(candles);\n }\n\n private calculateADX(candles: Candle[], period: number): number[] {\n if (candles.length < period + 1) return [];\n\n // Calculate True Range and Directional Movement\n const trueRanges: number[] = [];\n const dmPlus: number[] = [];\n const dmMinus: number[] = [];\n\n for (let i = 1; i < candles.length; i++) {\n const curr = candles[i]!;\n const prev = candles[i - 1]!;\n\n // True Range\n const tr = Math.max(\n curr.high - curr.low,\n Math.abs(curr.high - prev.close),\n Math.abs(curr.low - prev.close)\n );\n trueRanges.push(tr);\n\n // Directional Movement\n const upMove = curr.high - prev.high;\n const downMove = prev.low - curr.low;\n\n dmPlus.push(upMove > downMove && upMove > 0 ? upMove : 0);\n dmMinus.push(downMove > upMove && downMove > 0 ? downMove : 0);\n }\n\n // Smooth the values using Wilder's smoothing\n const smoothTR = this.wilderSmooth(trueRanges, period);\n const smoothDMPlus = this.wilderSmooth(dmPlus, period);\n const smoothDMMinus = this.wilderSmooth(dmMinus, period);\n\n // Calculate DI+ and DI-\n const diPlus: number[] = [];\n const diMinus: number[] = [];\n const dx: number[] = [];\n\n for (let i = 0; i < smoothTR.length; i++) {\n if (smoothTR[i] > 0) {\n const diP = (smoothDMPlus[i] / smoothTR[i]) * 100;\n const diM = (smoothDMMinus[i] / smoothTR[i]) * 100;\n diPlus.push(diP);\n diMinus.push(diM);\n\n // Calculate DX\n const diSum = diP + diM;\n if (diSum > 0) {\n dx.push((Math.abs(diP - diM) / diSum) * 100);\n } else {\n dx.push(0);\n }\n } else {\n diPlus.push(0);\n diMinus.push(0);\n dx.push(0);\n }\n }\n\n // ADX is the smoothed DX\n return this.wilderSmooth(dx, period);\n }\n\n private wilderSmooth(values: number[], period: number): number[] {\n const result: number[] = [];\n\n for (let i = 0; i < values.length; i++) {\n if (i < period - 1) {\n result.push(NaN);\n } else if (i === period - 1) {\n // Initial smoothed value is simple average\n const sum = values.slice(i - period + 1, i + 1).reduce((a, b) => a + b, 0);\n result.push(sum / period);\n } else {\n // Subsequent values use Wilder's smoothing\n const prev = result[i - 1]!;\n const current = values[i]!;\n result.push((prev * (period - 1) + current) / period);\n }\n }\n\n return result;\n }\n\n /**\n * Fast momentum check — catches intraday trends that EMA/ADX miss.\n * Returns a RegimeAnalysis if momentum override fires, or null to fall\n * through to the standard EMA/ADX classification.\n *\n * Threshold is volatility-adjusted: 1.5× the 14-candle ATR as a\n * percentage of current price, floored at 2% and capped at 8%.\n * - BTC (daily ATR ~2%): threshold ≈ 3% — same as before\n * - SOL (daily ATR ~5%): threshold ≈ 7.5% — avoids false triggers on normal alt vol\n * - FARTCOIN (daily ATR ~10%): threshold = 8% cap — only fires on genuinely unusual moves\n *\n * Requirements for trending-up: price above threshold AND in the upper\n * half of the range (prevents false positives on bounces within a larger\n * downtrend). Symmetric for trending-down.\n */\n private checkMomentumOverride(candles: Candle[], currentPrice: number): RegimeAnalysis | null {\n const MOMENTUM_LOOKBACK = 24;\n const MIN_THRESHOLD = 0.02; // floor: 2%\n const MAX_THRESHOLD = 0.08; // cap: 8%\n const ATR_MULTIPLIER = 1.5;\n\n // Compute 14-period ATR as a percentage of current price for vol-adjustment\n const atrValues = calculateATR(candles, 14);\n const currentAtr = this.getLastValidValue(atrValues);\n const atrPct = currentPrice > 0 && !isNaN(currentAtr) ? currentAtr / currentPrice : 0.02;\n const threshold = Math.min(MAX_THRESHOLD, Math.max(MIN_THRESHOLD, atrPct * ATR_MULTIPLIER));\n\n // Compute volatility level BEFORE the override so it propagates\n // into the returned RegimeAnalysis. Previously hardcoded \"normal\"\n // which masked extreme volatility during fast moves.\n const bb = calculateBollingerBands(candles, 20, 2.0);\n const currentBbWidth = this.getLastValidValue(bb.width);\n let volLevel: RegimeAnalysis[\"volatilityLevel\"] = \"normal\";\n if (!isNaN(currentBbWidth)) {\n if (currentBbWidth < 0.04) volLevel = \"low\";\n else if (currentBbWidth > 0.20) volLevel = \"extreme\";\n else if (currentBbWidth > 0.10) volLevel = \"high\";\n }\n\n const recentCandles = candles.slice(-MOMENTUM_LOOKBACK);\n const recentLow = Math.min(...recentCandles.map((c) => c.low));\n const recentHigh = Math.max(...recentCandles.map((c) => c.high));\n const pctAboveLow = recentLow > 0 ? (currentPrice - recentLow) / recentLow : 0;\n const pctBelowHigh = recentHigh > 0 ? (recentHigh - currentPrice) / recentHigh : 0;\n\n const recentMid = (recentHigh + recentLow) / 2;\n const inUpperHalf = currentPrice >= recentMid;\n const inLowerHalf = currentPrice <= recentMid;\n\n if (pctAboveLow >= threshold && inUpperHalf) {\n return {\n regime: \"trending-up\",\n confidence: Math.min(0.85, 0.5 + pctAboveLow * 5),\n btcTrend: \"up\",\n volatilityLevel: volLevel,\n details: `Momentum override: price +${(pctAboveLow * 100).toFixed(1)}% above ${MOMENTUM_LOOKBACK}-candle low (threshold ${(threshold * 100).toFixed(1)}%, ATR-adjusted)`,\n strategyAdjustments: this.getStrategyAdjustments(\"trending-up\"),\n };\n }\n if (pctBelowHigh >= threshold && inLowerHalf) {\n return {\n regime: \"trending-down\",\n confidence: Math.min(0.85, 0.5 + pctBelowHigh * 5),\n btcTrend: \"down\",\n volatilityLevel: volLevel,\n details: `Momentum override: price -${(pctBelowHigh * 100).toFixed(1)}% below ${MOMENTUM_LOOKBACK}-candle high (threshold ${(threshold * 100).toFixed(1)}%, ATR-adjusted)`,\n strategyAdjustments: this.getStrategyAdjustments(\"trending-down\"),\n };\n }\n return null;\n }\n\n private getStrategyAdjustments(regime: MarketRegime): Record<string, number> {\n // Only active strategies. Trend-aligned signals boosted during trends,\n // contrarian signals boosted during ranging.\n const baseMultipliers: Record<MarketRegime, Record<string, number>> = {\n \"trending-up\": {\n breakoutOnChain: 1.3,\n fundingRate: 1.0,\n dexFlow: 1.2,\n sentimentContrarian: 0.7,\n hyperliquidFlow: 1.2,\n multiTimeframe: 1.2,\n crossSectionalMomentum: 1.2,\n tradingviewSignal: 1.0,\n },\n \"trending-down\": {\n breakoutOnChain: 1.3,\n fundingRate: 1.2,\n dexFlow: 1.2,\n sentimentContrarian: 0.8,\n hyperliquidFlow: 1.3,\n multiTimeframe: 1.2,\n crossSectionalMomentum: 1.2,\n tradingviewSignal: 1.0,\n },\n \"ranging\": {\n breakoutOnChain: 0.5,\n fundingRate: 1.0,\n dexFlow: 0.8,\n sentimentContrarian: 1.3,\n hyperliquidFlow: 1.0,\n multiTimeframe: 0.8,\n crossSectionalMomentum: 1.0,\n tradingviewSignal: 1.0,\n },\n \"high-volatility\": {\n breakoutOnChain: 1.0,\n fundingRate: 1.0,\n dexFlow: 0.7,\n sentimentContrarian: 0.7,\n hyperliquidFlow: 0.7,\n multiTimeframe: 0.7,\n crossSectionalMomentum: 0.8,\n tradingviewSignal: 0.8,\n },\n \"low-volatility\": {\n breakoutOnChain: 1.3,\n fundingRate: 0.8,\n dexFlow: 0.8,\n sentimentContrarian: 0.8,\n hyperliquidFlow: 0.8,\n multiTimeframe: 1.2,\n crossSectionalMomentum: 1.0,\n tradingviewSignal: 1.0,\n },\n };\n\n return baseMultipliers[regime];\n }\n\n private getLastValidValue(arr: number[]): number {\n for (let i = arr.length - 1; i >= 0; i--) {\n if (!isNaN(arr[i]!)) return arr[i]!;\n }\n return NaN;\n }\n\n private fallbackAnalysis(reason: string): RegimeAnalysis {\n return {\n regime: \"ranging\",\n confidence: 0.3,\n btcTrend: \"neutral\",\n volatilityLevel: \"normal\",\n details: reason,\n strategyAdjustments: {\n breakoutOnChain: 1.0,\n fundingRate: 1.0,\n dexFlow: 1.0,\n sentimentContrarian: 1.0,\n hyperliquidFlow: 1.0,\n multiTimeframe: 1.0,\n crossSectionalMomentum: 1.0,\n tradingviewSignal: 1.0,\n },\n };\n }\n\n private async loadCache(): Promise<RegimeCache> {\n const data = await readFile(this.cacheFile, 'utf-8');\n return JSON.parse(data) as RegimeCache;\n }\n\n private async saveCache(analysis: RegimeAnalysis): Promise<void> {\n await mkdir(this.cacheDir, { recursive: true });\n const cache: RegimeCache = {\n timestamp: Date.now(),\n analysis,\n };\n await writeFile(this.cacheFile, JSON.stringify(cache, null, 2), 'utf-8');\n }\n}","/**\n * Correlation guard module — prevents going long on alts when BTC is bearish.\n * Uses BTC technicals to assess market structure and suppress/boost alt signals.\n */\n\nimport { readFile, writeFile, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport type { Candle } from \"./technical.js\";\nimport { calculateEMA, calculateRSI, calculateMACD } from \"./technical.js\";\nimport { CoinGeckoProvider } from \"../providers/data/coingecko.js\";\nimport { HyperliquidProvider } from \"../providers/data/hyperliquid.js\";\n\nexport interface CorrelationCheck {\n btcBias: \"bullish\" | \"bearish\" | \"neutral\";\n btcScore: number; // -1 to +1\n shouldSuppress: boolean; // true if alt long signal should be reduced\n suppressionFactor: number; // 0.0 to 1.0 multiplier on alt signals\n reason: string;\n}\n\ninterface CorrelationCache {\n timestamp: number;\n btcStructure: BtcStructure;\n}\n\ninterface BtcStructure {\n price: number;\n ema50: number;\n ema200: number;\n rsi: number;\n macdDirection: \"bullish\" | \"bearish\" | \"neutral\";\n score: number;\n}\n\nexport class CorrelationGuard {\n private cacheDir: string;\n private cacheFile: string;\n private coingecko: CoinGeckoProvider;\n private hyperliquid: HyperliquidProvider;\n\n constructor() {\n this.cacheDir = join(homedir(), '.sherwood', 'agent', 'cache');\n this.cacheFile = join(this.cacheDir, 'btc-correlation.json');\n this.coingecko = new CoinGeckoProvider();\n this.hyperliquid = new HyperliquidProvider();\n }\n\n /**\n * Check correlation and return suppression factors for a given token.\n * BTC itself is never suppressed. Stablecoins are never suppressed.\n */\n async checkCorrelation(tokenId: string): Promise<CorrelationCheck> {\n // Skip correlation check for BTC and stablecoins\n if (this.shouldSkipCorrelation(tokenId)) {\n return this.neutralCheck(\"BTC or stablecoin - no correlation suppression\");\n }\n\n // Get BTC structure (cached for 10 minutes)\n const btcStructure = await this.getBtcStructure();\n\n // Determine BTC bias and score\n const { bias, score, reason } = this.assessBtcBias(btcStructure);\n\n // Calculate suppression for alt tokens\n const { shouldSuppress, suppressionFactor } = this.calculateSuppression(bias, score);\n\n return {\n btcBias: bias,\n btcScore: score,\n shouldSuppress,\n suppressionFactor,\n reason,\n };\n }\n\n private shouldSkipCorrelation(tokenId: string): boolean {\n // Skip BTC itself\n if (tokenId === \"bitcoin\") return true;\n\n // Skip stablecoins\n const stablecoins = [\n \"tether\", \"usd-coin\", \"binance-usd\", \"dai\", \"frax\",\n \"trueusd\", \"paxos-standard\", \"gemini-dollar\", \"liquity-usd\"\n ];\n\n return stablecoins.includes(tokenId);\n }\n\n private async getBtcStructure(): Promise<BtcStructure> {\n // Check cache first (5-min TTL — HL data is free and fast, no rate-limit concern).\n let cached: CorrelationCache | undefined;\n try {\n cached = await this.loadCache();\n const cacheAge = Date.now() - cached.timestamp;\n const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes — HL data is real-time and free\n if (cacheAge < CACHE_DURATION && cached.btcStructure.price > 0) {\n return cached.btcStructure;\n }\n } catch {\n // Cache miss — continue to fresh analysis.\n }\n\n // PRIMARY: Hyperliquid native data (free, no rate limits, real-time)\n const hlStructure = await this.analyzeBtcFromHL();\n if (hlStructure && hlStructure.price > 0) {\n try { await this.saveCache(hlStructure); } catch { /* non-critical */ }\n return hlStructure;\n }\n\n // FALLBACK: CoinGecko OHLC (rate-limited on free tier, often 429s)\n const cgStructure = await this.analyzeBtcFromCG();\n if (cgStructure.price > 0) {\n try { await this.saveCache(cgStructure); } catch { /* non-critical */ }\n return cgStructure;\n }\n\n // Both failed — prefer stale-but-real cache over neutral fallback.\n if (cached && cached.btcStructure.price > 0) {\n return cached.btcStructure;\n }\n\n return cgStructure; // neutral fallback\n }\n\n /**\n * PRIMARY path: derive BTC structure from Hyperliquid exchange data.\n *\n * Uses mark price, 24h price change, funding rate, OI change, order-book\n * imbalance, and large-trades bias — all fetched in a single free API call\n * with no rate-limit risk. Replaces the CG OHLC path that was permanently\n * 429-ing on the free tier and poisoning the correlation cache.\n *\n * Scoring weights mirror the CG path's 40/30/30 split:\n * - 24h momentum (40%) — replaces EMA alignment\n * - Funding direction (30%) — replaces MACD histogram\n * - Flow bias (30%) — replaces RSI, derived from OB imbalance + large trades\n */\n private async analyzeBtcFromHL(): Promise<BtcStructure | null> {\n try {\n const data = await this.hyperliquid.getHyperliquidData(\"bitcoin\");\n if (!data || data.markPrice <= 0) return null;\n\n let score = 0;\n\n // 24h momentum (40% weight) — replaces EMA alignment\n const pctChange24h = data.prevDayPrice > 0\n ? (data.markPrice - data.prevDayPrice) / data.prevDayPrice\n : 0;\n if (pctChange24h > 0.01) score += 0.4; // >+1% day → bullish\n else if (pctChange24h < -0.01) score -= 0.4; // <-1% day → bearish\n else score += pctChange24h * 40; // linear between ±1%\n\n // Funding direction (30% weight) — replaces MACD\n // Positive funding = longs overleveraged = potentially bearish\n // Negative = shorts overleveraged = potentially bullish\n const fr = data.fundingRate;\n if (fr > 0.0001) score -= 0.3; // strong positive funding → bearish signal\n else if (fr < -0.0001) score += 0.3; // strong negative funding → bullish signal\n else score -= fr * 3000; // linear in between (inverted — high funding is bearish)\n\n // Flow bias (30% weight) — replaces RSI\n // Combine order-book imbalance + large-trades net direction\n const flowBias = (data.orderBookImbalance + data.largeTradesBias) / 2;\n if (flowBias > 0.2) score += 0.3;\n else if (flowBias < -0.2) score -= 0.3;\n else score += flowBias * 1.5; // linear scale\n\n score = Math.max(-1, Math.min(1, score));\n\n // Map to BtcStructure — ema50/ema200/rsi aren't computed from HL data,\n // but score + price + macdDirection are the only fields consumed by\n // assessBtcBias / calculateSuppression downstream. Set the rest to\n // sentinel values that won't confuse debuggers.\n const macdDirection: BtcStructure[\"macdDirection\"] =\n fr > 0.0001 ? \"bearish\" : fr < -0.0001 ? \"bullish\" : \"neutral\";\n\n return {\n price: data.markPrice,\n ema50: 0, // not available from HL — unused in scoring\n ema200: 0, // not available from HL — unused in scoring\n rsi: 50, // not available from HL — unused in scoring\n macdDirection,\n score,\n };\n } catch (err) {\n console.warn(`BTC structure from Hyperliquid failed: ${(err as Error).message}`);\n return null;\n }\n }\n\n /** FALLBACK: CoinGecko OHLC-based BTC analysis. Rate-limited on free tier. */\n private async analyzeBtcFromCG(): Promise<BtcStructure> {\n try {\n // Fetch BTC candles (90 days for EMAs — CoinGecko returns daily candles at days>=90,\n // giving us ~90 bars, enough for the 50-bar floor required by EMA/RSI/MACD history).\n const ohlcData = await this.coingecko.getOHLC(\"bitcoin\", 90);\n\n if (!ohlcData || ohlcData.length < 50) {\n throw new Error(\"Insufficient BTC data\");\n }\n\n const candles: Candle[] = ohlcData.map((c: number[]) => ({\n timestamp: c[0]!,\n open: c[1]!,\n high: c[2]!,\n low: c[3]!,\n close: c[4] ?? c[3]!,\n volume: 0, // Not needed for structure assessment\n }));\n\n const closes = candles.map(c => c.close);\n const currentPrice = closes[closes.length - 1]!;\n\n // Calculate technical indicators\n const ema50Array = calculateEMA(closes, 50);\n const ema200Array = calculateEMA(closes, 200);\n const rsiArray = calculateRSI(candles, 14);\n const macd = calculateMACD(candles, 12, 26, 9);\n\n // Get latest valid values\n const ema50 = this.getLastValidValue(ema50Array);\n const ema200 = this.getLastValidValue(ema200Array);\n const rsi = this.getLastValidValue(rsiArray);\n const macdHistogram = this.getLastValidValue(macd.histogram);\n\n // Determine MACD direction\n let macdDirection: BtcStructure[\"macdDirection\"] = \"neutral\";\n if (!isNaN(macdHistogram)) {\n if (macdHistogram > 0.1) macdDirection = \"bullish\";\n else if (macdHistogram < -0.1) macdDirection = \"bearish\";\n }\n\n // Calculate composite score (-1 to +1)\n let score = 0;\n\n // EMA alignment (40% weight)\n if (!isNaN(ema50) && !isNaN(ema200)) {\n if (currentPrice > ema50 && ema50 > ema200) {\n score += 0.4; // Bullish alignment\n } else if (currentPrice < ema50 && ema50 < ema200) {\n score -= 0.4; // Bearish alignment\n }\n // Neutral if mixed\n }\n\n // RSI (30% weight)\n if (!isNaN(rsi)) {\n if (rsi < 40) {\n score += 0.3; // Oversold = potentially bullish\n } else if (rsi > 60) {\n score -= 0.3; // Overbought = potentially bearish\n }\n // Scale linearly between 40-60\n else {\n const rsiScore = -((rsi - 50) / 10) * 0.15; // Max ±0.15 in neutral range\n score += rsiScore;\n }\n }\n\n // MACD direction (30% weight)\n if (macdDirection === \"bullish\") {\n score += 0.3;\n } else if (macdDirection === \"bearish\") {\n score -= 0.3;\n }\n\n // Clamp score to [-1, 1]\n score = Math.max(-1, Math.min(1, score));\n\n return {\n price: currentPrice,\n ema50: isNaN(ema50) ? 0 : ema50,\n ema200: isNaN(ema200) ? 0 : ema200,\n rsi: isNaN(rsi) ? 50 : rsi,\n macdDirection,\n score,\n };\n\n } catch (error) {\n console.warn(`BTC structure analysis failed: ${(error as Error).message}`);\n\n // Return neutral structure on failure\n return {\n price: 0,\n ema50: 0,\n ema200: 0,\n rsi: 50,\n macdDirection: \"neutral\",\n score: 0,\n };\n }\n }\n\n private assessBtcBias(structure: BtcStructure): { bias: CorrelationCheck[\"btcBias\"], score: number, reason: string } {\n const score = structure.score;\n\n if (score < -0.3) {\n const components = [];\n if (structure.price > 0 && structure.ema50 > 0 && structure.ema200 > 0) {\n if (structure.price < structure.ema50 && structure.ema50 < structure.ema200) {\n components.push(\"below EMAs\");\n }\n }\n if (structure.rsi < 40) components.push(`RSI ${structure.rsi.toFixed(1)}`);\n if (structure.macdDirection === \"bearish\") components.push(\"MACD bearish\");\n\n return {\n bias: \"bearish\",\n score,\n reason: `BTC bearish (${score.toFixed(2)}) — ${components.join(\", \") || \"weak structure\"}`,\n };\n }\n\n if (score > 0.3) {\n const components = [];\n if (structure.price > 0 && structure.ema50 > 0 && structure.ema200 > 0) {\n if (structure.price > structure.ema50 && structure.ema50 > structure.ema200) {\n components.push(\"above EMAs\");\n }\n }\n if (structure.rsi > 60) components.push(`RSI ${structure.rsi.toFixed(1)}`);\n if (structure.macdDirection === \"bullish\") components.push(\"MACD bullish\");\n\n return {\n bias: \"bullish\",\n score,\n reason: `BTC bullish (${score.toFixed(2)}) — ${components.join(\", \") || \"strong structure\"}`,\n };\n }\n\n return {\n bias: \"neutral\",\n score,\n reason: `BTC neutral (${score.toFixed(2)}) — mixed signals`,\n };\n }\n\n private calculateSuppression(bias: CorrelationCheck[\"btcBias\"], score: number): { shouldSuppress: boolean, suppressionFactor: number } {\n if (bias === \"bearish\") {\n // Strong bearish: suppress longs heavily, boost shorts slightly\n const suppressionFactor = 1 + score; // score is negative, so this reduces the factor\n return {\n shouldSuppress: true,\n suppressionFactor: Math.max(0.1, suppressionFactor), // Min 10% of original signal\n };\n }\n\n if (bias === \"bullish\") {\n // Strong bullish: boost longs slightly, suppress shorts\n const boostFactor = 1 + score * 0.2; // Max 20% boost for longs\n return {\n shouldSuppress: false,\n suppressionFactor: Math.min(1.2, boostFactor), // Max 20% boost\n };\n }\n\n // Neutral: no suppression\n return {\n shouldSuppress: false,\n suppressionFactor: 1.0,\n };\n }\n\n private neutralCheck(reason: string): CorrelationCheck {\n return {\n btcBias: \"neutral\",\n btcScore: 0,\n shouldSuppress: false,\n suppressionFactor: 1.0,\n reason,\n };\n }\n\n private getLastValidValue(arr: number[]): number {\n for (let i = arr.length - 1; i >= 0; i--) {\n if (!isNaN(arr[i]!)) return arr[i]!;\n }\n return NaN;\n }\n\n private async loadCache(): Promise<CorrelationCache> {\n const data = await readFile(this.cacheFile, 'utf-8');\n return JSON.parse(data) as CorrelationCache;\n }\n\n private async saveCache(btcStructure: BtcStructure): Promise<void> {\n await mkdir(this.cacheDir, { recursive: true });\n const cache: CorrelationCache = {\n timestamp: Date.now(),\n btcStructure,\n };\n await writeFile(this.cacheFile, JSON.stringify(cache, null, 2), 'utf-8');\n }\n}","/**\n * Smart alert system for regime transitions and signal changes.\n * Detects meaningful state changes and generates prioritized alerts.\n */\n\nimport { readFile, writeFile, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { createHash } from \"node:crypto\";\nimport type { MarketRegime } from \"./regime.js\";\nimport type { TokenAnalysis } from \"./index.js\";\n\nexport type AlertPriority = \"critical\" | \"high\" | \"medium\" | \"low\";\n\nexport interface Alert {\n id: string; // unique hash\n priority: AlertPriority;\n type: string; // e.g. \"regime-transition\", \"signal-divergence\", \"volume-anomaly\"\n title: string; // short headline\n details: string; // full explanation\n tokenId: string;\n timestamp: number;\n acknowledged: boolean;\n sent: boolean; // whether alert was sent via Telegram\n}\n\nexport interface AlertConfig {\n enableRegimeAlerts: boolean;\n enableSignalAlerts: boolean;\n enableVolumeAlerts: boolean;\n minPriority: AlertPriority;\n}\n\ninterface AlertState {\n lastRegime: MarketRegime | null;\n lastRegimeAt: number;\n lastSignals: Record<string, number>;\n alerts: Alert[];\n lastVolumeCheck: Record<string, { twitterVolume: number; hlVolume?: number; timestamp: number }>;\n}\n\nconst DEFAULT_CONFIG: AlertConfig = {\n enableRegimeAlerts: true,\n enableSignalAlerts: true,\n enableVolumeAlerts: true,\n minPriority: \"medium\",\n};\n\nexport class AlertSystem {\n private stateDir: string;\n private stateFile: string;\n private configFile: string;\n private config: AlertConfig;\n\n constructor() {\n this.stateDir = join(homedir(), '.sherwood', 'agent');\n this.stateFile = join(this.stateDir, 'alerts-state.json');\n this.configFile = join(this.stateDir, 'alerts-config.json');\n this.config = DEFAULT_CONFIG;\n }\n\n /**\n * Process analysis results and generate alerts for state changes.\n */\n async processAnalysis(analyses: TokenAnalysis[]): Promise<Alert[]> {\n const state = await this.loadState();\n const newAlerts: Alert[] = [];\n\n // Process regime transitions (using first analysis result with regime data)\n const regimeAnalysis = analyses.find(a => a.regime);\n if (regimeAnalysis?.regime && this.config.enableRegimeAlerts) {\n const regimeAlerts = this.checkRegimeTransition(state, regimeAnalysis.regime.regime);\n newAlerts.push(...regimeAlerts);\n\n state.lastRegime = regimeAnalysis.regime.regime;\n state.lastRegimeAt = Date.now();\n }\n\n // Process signal divergences for each token\n if (this.config.enableSignalAlerts) {\n for (const analysis of analyses) {\n const signalAlerts = this.checkSignalDivergence(state, analysis);\n newAlerts.push(...signalAlerts);\n\n // Update last signal for this token\n state.lastSignals[analysis.token] = analysis.decision.score;\n }\n }\n\n // Process volume anomalies if data is available\n if (this.config.enableVolumeAlerts) {\n for (const analysis of analyses) {\n const volumeAlerts = await this.checkVolumeAnomalies(state, analysis);\n newAlerts.push(...volumeAlerts);\n }\n }\n\n // Add new alerts to state and deduplicate\n for (const alert of newAlerts) {\n if (!this.isAlertDuplicate(state, alert)) {\n state.alerts.push(alert);\n }\n }\n\n // Prune old alerts (keep last 50)\n if (state.alerts.length > 50) {\n state.alerts = state.alerts.slice(-50);\n }\n\n await this.saveState(state);\n return newAlerts.filter(alert => this.shouldShowAlert(alert));\n }\n\n /**\n * Get recent alerts, optionally filtered by priority.\n */\n async getRecentAlerts(maxAge?: number, minPriority?: AlertPriority): Promise<Alert[]> {\n const state = await this.loadState();\n const now = Date.now();\n const ageLimit = maxAge ?? (24 * 60 * 60 * 1000); // Default: 24 hours\n\n return state.alerts\n .filter(alert => (now - alert.timestamp) <= ageLimit)\n .filter(alert => !minPriority || this.priorityValue(alert.priority) >= this.priorityValue(minPriority))\n .sort((a, b) => b.timestamp - a.timestamp);\n }\n\n /**\n * Clear all alerts by marking them as acknowledged.\n */\n async clearAlerts(): Promise<void> {\n const state = await this.loadState();\n for (const alert of state.alerts) {\n alert.acknowledged = true;\n }\n await this.saveState(state);\n }\n\n /**\n * Get unsent CRITICAL/HIGH alerts from last 30min and mark them as sent.\n */\n async getUrgentAlerts(): Promise<Alert[]> {\n const state = await this.loadState();\n const now = Date.now();\n const thirtyMinutes = 30 * 60 * 1000;\n\n // Find unsent critical/high alerts from last 30min\n const urgentAlerts = state.alerts.filter(alert =>\n !alert.sent &&\n !alert.acknowledged &&\n (alert.priority === \"critical\" || alert.priority === \"high\") &&\n (now - alert.timestamp) <= thirtyMinutes\n );\n\n if (urgentAlerts.length === 0) {\n return [];\n }\n\n // Mark alerts as sent\n for (const alert of urgentAlerts) {\n alert.sent = true;\n }\n\n await this.saveState(state);\n return urgentAlerts.sort((a, b) => b.timestamp - a.timestamp);\n }\n\n /**\n * Check for regime transitions and generate alerts.\n */\n private checkRegimeTransition(state: AlertState, currentRegime: MarketRegime): Alert[] {\n const alerts: Alert[] = [];\n\n if (!state.lastRegime || state.lastRegime === currentRegime) {\n return alerts; // No transition\n }\n\n const transition = `${state.lastRegime} → ${currentRegime}`;\n\n // Define transition priorities and messages\n const transitionMap: Record<string, { priority: AlertPriority; title: string; details: string }> = {\n \"ranging → trending-up\": {\n priority: \"high\",\n title: \"Market structure shifted bullish\",\n details: \"Market regime changed from ranging to trending-up. Breakout strategies now activating. Consider increasing position sizes for momentum plays.\"\n },\n \"ranging → trending-down\": {\n priority: \"high\",\n title: \"Market structure shifted bearish\",\n details: \"Market regime changed from ranging to trending-down. Risk-off mode engaged. Consider reducing exposure and defensive positioning.\"\n },\n \"trending-up → ranging\": {\n priority: \"medium\",\n title: \"Uptrend losing momentum\",\n details: \"Market regime changed from trending-up to ranging. Switching to mean reversion strategies. Momentum strategies reduced effectiveness.\"\n },\n \"trending-up → trending-down\": {\n priority: \"critical\",\n title: \"Trend reversal — bull to bear transition\",\n details: \"Major regime shift from trending-up to trending-down. Consider significant risk reduction and potential short positions.\"\n },\n \"trending-down → trending-up\": {\n priority: \"critical\",\n title: \"Trend reversal — bear to bull transition\",\n details: \"Major regime shift from trending-down to trending-up. Consider increasing long exposure and reducing hedges.\"\n },\n \"trending-up → high-volatility\": {\n priority: \"high\",\n title: \"Volatility spike during uptrend\",\n details: \"Market regime changed to high-volatility from trending-up. Reducing position sizes due to increased uncertainty.\"\n },\n \"trending-down → high-volatility\": {\n priority: \"high\",\n title: \"Volatility spike during downtrend\",\n details: \"Market regime changed to high-volatility from trending-down. Extreme caution advised - reducing position sizes.\"\n },\n \"ranging → high-volatility\": {\n priority: \"high\",\n title: \"Volatility breakout from ranging\",\n details: \"Market regime changed to high-volatility from ranging. Preparing for directional moves but reducing sizes due to uncertainty.\"\n },\n \"high-volatility → trending-up\": {\n priority: \"medium\",\n title: \"Volatility normalizing — bullish resolution\",\n details: \"Market regime stabilized from high-volatility to trending-up. Resuming normal position sizing with bullish bias.\"\n },\n \"high-volatility → trending-down\": {\n priority: \"medium\",\n title: \"Volatility normalizing — bearish resolution\",\n details: \"Market regime stabilized from high-volatility to trending-down. Resuming normal position sizing with bearish bias.\"\n },\n \"high-volatility → ranging\": {\n priority: \"medium\",\n title: \"Volatility normalizing — neutral resolution\",\n details: \"Market regime stabilized from high-volatility to ranging. Resuming normal position sizing, mean reversion strategies activated.\"\n }\n };\n\n const transitionData = transitionMap[transition];\n if (transitionData) {\n alerts.push(this.createAlert({\n type: \"regime-transition\",\n priority: transitionData.priority,\n title: transitionData.title,\n details: transitionData.details,\n tokenId: \"bitcoin\", // Regime is based on BTC\n }));\n } else {\n // Generic transition alert for unmapped combinations\n alerts.push(this.createAlert({\n type: \"regime-transition\",\n priority: \"medium\",\n title: `Market regime changed: ${transition}`,\n details: `Market regime transitioned from ${state.lastRegime} to ${currentRegime}. Adjusting strategy weights accordingly.`,\n tokenId: \"bitcoin\",\n }));\n }\n\n return alerts;\n }\n\n /**\n * Check for significant signal changes.\n */\n private checkSignalDivergence(state: AlertState, analysis: TokenAnalysis): Alert[] {\n const alerts: Alert[] = [];\n const currentScore = analysis.decision.score;\n const lastScore = state.lastSignals[analysis.token];\n\n if (lastScore === undefined) {\n return alerts; // First time seeing this token\n }\n\n const scoreChange = currentScore - lastScore;\n const absChange = Math.abs(scoreChange);\n\n // Signal flip (positive to negative or vice versa)\n if ((lastScore > 0 && currentScore < 0) || (lastScore < 0 && currentScore > 0)) {\n alerts.push(this.createAlert({\n type: \"signal-divergence\",\n priority: \"medium\",\n title: `${analysis.token.toUpperCase()} signal flipped ${lastScore > 0 ? 'negative' : 'positive'}`,\n details: `Signal changed from ${lastScore > 0 ? '+' : ''}${lastScore.toFixed(2)} to ${currentScore > 0 ? '+' : ''}${currentScore.toFixed(2)}. Consider reviewing position.`,\n tokenId: analysis.token,\n }));\n }\n // Rapid signal shift (>0.5 change in one scan)\n else if (absChange > 0.5) {\n alerts.push(this.createAlert({\n type: \"signal-divergence\",\n priority: \"high\",\n title: `Rapid signal shift for ${analysis.token.toUpperCase()}`,\n details: `Signal changed by ${scoreChange > 0 ? '+' : ''}${scoreChange.toFixed(2)} in one scan (${lastScore.toFixed(2)} → ${currentScore.toFixed(2)}). Significant momentum detected.`,\n tokenId: analysis.token,\n }));\n }\n\n return alerts;\n }\n\n /**\n * Check for volume anomalies across Twitter and Hyperliquid.\n */\n private async checkVolumeAnomalies(state: AlertState, analysis: TokenAnalysis): Promise<Alert[]> {\n const alerts: Alert[] = [];\n\n // This is a placeholder for volume anomaly detection\n // In a real implementation, you would access Twitter and Hyperliquid volume data\n // from the analysis context and compare against historical averages\n\n const lastVolumeData = state.lastVolumeCheck[analysis.token];\n const now = Date.now();\n\n // Example logic - you would replace this with actual volume data from analysis\n // if (analysis.data.twitterData?.volumeSpike && analysis.data.hyperliquidData?.volumeRatio) {\n // const twitterSpike = analysis.data.twitterData.volumeSpike;\n // const hlVolumeRatio = analysis.data.hyperliquidData.volumeRatio;\n //\n // if (twitterSpike > 5 && hlVolumeRatio > 2) {\n // alerts.push(this.createAlert({\n // type: \"volume-anomaly\",\n // priority: \"high\",\n // title: `Coordinated volume surge for ${analysis.token.toUpperCase()}`,\n // details: `Twitter volume ${twitterSpike.toFixed(1)}x spike with ${hlVolumeRatio.toFixed(1)}x Hyperliquid volume. Monitor for breakout.`,\n // tokenId: analysis.token,\n // }));\n // }\n //\n // // Update volume tracking\n // state.lastVolumeCheck[analysis.token] = {\n // twitterVolume: twitterSpike,\n // hlVolume: hlVolumeRatio,\n // timestamp: now,\n // };\n // }\n\n return alerts;\n }\n\n /**\n * Create an alert with unique ID and timestamp.\n */\n private createAlert(params: Omit<Alert, 'id' | 'timestamp' | 'acknowledged' | 'sent'>): Alert {\n const content = `${params.type}-${params.tokenId}-${params.title}`;\n const id = createHash('sha256').update(content).digest('hex').substring(0, 12);\n\n return {\n id,\n timestamp: Date.now(),\n acknowledged: false,\n sent: false,\n ...params,\n };\n }\n\n /**\n * Check if alert is duplicate within deduplication window.\n */\n private isAlertDuplicate(state: AlertState, newAlert: Alert): boolean {\n const DEDUP_WINDOW = 60 * 60 * 1000; // 1 hour\n const now = Date.now();\n\n return state.alerts.some(existing =>\n existing.type === newAlert.type &&\n existing.tokenId === newAlert.tokenId &&\n existing.title === newAlert.title &&\n (now - existing.timestamp) < DEDUP_WINDOW &&\n !existing.acknowledged\n );\n }\n\n /**\n * Determine if alert should be shown based on config.\n */\n private shouldShowAlert(alert: Alert): boolean {\n return this.priorityValue(alert.priority) >= this.priorityValue(this.config.minPriority);\n }\n\n /**\n * Convert priority to numeric value for comparison.\n */\n private priorityValue(priority: AlertPriority): number {\n const values = { low: 1, medium: 2, high: 3, critical: 4 };\n return values[priority];\n }\n\n /**\n * Load alert state from disk.\n */\n private async loadState(): Promise<AlertState> {\n try {\n const data = await readFile(this.stateFile, 'utf-8');\n return JSON.parse(data) as AlertState;\n } catch {\n return {\n lastRegime: null,\n lastRegimeAt: 0,\n lastSignals: {},\n alerts: [],\n lastVolumeCheck: {},\n };\n }\n }\n\n /**\n * Save alert state to disk.\n */\n private async saveState(state: AlertState): Promise<void> {\n try {\n await mkdir(this.stateDir, { recursive: true });\n await writeFile(this.stateFile, JSON.stringify(state, null, 2), 'utf-8');\n } catch (err) {\n console.error(`Failed to save alert state: ${(err as Error).message}`);\n }\n }\n\n /**\n * Format alerts for CLI display.\n */\n formatAlerts(alerts: Alert[], useMarkdown: boolean = false): string {\n if (alerts.length === 0) {\n return useMarkdown ? \"*No active alerts*\" : \" No active alerts\";\n }\n\n const lines: string[] = [];\n\n if (useMarkdown) {\n lines.push(\"## ALERTS\");\n } else {\n lines.push(\"ALERTS\");\n }\n\n for (const alert of alerts.slice(0, 10)) { // Show max 10\n const ageStr = this.formatAge(Date.now() - alert.timestamp);\n const icon = this.getPriorityIcon(alert.priority);\n\n if (useMarkdown) {\n lines.push(` ${icon} **${alert.priority.toUpperCase()}**: ${alert.title} (${ageStr})`);\n } else {\n lines.push(` ${icon} ${alert.priority.toUpperCase()}: ${alert.title} (${ageStr})`);\n }\n }\n\n return lines.join(\"\\n\");\n }\n\n /**\n * Get emoji icon for alert priority.\n */\n private getPriorityIcon(priority: AlertPriority): string {\n switch (priority) {\n case \"critical\": return \"🔴\";\n case \"high\": return \"🟡\";\n case \"medium\": return \"🔵\";\n case \"low\": return \"⚫\";\n }\n }\n\n /**\n * Format time difference for display.\n */\n private formatAge(ageMs: number): string {\n const minutes = Math.floor(ageMs / (60 * 1000));\n const hours = Math.floor(minutes / 60);\n const days = Math.floor(hours / 24);\n\n if (days > 0) return `${days}d ago`;\n if (hours > 0) return `${hours}h ago`;\n if (minutes > 0) return `${minutes}m ago`;\n return \"just now\";\n }\n}","/**\n * Dynamic token selection from Hyperliquid market data.\n * Selects the most interesting tokens to analyze based on volume, funding rates, and other criteria.\n */\n\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { safeNumber } from '../providers/data/hyperliquid.js';\n\nconst HYPERLIQUID_BASE = 'https://api.hyperliquid.xyz/info';\n\nexport interface TokenSelection {\n tokens: string[]; // CoinGecko IDs for the agent to analyze\n reason: Record<string, string>; // why each token was selected\n totalMarketsScanned: number;\n timestamp: number;\n}\n\nexport interface SelectionCriteria {\n minVolume24h: number; // minimum 24h volume in USD (default: $5M)\n topByVolume: number; // take top N by volume (default: 10)\n fundingExtremes: number; // take N tokens with most extreme funding (default: 5)\n minFundingRate: number; // absolute funding rate threshold (default: 0.03%)\n}\n\ninterface MetaAndAssetCtxs {\n universe: Array<{\n name: string;\n szDecimals: number;\n }>;\n}\n\ninterface AssetCtx {\n funding: string;\n openInterest: string;\n dayNtlVlm: string;\n markPx: string;\n oraclePx: string;\n prevDayPx: string;\n}\n\ninterface MarketInfo {\n name: string;\n volume24h: number;\n funding: number;\n openInterest: number;\n markPrice: number;\n coingeckoId?: string;\n}\n\n// Comprehensive mapping from Hyperliquid coin names to CoinGecko IDs\nconst HYPERLIQUID_TO_COINGECKO: Record<string, string> = {\n BTC: 'bitcoin',\n ETH: 'ethereum',\n SOL: 'solana',\n HYPE: 'hyperliquid', // May not exist on CG, but try\n XRP: 'ripple',\n DOGE: 'dogecoin',\n AVAX: 'avalanche-2',\n LINK: 'chainlink',\n SUI: 'sui',\n NEAR: 'near',\n AAVE: 'aave',\n ARB: 'arbitrum',\n TAO: 'bittensor',\n TRUMP: 'official-trump',\n ZEC: 'zcash',\n BNB: 'binancecoin',\n OP: 'optimism',\n DOT: 'polkadot',\n ADA: 'cardano',\n ATOM: 'cosmos',\n UNI: 'uniswap',\n MKR: 'maker',\n INJ: 'injective-protocol',\n PENDLE: 'pendle',\n PEPE: 'pepe',\n ENA: 'ethena',\n TIA: 'celestia',\n SEI: 'sei-network',\n APT: 'aptos',\n FIL: 'filecoin',\n RENDER: 'render-token',\n JUP: 'jupiter-exchange-solana',\n STX: 'blockstack',\n WLD: 'worldcoin-wld',\n FET: 'fetch-ai',\n ONDO: 'ondo-finance',\n LDO: 'lido-dao',\n CRV: 'curve-dao-token',\n BLUR: 'blur',\n EIGEN: 'eigenlayer',\n POL: 'polygon-ecosystem-token',\n FARTCOIN: 'fartcoin',\n LTC: 'litecoin',\n MATIC: 'polygon-ecosystem-token', // POL is the new MATIC\n TNSR: 'tensor',\n GRASS: 'grass',\n MOVE: 'move-network',\n GOAT: 'goatseus-maximus',\n PNUT: 'peanut-the-squirrel',\n POPCAT: 'popcat',\n NEIRO: 'neiro',\n WIF: 'dogwifcoin',\n BONK: 'bonk',\n FLOKI: 'floki',\n SHIB: 'shiba-inu',\n MEW: 'cat-in-a-dogs-world',\n MEME: 'memecoin',\n AI16Z: 'ai16z',\n VIRTUAL: 'virtual-protocol',\n AIXBT: 'aixbt-by-virtuals',\n ZEREBRO: 'zerebro',\n arc: 'arc',\n HYPURR: 'hypurr',\n ELIZA: 'eliza',\n GRIFFAIN: 'griffain',\n LUNA: 'ai-analysis-token',\n DEEP: 'deep-worm',\n POLY: 'polyhedra-network',\n VANA: 'vana',\n VELODROME: 'velodrome-finance',\n ACX: 'across-protocol',\n COOKIE: 'cookie-dao',\n USUAL: 'usual',\n PENGU: 'pudgy-penguins',\n ETHFi: 'ether-fi',\n ETHFI: 'ether-fi', // Alternative spelling\n RSR: 'reserve-rights',\n CKB: 'nervos-network',\n RUNE: 'thorchain',\n MANA: 'decentraland',\n SAND: 'the-sandbox',\n ENS: 'ethereum-name-service',\n LRC: 'loopring',\n IMX: 'immutable-x',\n GALA: 'gala',\n CHZ: 'chiliz',\n BAT: 'basic-attention-token',\n ZRX: '0x',\n COMP: 'compound-governance-token',\n SUSHI: 'sushi',\n SNX: 'havven',\n DYDX: 'dydx',\n GMX: 'gmx',\n GRT: 'the-graph',\n FLOW: 'flow',\n KAVA: 'kava',\n ROSE: 'oasis-network',\n ONE: 'harmony',\n ALGO: 'algorand',\n XTZ: 'tezos',\n EGLD: 'elrond-erd-2',\n FTM: 'fantom',\n CAKE: 'pancakeswap-token',\n CVX: 'convex-finance',\n SPELL: 'spell-token',\n ALPHA: 'alpha-finance',\n BADGER: 'badger-dao',\n STKETH: 'lido-staked-ether',\n RETH: 'rocket-pool-eth',\n WSTETH: 'wrapped-steth',\n STETH: 'staked-ether',\n USDC: 'usd-coin',\n USDT: 'tether',\n DAI: 'dai',\n FRAX: 'frax',\n USDD: 'usdd',\n TUSD: 'trueusd',\n LUSD: 'liquity-usd',\n MIM: 'magic-internet-money',\n GUSD: 'gemini-dollar',\n PYUSD: 'paypal-usd',\n USDP: 'paxos-standard',\n};\n\nexport class DynamicTokenSelector {\n private cacheDir: string;\n private cacheTTL = 30 * 60 * 1000; // 30 minutes\n\n constructor() {\n this.cacheDir = join(homedir(), '.sherwood', 'agent', 'cache');\n }\n\n /** Get dynamic token selection based on Hyperliquid market data. */\n async selectTokens(criteria?: Partial<SelectionCriteria>): Promise<TokenSelection> {\n const config: SelectionCriteria = {\n minVolume24h: 5_000_000, // $5M minimum\n topByVolume: 18, // bumped 10 → 18 for more shots on goal per cycle\n fundingExtremes: 5,\n minFundingRate: 0.0003, // 0.03%\n ...criteria\n };\n\n // Check cache first\n const cached = await this.readCache();\n if (cached) {\n console.log(`Using cached token selection (${cached.tokens.length} tokens)`);\n return cached;\n }\n\n try {\n // Fetch all Hyperliquid markets\n const marketData = await this.fetchHyperliquidMarkets();\n if (!marketData) {\n console.warn('Failed to fetch Hyperliquid data, using default tokens');\n return this.getDefaultSelection();\n }\n\n // Apply selection algorithm\n const selection = this.applySelectionCriteria(marketData, config);\n\n // Cache the results\n await this.writeCache(selection);\n\n return selection;\n } catch (error) {\n console.error(`Token selection failed: ${(error as Error).message}`);\n return this.getDefaultSelection();\n }\n }\n\n /** Fetch all Hyperliquid market data. */\n private async fetchHyperliquidMarkets(): Promise<MarketInfo[] | null> {\n try {\n const response = await fetch(HYPERLIQUID_BASE, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ type: 'metaAndAssetCtxs' }),\n });\n\n if (!response.ok) return null;\n\n const [meta, assetCtxs] = await response.json() as [MetaAndAssetCtxs, AssetCtx[]];\n\n const markets: MarketInfo[] = [];\n\n for (let i = 0; i < meta.universe.length && i < assetCtxs.length; i++) {\n const coin = meta.universe[i]!;\n const ctx = assetCtxs[i]!;\n\n const name = coin.name;\n // Reject rows with any non-finite field — a single NaN would otherwise\n // corrupt selection criteria downstream (volume filters, funding sort).\n const volume24h = safeNumber(ctx.dayNtlVlm);\n const funding = safeNumber(ctx.funding);\n const openInterest = safeNumber(ctx.openInterest);\n const markPrice = safeNumber(ctx.markPx);\n if (volume24h === null || funding === null || openInterest === null || markPrice === null) {\n continue;\n }\n const coingeckoId = HYPERLIQUID_TO_COINGECKO[name];\n\n markets.push({\n name,\n volume24h,\n funding,\n openInterest,\n markPrice,\n coingeckoId,\n });\n }\n\n return markets;\n } catch (error) {\n console.error(`Failed to fetch Hyperliquid markets: ${(error as Error).message}`);\n return null;\n }\n }\n\n /** Apply selection criteria to market data. */\n private applySelectionCriteria(markets: MarketInfo[], criteria: SelectionCriteria): TokenSelection {\n const selectedTokens = new Set<string>();\n const reasons: Record<string, string> = {};\n\n // Filter markets that have CoinGecko mappings and minimum volume\n const validMarkets = markets.filter(m =>\n m.coingeckoId &&\n m.volume24h >= 1_000_000 // At least $1M for any consideration\n );\n\n // 1. Always include BTC and ETH (macro indicators)\n const btc = validMarkets.find(m => m.name === 'BTC');\n const eth = validMarkets.find(m => m.name === 'ETH');\n\n if (btc?.coingeckoId) {\n selectedTokens.add(btc.coingeckoId);\n reasons[btc.coingeckoId] = 'Always included: macro indicator';\n }\n\n if (eth?.coingeckoId) {\n selectedTokens.add(eth.coingeckoId);\n reasons[eth.coingeckoId] = 'Always included: macro indicator';\n }\n\n // 2. Top N by volume (meeting minimum threshold)\n const topByVolume = validMarkets\n .filter(m => m.volume24h >= criteria.minVolume24h)\n .sort((a, b) => b.volume24h - a.volume24h)\n .slice(0, criteria.topByVolume);\n\n for (const market of topByVolume) {\n if (market.coingeckoId) {\n selectedTokens.add(market.coingeckoId);\n if (!reasons[market.coingeckoId]) {\n const volumeStr = market.volume24h >= 1_000_000_000\n ? `$${(market.volume24h / 1_000_000_000).toFixed(1)}B`\n : `$${(market.volume24h / 1_000_000).toFixed(0)}M`;\n reasons[market.coingeckoId] = `Top volume: ${volumeStr} 24h`;\n }\n }\n }\n\n // 3. Extreme funding rates\n const extremeFunding = validMarkets\n .filter(m => Math.abs(m.funding) >= criteria.minFundingRate && m.volume24h >= 1_000_000)\n .sort((a, b) => Math.abs(b.funding) - Math.abs(a.funding))\n .slice(0, criteria.fundingExtremes);\n\n for (const market of extremeFunding) {\n if (market.coingeckoId) {\n selectedTokens.add(market.coingeckoId);\n if (!reasons[market.coingeckoId] || reasons[market.coingeckoId]?.startsWith('Always')) {\n const fundingPercent = (market.funding * 100).toFixed(3);\n const direction = market.funding > 0 ? 'longs paying' : 'shorts paying';\n reasons[market.coingeckoId] = `Extreme funding: ${fundingPercent}% (${direction})`;\n }\n }\n }\n\n // Cap at 25 tokens — bumped from 20 to fit the wider topByVolume sweep\n // while staying under CoinGecko's free-tier rate limit (~30 req/min).\n const tokens = Array.from(selectedTokens).slice(0, 25);\n\n return {\n tokens,\n reason: Object.fromEntries(\n tokens.map(token => [token, reasons[token] || 'Selected by criteria'])\n ),\n totalMarketsScanned: markets.length,\n timestamp: Date.now(),\n };\n }\n\n /** Get default token selection when Hyperliquid is unavailable. */\n private getDefaultSelection(): TokenSelection {\n const defaultTokens = ['bitcoin', 'ethereum', 'solana', 'arbitrum', 'chainlink', 'aave', 'uniswap'];\n const reasons = Object.fromEntries(\n defaultTokens.map(token => [token, 'Fallback: Hyperliquid unavailable'])\n );\n\n return {\n tokens: defaultTokens,\n reason: reasons,\n totalMarketsScanned: 0,\n timestamp: Date.now(),\n };\n }\n\n /** Read cached token selection. */\n private async readCache(): Promise<TokenSelection | null> {\n try {\n const cacheFile = join(this.cacheDir, 'token-selection.json');\n const raw = await readFile(cacheFile, 'utf-8');\n const cached = JSON.parse(raw) as TokenSelection;\n\n if (Date.now() - cached.timestamp < this.cacheTTL) {\n return cached;\n }\n } catch {\n // No cache or invalid cache\n }\n return null;\n }\n\n /** Write token selection to cache. */\n private async writeCache(selection: TokenSelection): Promise<void> {\n try {\n await mkdir(this.cacheDir, { recursive: true });\n const cacheFile = join(this.cacheDir, 'token-selection.json');\n await writeFile(cacheFile, JSON.stringify(selection, null, 2), 'utf-8');\n } catch {\n // Cache write failure is non-fatal\n }\n }\n\n /** Format token selection summary for display. */\n formatSelectionSummary(selection: TokenSelection): string {\n const lines: string[] = [];\n\n lines.push(`Dynamic Token Selection (${selection.tokens.length} tokens from ${selection.totalMarketsScanned} HL markets)`);\n lines.push('──────────────────────────────────────────');\n\n // Group tokens by reason type\n const topVolume: string[] = [];\n const fundingPlays: string[] = [];\n const always: string[] = [];\n const other: string[] = [];\n\n for (const token of selection.tokens) {\n const reason = selection.reason[token] || '';\n if (reason.includes('Top volume')) {\n topVolume.push(token.toUpperCase());\n } else if (reason.includes('Extreme funding')) {\n const match = reason.match(/(-?\\d+\\.\\d+)%/);\n const rate = match ? ` (${match[1]}%)` : '';\n fundingPlays.push(`${token.toUpperCase()}${rate}`);\n } else if (reason.includes('Always included')) {\n always.push(token.toUpperCase());\n } else {\n other.push(token.toUpperCase());\n }\n }\n\n if (topVolume.length > 0) {\n lines.push(`Top volume: ${topVolume.join(', ')}`);\n }\n if (fundingPlays.length > 0) {\n lines.push(`Funding plays: ${fundingPlays.join(', ')}`);\n }\n if (always.length > 0) {\n lines.push(`Always: ${always.join(', ')}`);\n }\n if (other.length > 0) {\n lines.push(`Other: ${other.join(', ')}`);\n }\n\n return lines.join('\\n');\n }\n}","/**\n * Autonomous agent loop — runs analysis + execution cycles on a timer.\n */\n\nimport chalk from 'chalk';\nimport { mkdir, appendFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { TradingAgent } from './index.js';\nimport type { AgentConfig } from './index.js';\nimport { TradeExecutor } from './executor.js';\nimport type { ExecutionConfig } from './executor.js';\nimport { PortfolioTracker, resetPnlCounters } from './portfolio.js';\nimport { RiskManager, DEFAULT_RISK_CONFIG } from './risk.js';\nimport type { RiskConfig, PortfolioState } from './risk.js';\nimport { CoinGeckoProvider } from '../providers/data/coingecko.js';\nimport { Reporter } from './reporter.js';\nimport { DynamicTokenSelector } from './token-selector.js';\nimport { PriceValidator } from './price-validator.js';\n\nexport interface LoopConfig {\n agent: AgentConfig;\n execution: ExecutionConfig;\n riskConfig?: Partial<RiskConfig>;\n reportToTelegram?: boolean;\n logPath?: string;\n autoDynamicSelection?: boolean;\n}\n\nexport interface CycleResult {\n cycleNumber: number;\n timestamp: number;\n duration: number;\n tokensAnalyzed: number;\n signals: Array<{ token: string; score: number; action: string; regime?: string }>;\n tradesExecuted: number;\n exitsProcessed: number;\n portfolioValue: number;\n /** Realized PnL since UTC day start (closed trades only). */\n dailyRealizedPnl: number;\n /** Sum of mark-to-market pnlUsd across all open positions at cycle end. */\n unrealizedPnl: number;\n /** @deprecated Alias for dailyRealizedPnl — retained for downstream consumers of cycles.jsonl. */\n dailyPnl: number;\n /** Cumulative PnL in USD since the portfolio was initialized\n * (`portfolioValue - portfolio.initialValue`). Includes both realized\n * closes and open-position mark-to-market. */\n totalPnlUsd: number;\n /** Cumulative PnL as a fraction of initial value (e.g. +0.0162 = +1.62%). */\n totalPnlPct: number;\n errors: string[];\n}\n\nexport class AgentLoop {\n private running = false;\n private cycleCount = 0;\n private config: LoopConfig;\n private agent: TradingAgent;\n private executor: TradeExecutor;\n private portfolio: PortfolioTracker;\n private riskManager: RiskManager;\n private coingecko: CoinGeckoProvider;\n private reporter: Reporter;\n /** Rejects bad-price ticks (floor + 20% delta cap). Persisted to disk so\n * anchors survive across cron invocations. */\n private priceValidator: PriceValidator;\n /** Last portfolio state loaded by runCycle(). Exposed to the judge via\n * agent.setPortfolioStateGetter() so portfolio-veto branches can fire. */\n private lastPortfolioState: PortfolioState | undefined;\n\n constructor(config: LoopConfig) {\n this.config = config;\n this.agent = new TradingAgent(config.agent);\n this.portfolio = new PortfolioTracker();\n this.riskManager = new RiskManager(config.riskConfig);\n this.executor = new TradeExecutor(config.execution, this.riskManager, this.portfolio);\n this.coingecko = new CoinGeckoProvider();\n this.reporter = new Reporter();\n this.priceValidator = new PriceValidator();\n // Wire real portfolio state into the judge (replaces hardcoded zeros).\n this.agent.setPortfolioStateGetter(() => this.lastPortfolioState);\n }\n\n /** Start the autonomous loop */\n async start(): Promise<void> {\n this.running = true;\n\n // Ensure state directory exists\n const stateDir = join(homedir(), '.sherwood', 'agent');\n await mkdir(stateDir, { recursive: true });\n\n // Initialize portfolio from on-chain vault balance if no persisted state.\n // This replaces the $10k default with the actual vault USDC balance so\n // position sizing and risk management reflect real capital.\n if (this.config.execution.strategyClone && this.config.execution.chain) {\n await this.portfolio.initFromOnChain(\n this.config.execution.strategyClone,\n this.config.execution.chain,\n );\n }\n\n // Startup banner\n const cfg = this.config.agent;\n console.log('');\n console.log(chalk.bold(' ┌──────────────────────────────────────────────┐'));\n console.log(chalk.bold(' │ Sherwood Trading Agent — Loop │'));\n console.log(chalk.bold(' └──────────────────────────────────────────────┘'));\n console.log(` Mode: ${cfg.dryRun ? chalk.yellow('DRY RUN (paper trading)') : chalk.red('LIVE')}`);\n console.log(` Cycle: ${cfg.cycle}`);\n console.log(` Tokens: ${cfg.tokens.join(', ')}`);\n console.log(` Risk: max ${(this.config.riskConfig?.maxSinglePosition ?? DEFAULT_RISK_CONFIG.maxSinglePosition) * 100}% per position`);\n console.log(` Log: ${this.config.logPath ?? 'console only'}`);\n console.log(chalk.dim('\\n Press Ctrl+C to stop.\\n'));\n\n const cycleMs = parseCycleInterval(this.config.agent.cycle);\n\n while (this.running) {\n try {\n await this.runCycle();\n } catch (err) {\n console.error(chalk.red(` Cycle failed: ${(err as Error).message}`));\n }\n\n // Single-cycle mode: --cycle 1 (bare \"1\") means run exactly one cycle\n // then exit cleanly so the cron skill can proceed to XMTP send.\n // parseCycleInterval returns 0 for this case.\n if (cycleMs === 0 || !this.running) break;\n\n console.log(chalk.dim(` Next cycle in ${this.config.agent.cycle}. Sleeping...`));\n await this.sleepInterruptible(cycleMs);\n }\n\n console.log(chalk.bold('\\n Agent loop stopped.\\n'));\n\n // Force-exit after a short grace period. XMTP client, CoinGecko HTTP\n // agents, and other async handles can keep the Node event loop alive\n // indefinitely. In cron mode (single cycle) this manifests as the\n // process hanging after \"Shutting down gracefully...\" until hermes\n // kills it at script_timeout_seconds. 500ms is enough for any pending\n // file writes (portfolio.json, cycles.jsonl) to flush.\n setTimeout(() => process.exit(0), 500);\n }\n\n /** Run a single analysis + execution cycle */\n async runCycle(): Promise<CycleResult> {\n this.cycleCount++;\n const startTime = Date.now();\n const errors: string[] = [];\n let tradesExecuted = 0;\n let exitsProcessed = 0;\n\n // 1. Load portfolio and check drawdown limits\n const state = await this.portfolio.load();\n\n // Reset PnL counters if time boundaries crossed\n const resetState = resetPnlCounters(state);\n if (resetState.dailyPnl !== state.dailyPnl || resetState.weeklyPnl !== state.weeklyPnl || resetState.monthlyPnl !== state.monthlyPnl) {\n await this.portfolio.save(resetState);\n }\n\n // Publish the refreshed state to the judge (via getter set in the ctor).\n this.lastPortfolioState = resetState;\n this.riskManager.updatePortfolio(resetState);\n const drawdown = this.riskManager.isDrawdownLimitHit();\n if (drawdown.paused) {\n console.log(chalk.red(` ⚠ Trading paused: ${drawdown.message}`));\n const unrealizedPnlAtPause = state.positions.reduce((sum, p) => sum + p.pnlUsd, 0);\n const initValPause = state.initialValue && state.initialValue > 0 ? state.initialValue : 10_000;\n const totalPnlUsdPause = state.totalValue - initValPause;\n const result: CycleResult = {\n cycleNumber: this.cycleCount,\n timestamp: Date.now(),\n duration: Date.now() - startTime,\n tokensAnalyzed: 0,\n signals: [],\n tradesExecuted: 0,\n exitsProcessed: 0,\n portfolioValue: state.totalValue,\n dailyRealizedPnl: state.dailyPnl,\n unrealizedPnl: unrealizedPnlAtPause,\n dailyPnl: state.dailyPnl,\n totalPnlUsd: totalPnlUsdPause,\n totalPnlPct: totalPnlUsdPause / initValPause,\n errors: [drawdown.message],\n };\n await this.logCycle(result);\n return result;\n }\n\n // 2. Update prices for existing positions and process exits\n if (state.positions.length > 0) {\n try {\n const tokenIds = state.positions.map(p => p.tokenId);\n const priceData = await this.coingecko.getPrice(tokenIds, ['usd']);\n const currentPrices: Record<string, number> = {};\n\n for (const id of tokenIds) {\n const price = priceData?.[id]?.usd;\n if (typeof price !== 'number') continue;\n const check = this.priceValidator.check(id, price);\n if (!check.ok) {\n // Skip this token for the cycle — no MTM, no exit check, no trades.\n console.warn(\n chalk.yellow(\n ` ⚠ Bad price tick rejected: ${id} → $${price} (${check.reason})`,\n ),\n );\n errors.push(`Bad price rejected for ${id}: ${check.reason}`);\n continue;\n }\n currentPrices[id] = check.price;\n }\n\n if (Object.keys(currentPrices).length > 0) {\n const priceState = await this.portfolio.updatePrices(currentPrices);\n\n // Ratchet stops (breakeven + profit-lock + percent-trail) before\n // checking exits. This activates trailing-stop logic that was\n // previously dead code (updateStopLosses was defined but never\n // called). Stops only move up — never loosened.\n const tightened = this.riskManager.updateTrailingStops(priceState.positions);\n const anyChanged = tightened.some(\n (p, i) => p.stopLoss !== priceState.positions[i]!.stopLoss,\n );\n if (anyChanged) {\n for (let i = 0; i < tightened.length; i++) {\n const before = priceState.positions[i]!;\n const after = tightened[i]!;\n if (after.stopLoss > before.stopLoss) {\n console.log(\n chalk.dim(\n ` Stop tightened: ${after.symbol} $${before.stopLoss.toFixed(4)} → $${after.stopLoss.toFixed(4)}`,\n ),\n );\n }\n }\n priceState.positions = tightened;\n await this.portfolio.save(priceState);\n }\n\n // Process exits (stop losses, take profits)\n const exits = await this.executor.processExits(currentPrices);\n exitsProcessed = exits.length;\n\n for (const exit of exits) {\n const pnlColor = exit.pnl >= 0 ? chalk.green : chalk.red;\n console.log(\n ` ${exit.reason}: ${exit.position.symbol} @ $${exit.exitPrice.toFixed(4)} ${pnlColor(`PnL: $${exit.pnl.toFixed(2)}`)}`,\n );\n }\n }\n } catch (err) {\n errors.push(`Price update failed: ${(err as Error).message}`);\n }\n }\n\n // 3. Update token list if using dynamic selection\n if (this.config.autoDynamicSelection) {\n try {\n const selector = new DynamicTokenSelector();\n const selection = await selector.selectTokens();\n\n // Update token list without recreating the agent (preserves OI cache, provider state)\n this.config.agent.tokens = selection.tokens;\n this.agent.updateTokens(selection.tokens);\n\n if (this.cycleCount % 6 === 1) { // Show selection summary every 6th cycle (~30min for 5min cycles)\n console.log(chalk.dim(` Updated tokens: ${selection.tokens.length} from ${selection.totalMarketsScanned} HL markets`));\n }\n } catch (err) {\n errors.push(`Dynamic selection failed: ${(err as Error).message}`);\n console.log(chalk.yellow(` Warning: Dynamic selection failed, using existing tokens`));\n }\n }\n\n // 4. Analyze all watchlist tokens\n console.log(chalk.dim(` Analyzing ${this.config.agent.tokens.length} tokens...`));\n const results = await this.agent.analyzeAll();\n\n // 5. Collect signals and execute trades for actionable ones\n const signals: CycleResult['signals'] = [];\n\n for (const result of results) {\n const { action, score } = result.decision;\n const regime = result.regime?.regime;\n signals.push({ token: result.token, score, action, regime });\n\n // Only execute on BUY/SELL signals\n if (action === 'STRONG_BUY' || action === 'BUY' || action === 'SELL' || action === 'STRONG_SELL') {\n try {\n // Get current price — prefer Hyperliquid markPrice (free, real-time, no rate limit),\n // fall back to CoinGecko. The prior CG-only path failed when the circuit breaker was\n // open, blocking execution of valid signals (e.g. SOL SELL at -0.119 failed with\n // \"CoinGecko circuit breaker open\" despite HL markPrice being available in the\n // analysis result).\n let rawPrice: number | undefined = result.data?.price as number | undefined;\n if (typeof rawPrice !== 'number' || rawPrice <= 0) {\n try {\n const priceData = await this.coingecko.getPrice([result.token], ['usd']);\n rawPrice = priceData?.[result.token]?.usd;\n } catch {\n // CG unavailable — rawPrice stays undefined, handled below\n }\n }\n\n if (typeof rawPrice !== 'number' || rawPrice <= 0) {\n errors.push(`No price data for ${result.token} (HL + CG both failed)`);\n continue;\n }\n\n const check = this.priceValidator.check(result.token, rawPrice);\n if (!check.ok) {\n console.warn(\n chalk.yellow(\n ` ⚠ Bad price tick rejected (entry): ${result.token} → $${rawPrice} (${check.reason})`,\n ),\n );\n errors.push(`Bad price rejected for ${result.token}: ${check.reason}`);\n continue;\n }\n\n const currentPrice = check.price;\n const atr = result.data?.technicalSignals?.atr;\n const kronosVol = result.kronosVol4h;\n const execResult = await this.executor.execute(result.decision, result.token, currentPrice, atr, kronosVol);\n if (execResult.success) {\n tradesExecuted++;\n console.log(this.executor.formatExecution(execResult));\n } else if (execResult.error && !execResult.error.includes('does not trigger')) {\n errors.push(`${result.token}: ${execResult.error}`);\n }\n } catch (err) {\n errors.push(`Execution failed for ${result.token}: ${(err as Error).message}`);\n }\n }\n }\n\n // 5. Reload portfolio after trades\n const updatedState = await this.portfolio.load();\n\n // 6. Build cycle result\n const unrealizedPnl = updatedState.positions.reduce((sum, p) => sum + p.pnlUsd, 0);\n const initVal = updatedState.initialValue && updatedState.initialValue > 0 ? updatedState.initialValue : 10_000;\n const totalPnlUsd = updatedState.totalValue - initVal;\n const cycleResult: CycleResult = {\n cycleNumber: this.cycleCount,\n timestamp: Date.now(),\n duration: Date.now() - startTime,\n tokensAnalyzed: results.length,\n signals,\n tradesExecuted,\n exitsProcessed,\n portfolioValue: updatedState.totalValue,\n dailyRealizedPnl: updatedState.dailyPnl,\n unrealizedPnl,\n dailyPnl: updatedState.dailyPnl,\n totalPnlUsd,\n totalPnlPct: totalPnlUsd / initVal,\n errors,\n };\n\n // 7. Log and report\n await this.logCycle(cycleResult);\n this.reportCycle(cycleResult);\n\n return cycleResult;\n }\n\n /** Stop the loop gracefully */\n stop(): void {\n this.running = false;\n }\n\n /** Whether the loop is currently running */\n get isRunning(): boolean {\n return this.running;\n }\n\n /** Log cycle result to a JSONL file */\n private async logCycle(result: CycleResult): Promise<void> {\n const logPath = this.config.logPath ?? join(homedir(), '.sherwood', 'agent', 'cycles.jsonl');\n try {\n await mkdir(join(homedir(), '.sherwood', 'agent'), { recursive: true });\n await appendFile(logPath, JSON.stringify(result) + '\\n', 'utf-8');\n } catch (err) {\n console.error(chalk.dim(` Failed to write cycle log: ${(err as Error).message}`));\n }\n }\n\n /** Display cycle report to console */\n private reportCycle(result: CycleResult): void {\n console.log(this.reporter.formatCycleReport(result));\n }\n\n /** Sleep that can be interrupted by stop() */\n private sleepInterruptible(ms: number): Promise<void> {\n return new Promise<void>((resolve) => {\n let resolved = false;\n const done = () => {\n if (resolved) return;\n resolved = true;\n clearTimeout(timer);\n clearInterval(check);\n resolve();\n };\n const timer = setTimeout(done, ms);\n const check = setInterval(() => {\n if (!this.running) done();\n }, 1000);\n });\n }\n\n}\n\n/** Parse cycle interval string to milliseconds.\n * Accepts: \"15m\", \"1h\", \"4h\", or bare \"1\" (single-cycle mode → returns 0).\n * Bare \"1\" is the cron convention (`sherwood agent start --auto --cycle 1`):\n * run exactly one cycle, then exit so the calling skill can XMTP/report. */\nfunction parseCycleInterval(cycle: string): number {\n // Try with explicit unit first (e.g. \"15m\", \"4h\")\n const match = cycle.match(/^(\\d+)(m|h)$/);\n if (match) {\n const value = parseInt(match[1]!, 10);\n if (match[2] === 'h') return value * 60 * 60 * 1000;\n return value * 60 * 1000; // minutes\n }\n // Bare \"1\" → single-cycle mode (0 = sentinel, loop breaks after first cycle).\n // Any other bare number → treat as minutes for continuous mode.\n const bare = parseInt(cycle, 10);\n if (!isNaN(bare) && bare === 1) return 0; // single cycle\n if (!isNaN(bare) && bare > 1) return bare * 60 * 1000;\n return 4 * 60 * 60 * 1000; // default 4h\n}\n","/**\n * Trade execution module — dry-run paper trading + live execution placeholder.\n */\n\nimport chalk from 'chalk';\nimport type { Address, Hex } from 'viem';\nimport type { TradeDecision } from './scoring.js';\nimport type { Position } from './risk.js';\nimport { RiskManager } from './risk.js';\nimport { PortfolioTracker } from './portfolio.js';\nimport { hlMarketBuy, hlMarketSell, resolveHLCoin, validateHLEnv } from '../lib/hyperliquid-executor.js';\n\nexport interface ExecutionConfig {\n dryRun: boolean;\n mevProtection: boolean;\n maxGasPrice?: bigint;\n chain: string;\n /** Execution mode: 'dry-run' (default) or 'hyperliquid-perp' (live on-chain) */\n mode?: 'dry-run' | 'hyperliquid-perp';\n /** Strategy clone address on HyperEVM (required for hyperliquid-perp mode) */\n strategyClone?: Address;\n /** Proposer private key (read from SHERWOOD_PROPOSER_KEY env) */\n proposerPrivateKey?: Hex;\n /** HyperCore perp asset index (default 3 = ETH) */\n assetIndex?: number;\n}\n\nexport interface OrderParams {\n tokenId: string;\n side: 'buy' | 'sell';\n amountUsd: number;\n maxSlippage: number;\n stopLoss: number;\n takeProfit: number;\n}\n\n/** Directional trade leverage (notional multiplier on order quantity).\n *\n * Cash model: PortfolioTracker debits the same `MARGIN_FRACTION = 0.33`\n * (33%) of notional for both longs and shorts on every open / pyramid add,\n * and credits margin + PnL on close. Live mode (hyperliquid-perp) uses\n * the venue's own margin schedule. See `cli/src/agent/portfolio.ts`.\n *\n * Autoresearch (80 exp, 4 days): 3x→2x→1x each reduced DD dramatically.\n * At 54% WR with avg loss > avg win, leverage amplifies the problem.\n * Revert to 1x until WR > 60% and avg win > avg loss. */\nconst DIRECTIONAL_LEVERAGE = 1;\n\n/** Score-based position sizing multiplier.\n * Nunchi autoresearch (103 experiments): removing strength/volume scaling\n * improved Sharpe by +1.7. Uniform position sizing beats conviction-weighted\n * because the score predicts DIRECTION, not MAGNITUDE. A high-score entry\n * isn't more likely to be a BIG winner — it's just more likely to be RIGHT.\n * Oversizing on high scores amplifies losses on the 40% that still stop out.\n * Kept as a function for easy revert if calibration proves otherwise. */\nfunction convictionMultiplier(_score: number): number {\n return 1.0; // uniform sizing — autoresearch-proven\n}\n\nexport class TradeExecutor {\n private config: ExecutionConfig;\n private riskManager: RiskManager;\n private portfolio: PortfolioTracker;\n private lastAtr?: number;\n\n constructor(config: ExecutionConfig, riskManager: RiskManager, portfolio: PortfolioTracker) {\n this.config = config;\n this.riskManager = riskManager;\n this.portfolio = portfolio;\n }\n\n /** CoinGecko token ID → Hyperliquid perp asset index.\n * Used by multi-asset execution to resolve which HL perp to trade\n * when a signal fires. One strategy clone can trade any of these. */\n private static readonly TOKEN_TO_ASSET_INDEX: Record<string, number> = {\n bitcoin: 0,\n ethereum: 1,\n solana: 2,\n arbitrum: 4,\n dogecoin: 6,\n chainlink: 8,\n aave: 10,\n uniswap: 11,\n ripple: 23,\n polkadot: 28,\n avalanche: 35,\n hyperliquid: 131,\n zcash: 144,\n bittensor: 148,\n \"worldcoin-wld\": 265,\n fartcoin: 343,\n \"fetch-ai\": 373,\n pepe: 166,\n pendle: 249,\n sui: 116,\n near: 71,\n aptos: 83,\n };\n\n /** Resolve a CoinGecko token ID to its HL perp asset index. */\n static resolveAssetIndex(tokenId: string): number | undefined {\n return TradeExecutor.TOKEN_TO_ASSET_INDEX[tokenId];\n }\n\n /** Execute a trade based on a decision.\n * @param predictedVol - Kronos ML-predicted per-candle volatility (fraction).\n * When provided, overrides ATR-based stop distance for more forward-looking risk. */\n async execute(\n decision: TradeDecision,\n tokenId: string,\n currentPrice: number,\n atr?: number,\n predictedVol?: number,\n ): Promise<{\n success: boolean;\n position?: Position;\n error?: string;\n dryRun: boolean;\n }> {\n // Token blacklist — serial losers identified from trade analysis.\n // AAVE: 4 trades, 1W, -$145. FARTCOIN: 2 trades, 0W, -$118.\n const BLACKLISTED_TOKENS = new Set(['aave', 'fartcoin', 'dogecoin']);\n if (BLACKLISTED_TOKENS.has(tokenId)) {\n return {\n success: false,\n error: `Token ${tokenId} is blacklisted (serial loser)`,\n dryRun: this.config.dryRun,\n };\n }\n\n // Handle SELL/STRONG_SELL outside hyperliquid-perp mode.\n // • If an existing LONG exists → close it (signal flip / take-profit-by-signal).\n // • Otherwise (no position OR existing SHORT) → fall through to the\n // standard open/pyramid path below, which opens a paper short or\n // pyramids into the existing short. This was previously a hard error\n // (\"No open position to sell\") — paper shorting was effectively dead\n // code, masking SHORT signal performance in dry-run.\n if ((decision.action === 'SELL' || decision.action === 'STRONG_SELL') && this.config.mode !== 'hyperliquid-perp') {\n const sellState = await this.portfolio.load();\n const existing = sellState.positions.find((p) => p.tokenId === tokenId);\n const existingSide = existing?.side ?? 'long';\n\n if (existing && existingSide === 'long') {\n try {\n const reason = decision.action === 'STRONG_SELL' ? 'Strong sell signal' : 'Sell signal';\n if (this.config.dryRun) {\n console.error(chalk.cyan(`[DRY RUN] Paper trade: SELL ${existing.quantity.toFixed(6)} ${tokenId} @ $${currentPrice.toFixed(4)}`));\n }\n const closeResult = await this.portfolio.closePosition(tokenId, currentPrice, reason);\n return {\n success: true,\n position: { ...existing, currentPrice, pnlUsd: closeResult.pnl, pnlPercent: closeResult.pnlPercent },\n dryRun: this.config.dryRun,\n };\n } catch (err) {\n return {\n success: false,\n error: `Failed to close position: ${(err as Error).message}`,\n dryRun: this.config.dryRun,\n };\n }\n }\n // else: no existing long — fall through to open/pyramid a paper short\n }\n\n // Only execute buys/sells; HOLD does nothing\n if (decision.action !== 'BUY' && decision.action !== 'STRONG_BUY' && decision.action !== 'SELL' && decision.action !== 'STRONG_SELL') {\n return {\n success: false,\n error: `Action ${decision.action} does not trigger execution`,\n dryRun: this.config.dryRun,\n };\n }\n\n // Minimum conviction gate — filter out marginal \"just barely crossed the\n // threshold\" entries. Trade log review (25 trades): the 3 losing shorts\n // scored -0.10 to -0.15, winning longs scored 0.20-0.40+. Entries below\n // 0.18 absolute score have a much higher stop-out rate because they\n // represent weak signal consensus rather than genuine directional edge.\n // Minimum conviction — set below the lowest regime BUY threshold (ranging 0.17)\n // to avoid a dead zone where scores pass the threshold but fail conviction.\n // Prior value 0.12 created a [0.12, 0.17) dead zone.\n const MIN_CONVICTION_SCORE = 0.08;\n if (Math.abs(decision.score) < MIN_CONVICTION_SCORE) {\n return {\n success: false,\n error: `Score ${decision.score.toFixed(3)} below minimum conviction (${MIN_CONVICTION_SCORE})`,\n dryRun: this.config.dryRun,\n };\n }\n\n // Resolve token → HL asset index for multi-asset execution.\n // If the token doesn't have a known HL perp, skip execution.\n if (this.config.mode === 'hyperliquid-perp') {\n const resolvedIndex = TradeExecutor.resolveAssetIndex(tokenId);\n if (resolvedIndex === undefined) {\n return {\n success: false,\n error: `Token ${tokenId} has no known Hyperliquid perp asset index — skipping execution`,\n dryRun: this.config.dryRun,\n };\n }\n }\n\n // Load portfolio to get current state\n const state = await this.portfolio.load();\n this.riskManager.updatePortfolio(state);\n\n // Calculate stop loss and take profit from current price.\n // Calibrated for SHORT-TERM trades (1-2 day hold) with leverage:\n // Stop: 4% price move × 3x leverage = 12% capital risk (capped by riskPerTrade)\n // Take profit: 1.5:1 R:R = 6% price move × 3x = 18% capital gain\n // Partial exit at 2% move × 3x = 6% capital gain (locks half)\n // Time stop: 96h (see risk.ts) — if it hasn't moved, it's dead money\n const isShort = decision.action === 'SELL' || decision.action === 'STRONG_SELL';\n const direction: 'long' | 'short' = isShort ? 'short' : 'long';\n const RR_RATIO = 2.0; // autoresearch (Apr 27): 1.5→2.0 captures more upside from winners\n // Nunchi autoresearch (103 experiments): wider ATR stops let winners run.\n // Their optimal was 5.5x ATR; we use 3.5x as a compromise between letting\n // winners breathe and controlling loss on stopped trades. Prior 1.5x was\n // too tight — 64% of trades hit the stop, many of which reversed after.\n const ATR_STOP_MULTIPLIER = 3.5;\n // Trade log analysis (32 trades): 62.5% of exits were stops, many reversed\n // after. Widened floor from 3% → 4% to give trades more room to breathe\n // in crypto's noisy price action. ATR-based stops still dominate on\n // higher-vol tokens where ATR × 3.5 > 4%.\n const STOP_FLOOR = 0.05; // minimum 5% (was 4% — still noise-stopping, 63% of exits are stops)\n const STOP_CAP = 0.12; // maximum 12% (tightened from 15%)\n const FALLBACK_STOP = 0.05; // when no ATR available (was 4%)\n\n // Kronos ML-predicted vol overrides ATR when available.\n // predictedVol4h is the per-candle (4h) volatility from Monte Carlo paths.\n // Use 2.5× predicted vol as stop distance (captures ~95% of expected move).\n // Falls back to ATR×3.5 when Kronos is unavailable.\n const KRONOS_STOP_MULTIPLIER = 2.5;\n const useKronos = predictedVol && Number.isFinite(predictedVol) && predictedVol > 0;\n const atrPct = (atr && currentPrice > 0 && !isNaN(atr))\n ? atr / currentPrice\n : FALLBACK_STOP / ATR_STOP_MULTIPLIER;\n const kronosPct = useKronos ? predictedVol! * KRONOS_STOP_MULTIPLIER : 0;\n const rawStopPct = useKronos ? kronosPct : atrPct * ATR_STOP_MULTIPLIER;\n const stopPct = Math.min(STOP_CAP, Math.max(STOP_FLOOR, rawStopPct));\n this.lastAtr = atr;\n const stopLossDistance = currentPrice * stopPct;\n const stopLossPrice = isShort\n ? currentPrice + stopLossDistance // stop above for shorts\n : currentPrice - stopLossDistance; // stop below for longs\n const takeProfitPrice = isShort\n ? currentPrice * (1 - stopPct * RR_RATIO) // profit below for shorts\n : currentPrice * (1 + stopPct * RR_RATIO); // profit above for longs\n\n // Detect a pyramid (same-direction add to an existing position) and halve\n // the size for each add. Geometric decay (1.0x → 0.5x → 0.25x) caps total\n // exposure on a single name at 1.75x the original size.\n const existingSameSide = state.positions.find(\n (p) => p.tokenId === tokenId && (p.side ?? 'long') === direction,\n );\n const isPyramid = existingSameSide !== undefined;\n // Short sizing reduction: trade log analysis showed 25% short WR vs 67%\n // long WR. Halve short exposure until the regime gate + higher conviction\n // thresholds improve short performance.\n const SHORT_SIZE_MULTIPLIER = 0.5;\n const directionMultiplier = isShort ? SHORT_SIZE_MULTIPLIER : 1.0;\n const sizeMultiplier = (isPyramid\n ? 0.5 ** ((existingSameSide!.addCount ?? 0) + 1)\n : 1.0) * directionMultiplier;\n\n // Size the position using risk management. Conviction is fed in via the\n // risk budget — the sizer then clamps at maxSinglePosition, so a\n // high-conviction entry cannot blow past the hard cap (previous bug:\n // sizing was clamped, then multiplied by conviction after, yielding 30%\n // sizes on score ≥ 0.35 and 40% on score ≥ 0.45 against a 20% cap).\n const conviction = convictionMultiplier(decision.score);\n const sizing = this.riskManager.calculatePositionSize(\n currentPrice,\n stopLossPrice,\n state.totalValue,\n this.riskManager.getRiskPerTrade() * conviction,\n );\n\n // Apply leverage: multiply position size (not risk). A 3x leveraged position\n // on a $500 risk budget buys $1500 notional — the stop loss distance stays the\n // same in price terms, so the dollar risk per trade stays at riskPerTrade × portfolio.\n // Pyramid haircut then shrinks for each subsequent add (base 1.0x → 0.5x → 0.25x).\n const pyramidQuantity = sizing.quantity * sizeMultiplier * DIRECTIONAL_LEVERAGE;\n const pyramidSizeUsd = sizing.sizeUsd * sizeMultiplier * DIRECTIONAL_LEVERAGE;\n\n if (pyramidQuantity <= 0 || pyramidSizeUsd <= 0) {\n return {\n success: false,\n error: 'Position sizing returned zero — check portfolio value and stop distance',\n dryRun: this.config.dryRun,\n };\n }\n\n // Enforce Hyperliquid minimum notional ($15) AFTER the pyramid haircut.\n // calculatePositionSize() applies its own floor on the BASE size, but\n // halving (0.5x) and quartering (0.25x) for subsequent adds can push\n // the order below minimum. In dry-run this is harmless paper trading;\n // in hyperliquid-perp mode the order would revert at the venue.\n const MIN_PYRAMID_USD = 15;\n if (isPyramid && pyramidSizeUsd < MIN_PYRAMID_USD) {\n return {\n success: false,\n error: `Pyramid add #${(existingSameSide!.addCount ?? 0) + 1} would be $${pyramidSizeUsd.toFixed(2)} — below $${MIN_PYRAMID_USD} HL minimum notional. Skipping.`,\n dryRun: this.config.dryRun,\n };\n }\n\n // Check if risk manager allows this trade (passes pyramid + direction context)\n const check = this.riskManager.canOpenPosition(tokenId, pyramidSizeUsd, direction);\n if (!check.allowed) {\n return {\n success: false,\n error: `Risk check failed: ${check.reason}`,\n dryRun: this.config.dryRun,\n };\n }\n\n const order: OrderParams = {\n tokenId,\n side: isShort ? 'sell' : 'buy',\n amountUsd: pyramidSizeUsd,\n maxSlippage: 0.015,\n stopLoss: stopLossPrice,\n takeProfit: takeProfitPrice,\n };\n\n if (this.config.dryRun) {\n try {\n const position = await this.executeDryRun(order, currentPrice, isPyramid);\n return { success: true, position, dryRun: true };\n } catch (err) {\n return {\n success: false,\n error: `Dry-run failed: ${(err as Error).message}`,\n dryRun: true,\n };\n }\n } else {\n try {\n const result = await this.executeLive(order, currentPrice);\n // If live execution succeeded, also track in portfolio\n const position = isPyramid\n ? await this.portfolio.addToPosition(tokenId, result.executedPrice, pyramidQuantity, direction)\n : await this.portfolio.openPosition({\n tokenId,\n symbol: tokenId.toUpperCase(),\n side: direction,\n entryPrice: result.executedPrice,\n currentPrice: result.executedPrice,\n quantity: pyramidQuantity,\n entryTimestamp: Date.now(),\n stopLoss: order.stopLoss,\n takeProfit: order.takeProfit,\n strategy: decision.signals[0]?.source ?? 'agent',\n atrAtEntry: this.lastAtr,\n });\n return { success: true, position, dryRun: false };\n } catch (err) {\n return {\n success: false,\n error: `Live execution failed: ${(err as Error).message}`,\n dryRun: false,\n };\n }\n }\n }\n\n /** Process all pending exits (stops, take profits, time stops) */\n async processExits(\n currentPrices: Record<string, number>,\n ): Promise<Array<{ position: Position; exitPrice: number; reason: string; pnl: number }>> {\n const state = await this.portfolio.load();\n this.riskManager.updatePortfolio(state);\n\n const { toClose, reasons } = this.riskManager.checkExits(state.positions, currentPrices);\n const results: Array<{ position: Position; exitPrice: number; reason: string; pnl: number }> = [];\n\n for (const pos of toClose) {\n const exitPrice = currentPrices[pos.tokenId] ?? pos.currentPrice;\n const reason = reasons[pos.tokenId] ?? 'Unknown';\n\n try {\n const closeResult = await this.portfolio.closePosition(pos.tokenId, exitPrice, reason);\n results.push({\n position: pos,\n exitPrice,\n reason,\n pnl: closeResult.pnl,\n });\n } catch (err) {\n console.error(chalk.red(`Failed to close ${pos.symbol}: ${(err as Error).message}`));\n }\n }\n\n // --- Partial profit exits (50% at +3%) ---\n // Reload state after each partial close to avoid iterating stale positions.\n const PARTIAL_PROFIT_TRIGGER = 0.015; // +1.5% unrealized gain — lock profits earlier at 1x leverage\n const PARTIAL_FRACTION = 0.5;\n\n // First pass: identify candidates from a fresh load\n const partialCandidates: string[] = [];\n {\n const refreshedState = await this.portfolio.load();\n for (const pos of refreshedState.positions) {\n if (pos.partialTaken) continue;\n const price = currentPrices[pos.tokenId];\n if (price === undefined) continue;\n const isShort = pos.side === 'short';\n const pnlPercent = isShort\n ? (pos.entryPrice - price) / (pos.entryPrice || 1)\n : (price - pos.entryPrice) / (pos.entryPrice || 1);\n if (pnlPercent >= PARTIAL_PROFIT_TRIGGER) {\n partialCandidates.push(pos.tokenId);\n }\n }\n }\n\n // Second pass: execute each partial close with a fresh state reload\n for (const tokenId of partialCandidates) {\n const freshState = await this.portfolio.load();\n const pos = freshState.positions.find((p) => p.tokenId === tokenId);\n if (!pos || pos.partialTaken) continue; // re-check after reload\n\n const price = currentPrices[tokenId];\n if (price === undefined) continue;\n\n const isShort = pos.side === 'short';\n const pnlPercent = isShort\n ? (pos.entryPrice - price) / (pos.entryPrice || 1)\n : (price - pos.entryPrice) / (pos.entryPrice || 1);\n\n if (pnlPercent >= PARTIAL_PROFIT_TRIGGER) {\n try {\n const partial = await this.portfolio.closePartial(\n tokenId, PARTIAL_FRACTION, price, `Partial profit at ${(pnlPercent * 100).toFixed(1)}%`,\n );\n results.push({\n position: { ...pos, currentPrice: price },\n exitPrice: price,\n reason: `PARTIAL_PROFIT (${(pnlPercent * 100).toFixed(1)}%, closed ${(PARTIAL_FRACTION * 100).toFixed(0)}%)`,\n pnl: partial.pnl,\n });\n console.error(\n chalk.cyan(` Partial exit: ${pos.symbol} ${(PARTIAL_FRACTION * 100).toFixed(0)}% @ $${price.toFixed(4)} — locked $${partial.pnl.toFixed(2)}`),\n );\n } catch (err) {\n console.error(chalk.red(`Failed partial close ${pos.symbol}: ${(err as Error).message}`));\n }\n }\n }\n\n return results;\n }\n\n /** Dry-run execution — paper trade */\n private async executeDryRun(order: OrderParams, currentPrice: number, isPyramid = false): Promise<Position> {\n const quantity = order.amountUsd / currentPrice;\n const direction: 'long' | 'short' = order.side === 'sell' ? 'short' : 'long';\n const sideLabel = isPyramid\n ? (direction === 'short' ? 'PYRAMID SHORT' : 'PYRAMID BUY')\n : (direction === 'short' ? 'SHORT' : 'BUY');\n\n console.error(chalk.cyan(`[DRY RUN] Paper trade: ${sideLabel} ${quantity.toFixed(6)} ${order.tokenId} @ $${currentPrice.toFixed(4)} (${DIRECTIONAL_LEVERAGE}x)`));\n console.error(chalk.cyan(` Size: $${order.amountUsd.toFixed(2)} | SL: $${order.stopLoss.toFixed(4)} | TP: $${order.takeProfit.toFixed(4)}`));\n\n if (isPyramid) {\n return this.portfolio.addToPosition(order.tokenId, currentPrice, quantity, direction);\n }\n\n const position = await this.portfolio.openPosition({\n tokenId: order.tokenId,\n symbol: order.tokenId.toUpperCase(),\n side: direction,\n entryPrice: currentPrice,\n currentPrice,\n quantity,\n entryTimestamp: Date.now(),\n stopLoss: order.stopLoss,\n takeProfit: order.takeProfit,\n strategy: 'paper',\n atrAtEntry: this.lastAtr,\n });\n\n return position;\n }\n\n /** Live execution — dispatches to the configured mode */\n private async executeLive(order: OrderParams, currentPrice?: number): Promise<{ txHash: string; executedPrice: number }> {\n if (this.config.mode === 'hyperliquid-perp') {\n return this.executeHyperliquidPerp(order, currentPrice);\n }\n throw new Error(\n 'Live execution requires --mode hyperliquid-perp. ' +\n 'Use --dry-run for paper trading.',\n );\n }\n\n /** Execute a trade on Hyperliquid perps via the hermes HL skill script. */\n private async executeHyperliquidPerp(order: OrderParams, currentPrice?: number): Promise<{ txHash: string; executedPrice: number }> {\n validateHLEnv();\n if (!currentPrice || currentPrice <= 0) throw new Error('currentPrice is required for live execution');\n\n const hlCoin = resolveHLCoin(order.tokenId);\n if (!hlCoin) {\n throw new Error(`Token ${order.tokenId} has no known Hyperliquid ticker — cannot execute live`);\n }\n\n const quantity = order.amountUsd / currentPrice;\n const isShort = order.side === 'sell';\n\n console.error(chalk.yellow(`[LIVE] Submitting ${isShort ? 'SELL' : 'BUY'} ${quantity.toFixed(6)} ${hlCoin} ($${order.amountUsd.toFixed(2)}) via HL SDK...`));\n\n const result = isShort\n ? await hlMarketSell(hlCoin, quantity)\n : await hlMarketBuy(hlCoin, quantity);\n\n if (!result.success) {\n throw new Error(`Hyperliquid order failed: ${result.error}`);\n }\n\n const executedPrice = result.executedPrice ?? currentPrice;\n const orderId = result.orderId ?? 'unknown';\n\n console.error(chalk.green(`[LIVE] Order filled: ${hlCoin} ${isShort ? 'SHORT' : 'LONG'} @ $${executedPrice.toFixed(4)} (oid: ${orderId})`));\n\n return {\n txHash: orderId, // HL order ID serves as the \"tx hash\" identifier\n executedPrice,\n };\n }\n\n /** Format execution result for display */\n formatExecution(result: {\n success: boolean;\n position?: Position;\n error?: string;\n dryRun: boolean;\n }): string {\n const lines: string[] = [];\n const prefix = result.dryRun ? chalk.cyan('[DRY RUN]') : chalk.green('[LIVE]');\n\n if (result.success && result.position) {\n const p = result.position;\n lines.push('');\n lines.push(`${prefix} ${chalk.bold('Trade Executed')}`);\n lines.push(chalk.dim('─'.repeat(40)));\n lines.push(`Token: ${p.symbol}`);\n lines.push(`Entry: $${p.entryPrice.toFixed(4)}`);\n lines.push(`Quantity: ${p.quantity.toFixed(6)}`);\n lines.push(`Size: $${(p.quantity * p.entryPrice).toFixed(2)}`);\n lines.push(`Stop Loss: $${p.stopLoss.toFixed(4)}`);\n lines.push(`Take Profit: $${p.takeProfit.toFixed(4)}`);\n lines.push(`Strategy: ${p.strategy}`);\n lines.push('');\n } else {\n lines.push('');\n lines.push(`${prefix} ${chalk.red('Trade Failed')}`);\n lines.push(`Reason: ${result.error}`);\n lines.push('');\n }\n\n return lines.join('\\n');\n }\n}\n\n","/**\n * Portfolio tracking with JSON file persistence.\n */\n\nimport { mkdir, readFile, writeFile, rename } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { homedir } from 'node:os';\nimport chalk from 'chalk';\nimport type { Position, PortfolioState } from './risk.js';\n\n/** Margin fraction for perp positions — both longs and shorts. */\nconst MARGIN_FRACTION = 0.33;\n\n/**\n * Compute total portfolio value with margin-based position accounting.\n * For leveraged perp positions, the portfolio \"owns\" the margin + unrealized PnL,\n * not the full notional. This prevents phantom value when cash is debited at\n * margin rate but positions are counted at full notional.\n */\nfunction computeTotalValue(cash: number, positions: Position[]): number {\n const positionEquity = positions.reduce((sum, p) => {\n const isShort = p.side === 'short';\n const pnl = isShort\n ? (p.entryPrice - p.currentPrice) * p.quantity\n : (p.currentPrice - p.entryPrice) * p.quantity;\n return sum + p.quantity * p.entryPrice * MARGIN_FRACTION + pnl;\n }, 0);\n return cash + positionEquity;\n}\n\n/** Check and reset PnL counters based on time boundaries */\nexport function resetPnlCounters(state: PortfolioState): PortfolioState {\n const now = Date.now();\n const updated = { ...state };\n\n // Reset daily PnL at midnight UTC\n const todayMidnight = new Date();\n todayMidnight.setUTCHours(0, 0, 0, 0);\n const todayMs = todayMidnight.getTime();\n if (!updated.lastDailyReset || updated.lastDailyReset < todayMs) {\n updated.dailyPnl = 0;\n updated.lastDailyReset = now;\n }\n\n // Orca-inspired: PnL-aware daily cap, see README / PR #223.\n // Reset the daily-entry counter on the same UTC day boundary as dailyPnl.\n // Tracked independently (lastDailyEntriesReset) so legacy files missing\n // `dailyEntries` reset cleanly on first load.\n if (!updated.lastDailyEntriesReset || updated.lastDailyEntriesReset < todayMs) {\n updated.dailyEntries = 0;\n updated.lastDailyEntriesReset = now;\n }\n\n // Reset weekly PnL on Monday midnight UTC\n const dayOfWeek = new Date(now).getUTCDay(); // 0=Sun, 1=Mon\n const daysSinceMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;\n const mondayMidnight = new Date();\n mondayMidnight.setUTCHours(0, 0, 0, 0);\n mondayMidnight.setUTCDate(mondayMidnight.getUTCDate() - daysSinceMonday);\n const mondayMs = mondayMidnight.getTime();\n if (!updated.lastWeeklyReset || updated.lastWeeklyReset < mondayMs) {\n updated.weeklyPnl = 0;\n updated.lastWeeklyReset = now;\n }\n\n // Reset monthly PnL on 1st of month midnight UTC\n const firstOfMonth = new Date();\n firstOfMonth.setUTCHours(0, 0, 0, 0);\n firstOfMonth.setUTCDate(1);\n const firstMs = firstOfMonth.getTime();\n if (!updated.lastMonthlyReset || updated.lastMonthlyReset < firstMs) {\n updated.monthlyPnl = 0;\n updated.lastMonthlyReset = now;\n }\n\n return updated;\n}\n\nexport interface TradeRecord {\n tokenId: string;\n symbol: string;\n side: 'long' | 'short';\n entryPrice: number;\n exitPrice: number;\n quantity: number;\n pnlUsd: number;\n pnlPercent: number;\n entryTimestamp: number;\n exitTimestamp: number;\n duration: number;\n strategy: string;\n exitReason: string;\n}\n\nconst DEFAULT_PORTFOLIO: PortfolioState = {\n totalValue: 10000,\n positions: [],\n cash: 10000,\n dailyPnl: 0,\n weeklyPnl: 0,\n monthlyPnl: 0,\n dailyEntries: 0,\n initialValue: 10000,\n};\n\nfunction createDefaultPortfolio(): PortfolioState {\n return {\n ...DEFAULT_PORTFOLIO,\n positions: [],\n stopCooldowns: {},\n tokenConsecLosses: {},\n tokenLossCooldowns: {},\n };\n}\n\nexport class PortfolioTracker {\n private statePath: string;\n private historyPath: string;\n private state: PortfolioState;\n /** In-process mutex — serializes load/modify/save sequences to prevent\n * overlapping cycles from double-closing positions or double-debiting cash. */\n private _lock: Promise<void> = Promise.resolve();\n\n constructor() {\n const base = join(homedir(), '.sherwood', 'agent');\n this.statePath = join(base, 'portfolio.json');\n this.historyPath = join(base, 'trades.json');\n this.state = createDefaultPortfolio();\n }\n\n /** Acquire the in-process lock. Callers that perform load→modify→save\n * should wrap their sequence: `const release = await this.acquireLock();`\n * then `release()` in a finally block. */\n private acquireLock(): Promise<() => void> {\n let release: () => void;\n const next = new Promise<void>((resolve) => { release = resolve; });\n const prev = this._lock;\n this._lock = next;\n return prev.then(() => release!);\n }\n\n /**\n * Initialize portfolio from on-chain vault balance instead of the\n * hardcoded $10k default. Reads totalAssets() from the vault that\n * the strategy clone is deployed to.\n *\n * Call once on startup (before the first cycle) when no persisted\n * portfolio.json exists. If a portfolio already exists on disk,\n * this is a no-op — we trust the persisted state.\n *\n * @param strategyClone - HyperliquidPerpStrategy clone address\n * @param chain - 'hyperevm' | 'hyperevm-testnet'\n */\n async initFromOnChain(strategyClone: string, chain: string): Promise<void> {\n // Only initialize if no persisted state exists\n try {\n await readFile(this.statePath, 'utf-8');\n return; // file exists — trust persisted state\n } catch {\n // No file — initialize from on-chain\n }\n\n try {\n const { createPublicClient, http } = await import('viem');\n const { hyperevm, hyperevmTestnet } = await import('../lib/network.js');\n const { BASE_STRATEGY_ABI, SYNDICATE_VAULT_ABI } = await import('../lib/abis.js');\n\n const selectedChain = chain === 'hyperevm-testnet' ? hyperevmTestnet : hyperevm;\n const client = createPublicClient({\n chain: selectedChain,\n transport: http(),\n });\n\n // Read vault address from the strategy clone\n const vaultAddr = await client.readContract({\n address: strategyClone as `0x${string}`,\n abi: BASE_STRATEGY_ABI,\n functionName: 'vault',\n }) as `0x${string}`;\n\n if (!vaultAddr || vaultAddr === '0x0000000000000000000000000000000000000000') {\n console.error('[portfolio] Strategy clone has no vault — using default portfolio');\n return;\n }\n\n // Read total assets from the vault (USDC, 6 decimals)\n const totalAssets = await client.readContract({\n address: vaultAddr,\n abi: SYNDICATE_VAULT_ABI,\n functionName: 'totalAssets',\n }) as bigint;\n\n const vaultValueUsd = Number(totalAssets) / 1e6; // USDC 6 decimals\n\n if (vaultValueUsd > 0) {\n this.state = {\n ...createDefaultPortfolio(),\n totalValue: vaultValueUsd,\n cash: vaultValueUsd,\n initialValue: vaultValueUsd, // anchor cumulative PnL% to the on-chain starting balance\n };\n await this.save(this.state);\n console.error(`[portfolio] Initialized from on-chain vault: $${vaultValueUsd.toFixed(2)} USDC`);\n } else {\n console.error('[portfolio] Vault has 0 assets — using default portfolio');\n }\n } catch (err) {\n console.error(`[portfolio] Failed to read on-chain balance: ${(err as Error).message} — using default`);\n }\n }\n\n /**\n * Force-refresh portfolio value from on-chain vault balance.\n * Unlike initFromOnChain(), this runs even if portfolio.json exists.\n * Call via `sherwood agent config --sync-vault` or on explicit user request.\n */\n async syncFromOnChain(strategyClone: string, chain: string): Promise<void> {\n // Temporarily remove the file so initFromOnChain doesn't short-circuit\n try { await import('node:fs/promises').then(fs => fs.unlink(this.statePath)); } catch { /* ok */ }\n await this.initFromOnChain(strategyClone, chain);\n }\n\n /** Load portfolio state from disk with validation */\n async load(): Promise<PortfolioState> {\n try {\n const data = await readFile(this.statePath, 'utf-8');\n const parsed = JSON.parse(data) as PortfolioState;\n\n // Validate critical numeric fields are finite and non-negative\n if (\n !Number.isFinite(parsed.totalValue) || parsed.totalValue < 0\n || !Number.isFinite(parsed.cash) || parsed.cash < 0\n || !Number.isFinite(parsed.dailyPnl)\n || !Number.isFinite(parsed.weeklyPnl)\n || !Number.isFinite(parsed.monthlyPnl)\n || !Array.isArray(parsed.positions)\n ) {\n console.error('Portfolio file has invalid data — resetting to defaults');\n this.state = createDefaultPortfolio();\n return this.state;\n }\n\n // Validate each position\n for (const p of parsed.positions) {\n if (\n !Number.isFinite(p.entryPrice) || p.entryPrice <= 0\n || !Number.isFinite(p.quantity) || p.quantity <= 0\n || !Number.isFinite(p.currentPrice) || p.currentPrice <= 0\n || !Number.isFinite(p.stopLoss) || p.stopLoss <= 0\n || !Number.isFinite(p.takeProfit) || p.takeProfit <= 0\n ) {\n console.error(`Invalid position data for ${p.tokenId} — resetting portfolio`);\n this.state = createDefaultPortfolio();\n return this.state;\n }\n }\n\n // Legacy portfolios pre-dating the `initialValue` field get the default\n // $10k anchor — safe because paper-trading always started there. Live\n // syndicates that went through `initFromOnChain` already have this set.\n if (!Number.isFinite(parsed.initialValue) || (parsed.initialValue ?? 0) <= 0) {\n parsed.initialValue = DEFAULT_PORTFOLIO.initialValue;\n }\n\n this.state = parsed;\n } catch {\n // File doesn't exist or is invalid — start fresh\n this.state = createDefaultPortfolio();\n }\n return this.state;\n }\n\n /** Save portfolio state to disk (atomic write to prevent corruption) */\n async save(state: PortfolioState): Promise<void> {\n this.state = state;\n await mkdir(dirname(this.statePath), { recursive: true });\n const tmpPath = this.statePath + '.tmp';\n await writeFile(tmpPath, JSON.stringify(state, null, 2), 'utf-8');\n await rename(tmpPath, this.statePath);\n }\n\n /** Open a new position */\n async openPosition(position: Omit<Position, 'pnlPercent' | 'pnlUsd'>): Promise<Position> {\n if (position.quantity <= 0) {\n throw new Error(`Invalid position quantity: ${position.quantity}`);\n }\n if (position.entryPrice <= 0) {\n throw new Error(`Invalid entry price: ${position.entryPrice}`);\n }\n const release = await this.acquireLock();\n try {\n await this.load();\n\n const fullPosition: Position = {\n ...position,\n addCount: position.addCount ?? 0,\n lastAddTimestamp: position.lastAddTimestamp ?? position.entryTimestamp,\n // Orca-inspired HWM profit-lock, see README / PR #223.\n // Seed peakPrice at entry so subsequent cycles can ratchet from\n // the first favorable move.\n peakPrice: position.peakPrice ?? position.entryPrice,\n pnlPercent: 0,\n pnlUsd: 0,\n };\n\n this.state.positions.push(fullPosition);\n // Both longs and shorts on perp venues only require margin, not full notional.\n // With 3x directional leverage, a $3,500 notional position only locks $1,167\n // margin (33%). Prior code debited full notional for longs, depleting cash 3x\n // faster than reality and blocking subsequent trades.\n const cashDebit = position.quantity * position.entryPrice * MARGIN_FRACTION;\n this.state.cash -= cashDebit;\n this.state.totalValue = computeTotalValue(this.state.cash, this.state.positions);\n\n // Orca-inspired: PnL-aware daily cap, see README / PR #223.\n // Count this NEW entry against the day's turnover budget.\n // Pyramid adds go through addToPosition and do NOT count.\n const prior = Number.isFinite(this.state.dailyEntries) ? (this.state.dailyEntries ?? 0) : 0;\n this.state.dailyEntries = prior + 1;\n\n await this.save(this.state);\n return fullPosition;\n } finally { release(); }\n }\n\n /**\n * Pyramid into an existing position.\n * Computes a quantity-weighted average entry price and increments `addCount`.\n * Cash is debited at the new fill price. Stops/take-profits stay anchored to\n * the prior values (caller is free to widen them after).\n *\n * Throws if no existing position exists or the side disagrees.\n */\n async addToPosition(\n tokenId: string,\n addPrice: number,\n addQuantity: number,\n side: 'long' | 'short',\n ): Promise<Position> {\n if (addQuantity <= 0) throw new Error(`Invalid add quantity: ${addQuantity}`);\n if (addPrice <= 0) throw new Error(`Invalid add price: ${addPrice}`);\n const release = await this.acquireLock();\n try {\n await this.load();\n\n const idx = this.state.positions.findIndex((p) => p.tokenId === tokenId);\n if (idx === -1) throw new Error(`No open position for ${tokenId} to pyramid into`);\n\n const pos = this.state.positions[idx]!;\n const existingSide = pos.side ?? 'long';\n if (existingSide !== side) {\n throw new Error(`Cannot pyramid ${side} on existing ${existingSide} position in ${tokenId}`);\n }\n\n // Weighted-average entry price across the combined quantity\n const newQty = pos.quantity + addQuantity;\n const weightedEntry = (pos.entryPrice * pos.quantity + addPrice * addQuantity) / newQty;\n\n const updated: Position = {\n ...pos,\n entryPrice: weightedEntry,\n quantity: newQty,\n currentPrice: addPrice,\n addCount: (pos.addCount ?? 0) + 1,\n lastAddTimestamp: Date.now(),\n pnlPercent: 0, // recomputed on next price update\n pnlUsd: 0,\n };\n\n this.state.positions[idx] = updated;\n // Both longs and shorts use margin-based cash debit (33% = 3x leverage).\n this.state.cash -= addPrice * addQuantity * MARGIN_FRACTION;\n this.state.totalValue = computeTotalValue(this.state.cash, this.state.positions);\n\n await this.save(this.state);\n return updated;\n } finally { release(); }\n }\n\n /** Close a position and record trade */\n async closePosition(\n tokenId: string,\n exitPrice: number,\n reason: string,\n ): Promise<{ pnl: number; pnlPercent: number; duration: number }> {\n const release = await this.acquireLock();\n try {\n await this.load();\n\n const idx = this.state.positions.findIndex((p) => p.tokenId === tokenId);\n if (idx === -1) {\n throw new Error(`No open position for ${tokenId}`);\n }\n\n const pos = this.state.positions[idx]!;\n const isShort = pos.side === 'short';\n const pnlUsd = isShort\n ? (pos.entryPrice - exitPrice) * pos.quantity\n : (exitPrice - pos.entryPrice) * pos.quantity;\n const pnlPercent = isShort\n ? (pos.entryPrice - exitPrice) / (pos.entryPrice || 1)\n : (exitPrice - pos.entryPrice) / (pos.entryPrice || 1);\n const duration = Math.floor((Date.now() - pos.entryTimestamp) / 1000);\n\n // Record the trade\n const record: TradeRecord = {\n tokenId: pos.tokenId,\n symbol: pos.symbol,\n side: pos.side ?? 'long',\n entryPrice: pos.entryPrice,\n exitPrice,\n quantity: pos.quantity,\n pnlUsd,\n pnlPercent,\n entryTimestamp: pos.entryTimestamp,\n exitTimestamp: Date.now(),\n duration,\n strategy: pos.strategy,\n exitReason: reason,\n };\n\n await this.appendTradeRecord(record);\n\n // Remove position and update cash.\n // Both longs and shorts return margin (33% of notional) + PnL.\n this.state.positions.splice(idx, 1);\n const cashCredit = pos.quantity * pos.entryPrice * MARGIN_FRACTION + pnlUsd;\n this.state.cash += cashCredit;\n this.state.dailyPnl += pnlUsd;\n this.state.weeklyPnl += pnlUsd;\n this.state.monthlyPnl += pnlUsd;\n this.state.totalValue = computeTotalValue(this.state.cash, this.state.positions);\n\n // Record stop-loss cooldown to prevent rapid re-entry\n if (reason.toLowerCase().includes('stop')) {\n if (!this.state.stopCooldowns) this.state.stopCooldowns = {};\n this.state.stopCooldowns[tokenId] = Date.now();\n }\n\n // Track per-token consecutive losses for the serial-loser gate.\n // A losing trade increments the counter; a winning trade resets it to 0.\n // When the counter hits TOKEN_CONSEC_LOSS_LIMIT, record the cooldown timestamp.\n if (!this.state.tokenConsecLosses) this.state.tokenConsecLosses = {};\n if (!this.state.tokenLossCooldowns) this.state.tokenLossCooldowns = {};\n if (pnlUsd < 0) {\n this.state.tokenConsecLosses[tokenId] = (this.state.tokenConsecLosses[tokenId] ?? 0) + 1;\n if (this.state.tokenConsecLosses[tokenId]! >= 2) {\n this.state.tokenLossCooldowns[tokenId] = Date.now();\n }\n } else {\n // Win or breakeven — reset consecutive loss counter\n this.state.tokenConsecLosses[tokenId] = 0;\n delete this.state.tokenLossCooldowns[tokenId];\n }\n\n await this.save(this.state);\n\n return { pnl: pnlUsd, pnlPercent, duration };\n } finally { release(); }\n }\n\n /**\n * Close a fraction of a position. Records a partial trade and reduces\n * the position's quantity. Entry price and stops are preserved.\n */\n async closePartial(\n tokenId: string,\n fraction: number,\n exitPrice: number,\n reason: string,\n ): Promise<{ pnl: number; pnlPercent: number; quantityClosed: number }> {\n if (fraction <= 0 || fraction >= 1) {\n throw new Error(`Invalid fraction: ${fraction} (must be between 0 and 1 exclusive)`);\n }\n const release = await this.acquireLock();\n try {\n await this.load();\n\n const idx = this.state.positions.findIndex((p) => p.tokenId === tokenId);\n if (idx === -1) throw new Error(`No open position for ${tokenId}`);\n\n const pos = this.state.positions[idx]!;\n const isShort = pos.side === 'short';\n const quantityClosed = pos.quantity * fraction;\n const pnlUsd = isShort\n ? (pos.entryPrice - exitPrice) * quantityClosed\n : (exitPrice - pos.entryPrice) * quantityClosed;\n const pnlPercent = isShort\n ? (pos.entryPrice - exitPrice) / (pos.entryPrice || 1)\n : (exitPrice - pos.entryPrice) / (pos.entryPrice || 1);\n\n // Record partial trade\n const record: TradeRecord = {\n tokenId: pos.tokenId,\n symbol: pos.symbol,\n side: pos.side ?? 'long',\n entryPrice: pos.entryPrice,\n exitPrice,\n quantity: quantityClosed,\n pnlUsd,\n pnlPercent,\n entryTimestamp: pos.entryTimestamp,\n exitTimestamp: Date.now(),\n duration: Math.floor((Date.now() - pos.entryTimestamp) / 1000),\n strategy: pos.strategy,\n exitReason: reason,\n };\n await this.appendTradeRecord(record);\n\n // Reduce position, mark partial taken\n pos.quantity -= quantityClosed;\n pos.partialTaken = true;\n // Return margin fraction (33%) + PnL for both longs and shorts.\n const partialCashCredit = pos.entryPrice * quantityClosed * MARGIN_FRACTION + pnlUsd;\n this.state.cash += partialCashCredit;\n this.state.dailyPnl += pnlUsd;\n this.state.weeklyPnl += pnlUsd;\n this.state.monthlyPnl += pnlUsd;\n this.state.totalValue = computeTotalValue(this.state.cash, this.state.positions);\n\n await this.save(this.state);\n return { pnl: pnlUsd, pnlPercent, quantityClosed };\n } finally { release(); }\n }\n\n /** Update current prices for all positions */\n async updatePrices(prices: Record<string, number>): Promise<PortfolioState> {\n await this.load();\n\n for (const pos of this.state.positions) {\n const price = prices[pos.tokenId];\n if (price !== undefined) {\n // Guard against NaN/Infinity — a bad price update would corrupt PnL\n // calculations and make stops evaluate to false (NaN <= X is always false),\n // permanently trapping positions.\n if (!Number.isFinite(price) || price <= 0) {\n console.error(`[portfolio] Skipping invalid price for ${pos.tokenId}: ${price}`);\n continue;\n }\n pos.currentPrice = price;\n const isShort = pos.side === 'short';\n pos.pnlUsd = isShort\n ? (pos.entryPrice - price) * pos.quantity\n : (price - pos.entryPrice) * pos.quantity;\n pos.pnlPercent = isShort\n ? (pos.entryPrice - price) / (pos.entryPrice || 1)\n : (price - pos.entryPrice) / (pos.entryPrice || 1);\n }\n }\n\n this.state.totalValue = computeTotalValue(this.state.cash, this.state.positions);\n\n await this.save(this.state);\n return this.state;\n }\n\n /** Get trade history */\n async getHistory(days?: number): Promise<TradeRecord[]> {\n let records: TradeRecord[] = [];\n try {\n const data = await readFile(this.historyPath, 'utf-8');\n records = JSON.parse(data) as TradeRecord[];\n } catch {\n return [];\n }\n\n if (days !== undefined) {\n const cutoff = Date.now() - days * 24 * 60 * 60 * 1000;\n records = records.filter((r) => r.exitTimestamp >= cutoff);\n }\n\n return records;\n }\n\n /** Get performance metrics */\n async getMetrics(days?: number): Promise<{\n totalTrades: number;\n winRate: number;\n avgPnlPercent: number;\n totalPnlUsd: number;\n sharpeRatio: number;\n maxDrawdown: number;\n bestTrade: TradeRecord;\n worstTrade: TradeRecord;\n }> {\n const trades = await this.getHistory(days);\n\n if (trades.length === 0) {\n const empty: TradeRecord = {\n tokenId: '', symbol: '', side: 'long', entryPrice: 0, exitPrice: 0,\n quantity: 0, pnlUsd: 0, pnlPercent: 0, entryTimestamp: 0,\n exitTimestamp: 0, duration: 0, strategy: '', exitReason: '',\n };\n return {\n totalTrades: 0, winRate: 0, avgPnlPercent: 0, totalPnlUsd: 0,\n sharpeRatio: 0, maxDrawdown: 0, bestTrade: empty, worstTrade: empty,\n };\n }\n\n const wins = trades.filter((t) => t.pnlUsd > 0);\n const pnls = trades.map((t) => t.pnlPercent);\n const totalPnlUsd = trades.reduce((s, t) => s + t.pnlUsd, 0);\n const avgPnlPercent = pnls.reduce((s, p) => s + p, 0) / pnls.length;\n\n // Sharpe ratio: annualize returns based on average holding period\n // Standard formula: (mean return - risk-free rate) / stddev of returns\n const avgHoldingDays = trades.length > 0\n ? trades.reduce((sum, t) => sum + t.duration, 0) / trades.length / (24 * 60 * 60)\n : 1;\n const tradesPerYear = Math.max(252 / Math.max(avgHoldingDays, 1), 1);\n\n const riskFreePerTrade = 0.05 / tradesPerYear; // Risk-free rate per trade\n const mean = avgPnlPercent;\n const excessMean = mean - riskFreePerTrade;\n const variance = pnls.length > 1\n ? pnls.reduce((s, p) => s + (p - mean) ** 2, 0) / (pnls.length - 1)\n : 0;\n const stdDev = Math.sqrt(variance);\n const sharpeRatio = stdDev > 0 ? (excessMean / stdDev) * Math.sqrt(tradesPerYear) : 0;\n\n // Max drawdown from cumulative equity curve (peak-to-trough)\n // Start with a reasonable initial portfolio value\n const initialValue = 10000; // Start with default $10k portfolio\n let equityPeak = initialValue;\n let equity = initialValue;\n let maxDrawdown = 0;\n\n for (const t of trades) {\n equity += t.pnlUsd;\n if (equity > equityPeak) {\n equityPeak = equity;\n }\n if (equityPeak > 0 && equity < equityPeak) {\n const drawdown = (equityPeak - equity) / equityPeak;\n if (drawdown > maxDrawdown) {\n maxDrawdown = drawdown;\n }\n }\n }\n\n const sorted = [...trades].sort((a, b) => a.pnlUsd - b.pnlUsd);\n const worstTrade = sorted[0]!;\n const bestTrade = sorted[sorted.length - 1]!;\n\n return {\n totalTrades: trades.length,\n winRate: wins.length / trades.length,\n avgPnlPercent,\n totalPnlUsd,\n sharpeRatio,\n maxDrawdown,\n bestTrade,\n worstTrade,\n };\n }\n\n /** Format portfolio summary for display */\n formatSummary(): string {\n const s = this.state;\n const lines: string[] = [];\n\n lines.push('');\n lines.push(chalk.bold(' Portfolio Summary'));\n lines.push(chalk.dim(' ' + '─'.repeat(50)));\n lines.push(` Total Value: $${s.totalValue.toFixed(2)}`);\n lines.push(` Cash: $${s.cash.toFixed(2)}`);\n lines.push(` Positions: ${s.positions.length}`);\n lines.push('');\n\n if (s.positions.length > 0) {\n lines.push(chalk.bold(' Open Positions:'));\n for (const p of s.positions) {\n const pnlColor = p.pnlUsd >= 0 ? chalk.green : chalk.red;\n const pnlStr = pnlColor(\n `${p.pnlUsd >= 0 ? '+' : ''}$${p.pnlUsd.toFixed(2)} (${(p.pnlPercent * 100).toFixed(1)}%)`,\n );\n lines.push(\n ` ${p.symbol.padEnd(10)} ${p.quantity.toFixed(4)} @ $${p.entryPrice.toFixed(4)} PnL: ${pnlStr} [${p.strategy}]`,\n );\n }\n lines.push('');\n }\n\n const dailyColor = s.dailyPnl >= 0 ? chalk.green : chalk.red;\n lines.push(` Daily PnL: ${dailyColor((s.dailyPnl >= 0 ? '+' : '') + '$' + s.dailyPnl.toFixed(2))}`);\n lines.push(` Weekly PnL: ${s.weeklyPnl >= 0 ? '+' : ''}$${s.weeklyPnl.toFixed(2)}`);\n lines.push(` Monthly PnL: ${s.monthlyPnl >= 0 ? '+' : ''}$${s.monthlyPnl.toFixed(2)}`);\n lines.push('');\n\n return lines.join('\\n');\n }\n\n /** Append a trade record to history file */\n private async appendTradeRecord(record: TradeRecord): Promise<void> {\n let records: TradeRecord[] = [];\n try {\n const data = await readFile(this.historyPath, 'utf-8');\n records = JSON.parse(data) as TradeRecord[];\n } catch {\n // Start fresh\n }\n\n records.push(record);\n await mkdir(dirname(this.historyPath), { recursive: true });\n const tmpPath = this.historyPath + '.tmp';\n await writeFile(tmpPath, JSON.stringify(records, null, 2), 'utf-8');\n await rename(tmpPath, this.historyPath);\n }\n}\n","/**\n * Risk management module — position sizing, drawdown limits, stop-loss management.\n */\n\nimport chalk from 'chalk';\n\nexport interface PortfolioState {\n totalValue: number;\n positions: Position[];\n cash: number;\n dailyPnl: number;\n weeklyPnl: number;\n monthlyPnl: number;\n lastDailyReset?: number;\n lastWeeklyReset?: number;\n lastMonthlyReset?: number;\n /** Token → timestamp of last stop-loss exit. Used to enforce cooldown\n * before re-entry to prevent the stop-reentry-stop pattern. */\n stopCooldowns?: Record<string, number>;\n /** Token → count of consecutive losses. Reset to 0 on a winning trade.\n * When count >= TOKEN_CONSEC_LOSS_LIMIT, a 24h cooldown is enforced\n * before re-entry to prevent serial-loser patterns (e.g. AAVE: 4 trades,\n * 3 losses, -$144.75). */\n tokenConsecLosses?: Record<string, number>;\n /** Token → timestamp when the consecutive-loss cooldown was triggered.\n * Used alongside tokenConsecLosses to enforce a 24h ban after N losses. */\n tokenLossCooldowns?: Record<string, number>;\n /** Orca-inspired: PnL-aware daily cap, see README / PR #223.\n * Count of NEW position entries (not pyramid adds) opened since the\n * last daily reset. Used together with getDynamicDailyCap() to throttle\n * entry frequency based on the day's realized PnL. */\n dailyEntries?: number;\n /** Timestamp of the last `dailyEntries` reset — tracked independently\n * from `lastDailyReset` so future boundary logic can diverge if needed\n * (and so legacy portfolio.json files without this field reset cleanly). */\n lastDailyEntriesReset?: number;\n /** Portfolio value at inception — used to compute cumulative PnL%.\n * Defaults to the DEFAULT_PORTFOLIO value ($10k) for legacy files, or\n * the on-chain vault balance for live syndicates initialized via\n * `initFromOnChain`. Never updated after the first load. */\n initialValue?: number;\n}\n\nexport interface Position {\n tokenId: string;\n symbol: string;\n /** 'long' (default for backward compat) or 'short'. */\n side?: 'long' | 'short';\n entryPrice: number;\n currentPrice: number;\n quantity: number;\n entryTimestamp: number;\n stopLoss: number;\n takeProfit: number;\n trailingStop?: number;\n strategy: string;\n pnlPercent: number;\n pnlUsd: number;\n /** Number of pyramid adds since initial entry (0 = base position).\n * Capped by RiskManager.canOpenPosition; each add halves the prior add size. */\n addCount?: number;\n /** Timestamp of the most recent add (or initial entry).\n * Used to enforce minimum spacing between adds. */\n lastAddTimestamp?: number;\n /** ATR-14 at the time of entry. Used for per-position trailing stop\n * distance (1.0×ATR) and breakeven trigger calibration. */\n atrAtEntry?: number;\n /** Whether the 50% partial-profit exit has been taken. Prevents\n * re-triggering on subsequent cycles. */\n partialTaken?: boolean;\n /** Orca-inspired: HWM-based profit-lock, see README / PR #223.\n * For LONG: the highest price seen since entry (>= entryPrice).\n * For SHORT: the lowest price seen since entry (<= entryPrice).\n * Updated each cycle in `updateTrailingStops`. Enables stop-loss\n * ratcheting based on peak-to-date gain rather than unrealized PnL\n * alone — so a retrace followed by a new peak always advances the\n * lock, never resets it. Persisted to portfolio.json. Legacy positions\n * loaded without this field default to entryPrice (safest — never\n * locks too aggressively on the first cycle post-upgrade). */\n peakPrice?: number;\n}\n\nexport interface RiskConfig {\n maxPortfolioRisk: number;\n maxSinglePosition: number;\n maxCorrelatedExposure: number;\n maxConcurrentTrades: number;\n hardStopPercent: number;\n trailingStopAtr: number;\n /** Trailing stop as a fraction of current price (e.g. 0.05 = stop at price × 0.95).\n * Used by updateTrailingStops() which runs each loop cycle without needing ATR.\n * Set to 0 to disable. */\n trailingStopPct: number;\n /** Move stop to entry (breakeven) once position gains this percent.\n * E.g. 0.02 = after +2% unrealized gain, stop tightens to entry price.\n * Set to 0 to disable. */\n breakevenTriggerPct: number;\n /** Orca-inspired HWM profit-lock, see README / PR #223.\n * Stepped table: each {trigger, lockPct} pair means \"once PnL% from\n * entry crosses `trigger`, ratchet stop-loss to lock in `lockPct` of\n * the peak move from entry\".\n * For LONG: stop = entry + (peakPrice - entry) * lockPct.\n * For SHORT: stop = entry - (entry - peakPrice) * lockPct.\n * Entries iterate in order; highest-triggered tier wins (stricter\n * locks override looser ones) and stops only ratchet up — never down. */\n profitLockSteps: Array<{ trigger: number; lockPct: number }>;\n dailyLossLimit: number;\n weeklyLossLimit: number;\n monthlyLossLimit: number;\n maxSlippage: Record<string, number>;\n riskPerTrade: number;\n /** Orca-inspired: PnL-aware daily cap, see README / PR #223.\n * When set, short-circuits the tiered step function in\n * `getDynamicDailyCap()` and uses this value directly. Useful for\n * tests or an aggressive-mode config override. Leave unset for the\n * default PnL-tiered behavior. */\n dailyCapOverride?: number;\n}\n\n/**\n * Default risk config.\n *\n * `trailingStopPct` / `breakevenTriggerPct` / `profitLockSteps` are now ON\n * by default based on paper-trading analysis showing active trailing\n * significantly improves risk-adjusted returns. These match\n * RECOMMENDED_TRAILING_CONFIG (minus the third profit-lock step).\n *\n * Override via config:\n * sherwood agent config --set trailingStopPct=0\n * sherwood agent config --set breakevenTriggerPct=0\n */\nexport const DEFAULT_RISK_CONFIG: RiskConfig = {\n maxPortfolioRisk: 0.15,\n maxSinglePosition: 0.55, // 55% notional (with 3x leverage: ~18% margin)\n maxCorrelatedExposure: 0.25,\n maxConcurrentTrades: 8,\n hardStopPercent: 0.10, // 10% hard stop (was 5% — wider to match 3.5x ATR stops)\n trailingStopAtr: 3.5, // match executor ATR multiplier (was 1.5x — autoresearch: wider = better)\n trailingStopPct: 0.03, // 3% trail (autoresearch: tighter trail locks profits faster)\n breakevenTriggerPct: 0.015, // move to breakeven after +1.5% gain\n // Orca-inspired HWM profit-lock (see README / PR #223): each entry locks a\n // percentage of the peak-to-date move. Tiers cascade — the highest\n // triggered tier wins — and stops never ratchet down.\n profitLockSteps: [\n { trigger: 0.05, lockPct: 0.30 }, // +5% gain → lock 30% of peak move\n { trigger: 0.10, lockPct: 0.50 }, // +10% gain → lock 50% of peak move\n { trigger: 0.15, lockPct: 0.70 }, // +15% gain → lock 70% of peak move\n { trigger: 0.20, lockPct: 0.85 }, // +20% gain → lock 85% of peak move\n ],\n dailyLossLimit: 0.05,\n weeklyLossLimit: 0.10,\n monthlyLossLimit: 0.15,\n maxSlippage: { large: 0.005, mid: 0.015, small: 0.03 },\n riskPerTrade: 0.02,\n};\n\n/**\n * Opinionated trailing-stop preset.\n *\n * To enable all three mechanisms at once:\n * sherwood agent config --set trailingStopPct=0.05\n * sherwood agent config --set breakevenTriggerPct=0.02\n * (profitLockSteps currently requires editing config.json directly)\n */\n/**\n * Short-term trailing config (1-2 day holds).\n * Tighter stops and faster profit-locking than swing trading.\n */\nexport const RECOMMENDED_TRAILING_CONFIG = {\n trailingStopPct: 0.04, // 4% trail (autoresearch: wider stops let winners run)\n breakevenTriggerPct: 0.02, // move to breakeven after +2% gain (matches partial profit trigger)\n // HWM tiers: lock a growing fraction of the peak move from entry.\n profitLockSteps: [\n { trigger: 0.05, lockPct: 0.30 }, // +5% gain → lock 30% of peak move\n { trigger: 0.10, lockPct: 0.50 }, // +10% gain → lock 50% of peak move\n { trigger: 0.15, lockPct: 0.70 }, // +15% gain → lock 70% of peak move\n { trigger: 0.20, lockPct: 0.85 }, // +20% gain → lock 85% of peak move\n ],\n} as const;\n\n/** Pyramiding configuration — conservative defaults to limit blowup risk\n * on a single name. With MAX_PYRAMID_ADDS=2 and halving size each add,\n * total exposure caps at 1.0x + 0.5x + 0.25x = 1.75x of the base size. */\nexport const MAX_PYRAMID_ADDS = 2;\nexport const PYRAMID_MIN_SPACING_MS = 4 * 60 * 60 * 1000; // 4 hours\nexport const STOP_COOLDOWN_MS = 4 * 60 * 60 * 1000; // 4 hours after a stop-loss\n\n/** Number of consecutive losses on a single token before enforcing a longer cooldown. */\nexport const TOKEN_CONSEC_LOSS_LIMIT = 2;\n/** Cooldown duration after hitting the consecutive-loss limit (24 hours). */\nexport const TOKEN_LOSS_COOLDOWN_MS = 24 * 60 * 60 * 1000;\n\nconst EMPTY_PORTFOLIO: PortfolioState = {\n totalValue: 0,\n positions: [],\n cash: 0,\n dailyPnl: 0,\n weeklyPnl: 0,\n monthlyPnl: 0,\n};\n\nexport class RiskManager {\n private config: RiskConfig;\n private portfolio: PortfolioState;\n\n constructor(config?: Partial<RiskConfig>) {\n this.config = { ...DEFAULT_RISK_CONFIG, ...config };\n this.portfolio = { ...EMPTY_PORTFOLIO };\n }\n\n /** Base risk-per-trade fraction (e.g. 0.02 = 2%). Exposed so callers can\n * apply conviction / profile multipliers without duplicating the config. */\n getRiskPerTrade(): number {\n return this.config.riskPerTrade;\n }\n\n /**\n * Orca-inspired PnL-aware daily cap (see README / PR #223).\n *\n * Returns the maximum number of NEW position entries allowed today based\n * on realized daily PnL as a fraction of the start-of-day portfolio\n * value. Tiers (step function):\n *\n * >= +5% → 12 (\"hot hand\")\n * [ 0%, +5%) → 8\n * [-5%, 0%) → 5\n * [-15%, -5%) → 3\n * [-25%,-15%) → 1\n * < -25% → 0 (circuit breaker)\n *\n * `config.dailyCapOverride` short-circuits to a fixed value when set\n * (tests / aggressive mode). The start-of-day value is reconstructed\n * as `totalValue - dailyPnl` — same trick loop.ts uses to derive\n * dailyPnlPct for the judge.\n *\n * Orthogonal to `maxConcurrentTrades`: that limits simultaneous open\n * positions; this limits turnover per day.\n */\n getDynamicDailyCap(): number {\n // Override short-circuit\n if (\n this.config.dailyCapOverride !== undefined\n && Number.isFinite(this.config.dailyCapOverride)\n ) {\n return Math.max(0, Math.floor(this.config.dailyCapOverride));\n }\n\n const totalValue = Number.isFinite(this.portfolio.totalValue) ? this.portfolio.totalValue : 0;\n const dailyPnl = Number.isFinite(this.portfolio.dailyPnl) ? this.portfolio.dailyPnl : 0;\n // Reconstruct start-of-day value (mirrors loop.ts:691 logic). Fall\n // back to cash when totalValue is unset to avoid a false 0% baseline.\n const startOfDayValue = (totalValue - dailyPnl) > 0\n ? totalValue - dailyPnl\n : (Number.isFinite(this.portfolio.cash) ? this.portfolio.cash : 0);\n\n if (startOfDayValue <= 0) {\n // No baseline to measure against — fall back to the middle tier.\n return 5;\n }\n\n const pnlPct = dailyPnl / startOfDayValue;\n\n if (pnlPct >= 0.05) return 12;\n if (pnlPct >= 0) return 8;\n if (pnlPct >= -0.05) return 5;\n if (pnlPct >= -0.15) return 3;\n if (pnlPct >= -0.25) return 1;\n return 0; // circuit breaker — no new entries today\n }\n\n /** Check if we can open a new position (or pyramid into an existing one).\n * When `direction` is omitted, defaults to 'long' for backward compatibility.\n * An existing position with a different direction always rejects (no flips\n * via pyramid — flips must explicitly close the prior position first). */\n canOpenPosition(token: string, sizeUsd: number, direction: 'long' | 'short' = 'long'): { allowed: boolean; reason?: string } {\n // Check concurrent trades limit\n if (this.portfolio.positions.length >= this.config.maxConcurrentTrades) {\n return { allowed: false, reason: `Max concurrent trades (${this.config.maxConcurrentTrades}) reached` };\n }\n\n // Orca-inspired: PnL-aware daily entry cap (see README / PR #223).\n // Only applies to NEW positions — pyramid adds into an existing token\n // go through their own spacing / addCount guards below and do not\n // count toward the daily turnover budget.\n const isNewEntry = !this.portfolio.positions.find((p) => p.tokenId === token);\n if (isNewEntry) {\n const dailyCap = this.getDynamicDailyCap();\n const entriesToday = Number.isFinite(this.portfolio.dailyEntries)\n ? (this.portfolio.dailyEntries ?? 0)\n : 0;\n if (entriesToday >= dailyCap) {\n const totalValue = Number.isFinite(this.portfolio.totalValue) ? this.portfolio.totalValue : 0;\n const dailyPnl = Number.isFinite(this.portfolio.dailyPnl) ? this.portfolio.dailyPnl : 0;\n const startOfDayValue = (totalValue - dailyPnl) > 0 ? totalValue - dailyPnl : 0;\n const pnlPct = startOfDayValue > 0 ? (dailyPnl / startOfDayValue) * 100 : 0;\n return {\n allowed: false,\n reason: `Dynamic daily cap (${dailyCap}) reached — PnL ${pnlPct >= 0 ? '+' : ''}${pnlPct.toFixed(1)}%`,\n };\n }\n }\n\n // Check single position size limit\n const portfolioValue = this.portfolio.totalValue || this.portfolio.cash;\n if (portfolioValue > 0) {\n const positionPct = sizeUsd / portfolioValue;\n if (positionPct > this.config.maxSinglePosition) {\n return {\n allowed: false,\n reason: `Position size ${(positionPct * 100).toFixed(1)}% exceeds max ${(this.config.maxSinglePosition * 100).toFixed(0)}% per trade`,\n };\n }\n }\n\n // Check total portfolio risk by evaluating potential aggregate loss\n if (portfolioValue > 0) {\n const currentRiskExposure = this.portfolio.positions.reduce(\n (sum, p) => {\n // Estimate max loss per position using stop loss distance\n const maxLossPerPosition = Math.abs(p.entryPrice - p.stopLoss) * p.quantity;\n return sum + maxLossPerPosition;\n },\n 0,\n );\n\n // Estimate new position risk (assuming 8% stop loss)\n const newPositionRisk = sizeUsd * 0.08;\n const totalRiskExposure = currentRiskExposure + newPositionRisk;\n\n if (totalRiskExposure / portfolioValue > this.config.maxPortfolioRisk) {\n return {\n allowed: false,\n reason: `Total portfolio risk ${(totalRiskExposure / portfolioValue * 100).toFixed(1)}% would exceed max ${(this.config.maxPortfolioRisk * 100).toFixed(0)}%`,\n };\n }\n }\n\n // Check post-stop cooldown — prevent rapid re-entry after a stop loss\n const cooldowns = this.portfolio.stopCooldowns ?? {};\n const lastStop = cooldowns[token];\n if (lastStop !== undefined) {\n const elapsed = Date.now() - lastStop;\n if (elapsed < STOP_COOLDOWN_MS) {\n const remainHrs = ((STOP_COOLDOWN_MS - elapsed) / 3_600_000).toFixed(1);\n return { allowed: false, reason: `Stop cooldown active for ${token} (${remainHrs}h remaining)` };\n }\n }\n\n // Check per-token consecutive loss cooldown — prevent serial-loser re-entry.\n // After TOKEN_CONSEC_LOSS_LIMIT consecutive losses on a token, enforce a\n // 24h ban. Prevents the AAVE pattern (4 trades, 3 losses, -$144.75).\n const consecLosses = this.portfolio.tokenConsecLosses ?? {};\n const lossCooldowns = this.portfolio.tokenLossCooldowns ?? {};\n const tokenLosses = consecLosses[token] ?? 0;\n if (tokenLosses >= TOKEN_CONSEC_LOSS_LIMIT) {\n const lossCooldownStart = lossCooldowns[token];\n if (lossCooldownStart !== undefined) {\n const elapsed = Date.now() - lossCooldownStart;\n if (elapsed < TOKEN_LOSS_COOLDOWN_MS) {\n const remainHrs = ((TOKEN_LOSS_COOLDOWN_MS - elapsed) / 3_600_000).toFixed(1);\n return {\n allowed: false,\n reason: `Token loss cooldown: ${token} has ${tokenLosses} consecutive losses (${remainHrs}h remaining of 24h ban)`,\n };\n }\n }\n }\n\n // Check if we already have a position in this token.\n // Pyramiding (adding to a winning position) is allowed up to MAX_PYRAMID_ADDS\n // total adds, with at least PYRAMID_MIN_SPACING_MS between adds. The caller\n // (TradeExecutor) is responsible for halving the size each add and matching\n // the existing direction. If those preconditions aren't met, the executor\n // should not call canOpenPosition for an add.\n const existing = this.portfolio.positions.find((p) => p.tokenId === token);\n if (existing) {\n const existingSide = existing.side ?? 'long';\n if (existingSide !== direction) {\n return { allowed: false, reason: `Conflicting position in ${token} (existing ${existingSide}, signal ${direction}) — close first` };\n }\n const addCount = existing.addCount ?? 0;\n if (addCount >= MAX_PYRAMID_ADDS) {\n return { allowed: false, reason: `Pyramid cap reached for ${token} (${MAX_PYRAMID_ADDS} adds)` };\n }\n const lastAdd = existing.lastAddTimestamp ?? existing.entryTimestamp;\n const elapsed = Date.now() - lastAdd;\n if (elapsed < PYRAMID_MIN_SPACING_MS) {\n const remainHrs = ((PYRAMID_MIN_SPACING_MS - elapsed) / 3_600_000).toFixed(1);\n return { allowed: false, reason: `Pyramid spacing not met for ${token} (next add in ${remainHrs}h)` };\n }\n }\n\n // Check cash availability — compare margin requirement (33%) against cash,\n // not full notional. openPosition debits margin, not full notional.\n const MARGIN_FRACTION = 0.33;\n const marginRequired = sizeUsd * MARGIN_FRACTION;\n if (marginRequired > this.portfolio.cash) {\n return { allowed: false, reason: `Insufficient cash: need $${marginRequired.toFixed(2)} margin (${(MARGIN_FRACTION*100).toFixed(0)}% of $${sizeUsd.toFixed(0)}), have $${this.portfolio.cash.toFixed(2)}` };\n }\n\n // Check drawdown limits\n const drawdown = this.isDrawdownLimitHit();\n if (drawdown.paused) {\n return { allowed: false, reason: drawdown.message };\n }\n\n // Check correlated exposure by token category\n const TOKEN_CATEGORIES: Record<string, string> = {\n bitcoin: 'L1', ethereum: 'L1', solana: 'L1', avalanche: 'L1', cardano: 'L1',\n polkadot: 'L1', near: 'L1', cosmos: 'L1', sui: 'L1', aptos: 'L1',\n uniswap: 'DeFi', aave: 'DeFi', maker: 'DeFi', compound: 'DeFi', curve: 'DeFi',\n lido: 'DeFi', sushi: 'DeFi', pancakeswap: 'DeFi', jupiter: 'DeFi',\n arbitrum: 'L2', optimism: 'L2', polygon: 'L2', 'starknet': 'L2', base: 'L2',\n 'zksync': 'L2', mantle: 'L2',\n };\n\n const tokenCategory = TOKEN_CATEGORIES[token];\n if (tokenCategory && portfolioValue > 0) {\n const correlatedExposure = this.portfolio.positions\n .filter((p) => TOKEN_CATEGORIES[p.tokenId] === tokenCategory)\n .reduce((sum, p) => sum + p.quantity * p.currentPrice, 0);\n const newExposure = (correlatedExposure + sizeUsd) / portfolioValue;\n if (newExposure > this.config.maxCorrelatedExposure) {\n return {\n allowed: false,\n reason: `Correlated exposure for ${tokenCategory} would be ${(newExposure * 100).toFixed(1)}% (limit: ${(this.config.maxCorrelatedExposure * 100).toFixed(0)}%)`,\n };\n }\n }\n\n return { allowed: true };\n }\n\n /** Calculate position size using classic risk-based formula */\n calculatePositionSize(\n entryPrice: number,\n stopLossPrice: number,\n portfolioValue: number,\n maxRiskPercent?: number,\n ): { quantity: number; sizeUsd: number; riskUsd: number } {\n // Guard against invalid inputs\n if (entryPrice <= 0 || portfolioValue <= 0) {\n return { quantity: 0, sizeUsd: 0, riskUsd: 0 };\n }\n\n const riskPct = maxRiskPercent ?? this.config.riskPerTrade;\n let riskUsd = portfolioValue * riskPct;\n const riskPerUnit = Math.abs(entryPrice - stopLossPrice);\n\n if (riskPerUnit <= 0) {\n return { quantity: 0, sizeUsd: 0, riskUsd: 0 };\n }\n\n // positionSize = (portfolioValue * maxRiskPercent) / (entryPrice - stopLossPrice)\n let quantity = riskUsd / riskPerUnit;\n let sizeUsd = quantity * entryPrice;\n\n // Cap at max single position size FIRST — floor must not exceed cap.\n const maxSizeUsd = portfolioValue * this.config.maxSinglePosition;\n if (sizeUsd > maxSizeUsd) {\n sizeUsd = maxSizeUsd;\n quantity = maxSizeUsd / entryPrice;\n riskUsd = quantity * riskPerUnit;\n }\n\n // Minimum position floor: Hyperliquid requires ~$10+ notional per order.\n // Applied AFTER the cap so floor never exceeds maxSinglePosition.\n // Only scales up if vault can afford 2× the floor (safety buffer).\n const MIN_POSITION_USD = 15;\n const effectiveFloor = Math.min(MIN_POSITION_USD, maxSizeUsd);\n if (sizeUsd < effectiveFloor && portfolioValue >= MIN_POSITION_USD * 2) {\n sizeUsd = effectiveFloor;\n quantity = sizeUsd / entryPrice;\n riskUsd = quantity * riskPerUnit;\n }\n\n return { quantity, sizeUsd, riskUsd };\n }\n\n /** Check drawdown limits — returns true if trading should be paused */\n isDrawdownLimitHit(): { paused: boolean; level: 'daily' | 'weekly' | 'monthly' | null; message: string } {\n const portfolioValue = this.portfolio.totalValue || this.portfolio.cash;\n if (portfolioValue <= 0) {\n return { paused: false, level: null, message: 'No portfolio value set' };\n }\n\n const dailyPct = Math.abs(this.portfolio.dailyPnl) / portfolioValue;\n const weeklyPct = Math.abs(this.portfolio.weeklyPnl) / portfolioValue;\n const monthlyPct = Math.abs(this.portfolio.monthlyPnl) / portfolioValue;\n\n if (this.portfolio.dailyPnl < 0 && dailyPct >= this.config.dailyLossLimit) {\n return {\n paused: true,\n level: 'daily',\n message: `Daily loss limit hit: ${(dailyPct * 100).toFixed(1)}% (limit: ${(this.config.dailyLossLimit * 100).toFixed(0)}%)`,\n };\n }\n\n if (this.portfolio.weeklyPnl < 0 && weeklyPct >= this.config.weeklyLossLimit) {\n return {\n paused: true,\n level: 'weekly',\n message: `Weekly loss limit hit: ${(weeklyPct * 100).toFixed(1)}% (limit: ${(this.config.weeklyLossLimit * 100).toFixed(0)}%)`,\n };\n }\n\n if (this.portfolio.monthlyPnl < 0 && monthlyPct >= this.config.monthlyLossLimit) {\n return {\n paused: true,\n level: 'monthly',\n message: `Monthly loss limit hit: ${(monthlyPct * 100).toFixed(1)}% (limit: ${(this.config.monthlyLossLimit * 100).toFixed(0)}%)`,\n };\n }\n\n return { paused: false, level: null, message: 'Within limits' };\n }\n\n /**\n * Ratchet stop-losses each cycle based on current prices.\n * Applies three independent mechanisms, whichever gives the tightest stop:\n *\n * 1. Breakeven: if PnL% crosses +breakevenTriggerPct, move stop to entryPrice.\n * Locks in \"no loss\" once the trade is meaningfully in the money.\n *\n * 2. Orca-inspired HWM profit-lock (see README / PR #223): stepped table\n * keyed on \"gain from entry\". Each triggered tier locks a growing\n * fraction of the peak-to-date move:\n * LONG: stop = entry + (peakPrice - entry) * lockPct\n * SHORT: stop = entry - (entry - peakPrice) * lockPct\n * `peakPrice` tracks the best price observed since entry and is\n * updated on every call (max for LONG, min for SHORT). A retrace\n * followed by a new peak advances the lock — never resets it.\n *\n * 3. Percent-trail: stop tracks currentPrice × (1 ± trailingStopPct).\n * Continuous trail — tracks new highs (LONG) / lows (SHORT).\n *\n * All mechanisms respect the \"stops never loosen\" invariant.\n * `peakPrice` is updated even when no stop-change fires (so future\n * cycles see the ratchet). Returns a new array — does not mutate input.\n *\n * Legacy positions loaded from portfolio.json without `peakPrice`\n * default to `entryPrice` (safest — never triggers a tier on the\n * first cycle after upgrade until price actually advances).\n */\n updateTrailingStops(positions: Position[]): Position[] {\n return positions.map((pos) => {\n if (pos.currentPrice <= 0 || pos.entryPrice <= 0) return pos;\n\n const isShort = pos.side === 'short';\n\n // Update peak-price HWM. For LONG, peak = max observed price.\n // For SHORT, peak = min observed price (most favorable for a short).\n // Legacy positions without `peakPrice` seed from entryPrice — this\n // is the safer default: on the first post-upgrade cycle, no HWM\n // tier will fire until price actually moves beyond entry.\n const priorPeak = Number.isFinite(pos.peakPrice) ? (pos.peakPrice as number) : pos.entryPrice;\n const newPeak = isShort\n ? Math.min(priorPeak, pos.currentPrice)\n : Math.max(priorPeak, pos.currentPrice);\n\n // Direction-aware PnL from entry\n const pnlPct = isShort\n ? (pos.entryPrice - pos.currentPrice) / (pos.entryPrice || 1)\n : (pos.currentPrice - pos.entryPrice) / (pos.entryPrice || 1);\n let newStop = pos.stopLoss;\n\n if (isShort) {\n // For SHORTS: stops sit ABOVE price. \"Tighter\" = LOWER.\n\n // 1. Breakeven: move stop down to entry\n if (this.config.breakevenTriggerPct > 0 && pnlPct >= this.config.breakevenTriggerPct) {\n newStop = Math.min(newStop, pos.entryPrice);\n }\n\n // 2. HWM profit-lock. For shorts, the peak is the lowest price.\n // stop = entry - (entry - peak) * lockPct (peak <= entry)\n // Iterate tiers — highest-triggered tier wins.\n for (const step of this.config.profitLockSteps) {\n if (pnlPct >= step.trigger) {\n const lockedStop = pos.entryPrice - (pos.entryPrice - newPeak) * step.lockPct;\n newStop = Math.min(newStop, lockedStop);\n }\n }\n\n // 3. Percent-trail: track new lows\n if (this.config.trailingStopPct > 0) {\n const trailStop = pos.currentPrice * (1 + this.config.trailingStopPct);\n newStop = Math.min(newStop, trailStop);\n }\n\n if (newStop < pos.stopLoss || newPeak !== priorPeak) {\n return {\n ...pos,\n peakPrice: newPeak,\n stopLoss: newStop < pos.stopLoss ? newStop : pos.stopLoss,\n trailingStop: newStop < pos.stopLoss ? newStop : pos.trailingStop,\n };\n }\n } else {\n // For LONGS: stops sit BELOW price. \"Tighter\" = HIGHER.\n\n // 1. Breakeven\n if (this.config.breakevenTriggerPct > 0 && pnlPct >= this.config.breakevenTriggerPct) {\n newStop = Math.max(newStop, pos.entryPrice);\n }\n\n // 2. HWM profit-lock.\n // stop = entry + (peak - entry) * lockPct (peak >= entry)\n for (const step of this.config.profitLockSteps) {\n if (pnlPct >= step.trigger) {\n const lockedStop = pos.entryPrice + (newPeak - pos.entryPrice) * step.lockPct;\n newStop = Math.max(newStop, lockedStop);\n }\n }\n\n // 3. Percent-trail\n if (this.config.trailingStopPct > 0) {\n const trailStop = pos.currentPrice * (1 - this.config.trailingStopPct);\n newStop = Math.max(newStop, trailStop);\n }\n\n if (newStop > pos.stopLoss || newPeak !== priorPeak) {\n return {\n ...pos,\n peakPrice: newPeak,\n stopLoss: newStop > pos.stopLoss ? newStop : pos.stopLoss,\n trailingStop: newStop > pos.stopLoss ? newStop : pos.trailingStop,\n };\n }\n }\n return pos;\n });\n }\n\n /** Update trailing stop losses using ATR values */\n updateStopLosses(positions: Position[], atrValues: Record<string, number>): Position[] {\n return positions.map((pos) => {\n const atr = atrValues[pos.tokenId];\n if (atr === undefined || atr <= 0) return pos;\n\n const trailingDistance = atr * this.config.trailingStopAtr;\n const currentTrailing = pos.trailingStop ?? pos.stopLoss;\n\n if (pos.side === 'short') {\n // For SHORTS: stop sits ABOVE current price. Tighten only if new\n // candidate is LOWER than the current trailing stop.\n const newTrailingStop = pos.currentPrice + trailingDistance;\n if (newTrailingStop < currentTrailing) {\n return {\n ...pos,\n trailingStop: newTrailingStop,\n // Also update hard stop if trailing is tighter (lower)\n stopLoss: Math.min(pos.stopLoss, newTrailingStop),\n };\n }\n return pos;\n }\n\n // LONGS: stop sits BELOW current price. Only move trailing stop up.\n const newTrailingStop = pos.currentPrice - trailingDistance;\n if (newTrailingStop > currentTrailing) {\n return {\n ...pos,\n trailingStop: newTrailingStop,\n // Also update hard stop if trailing is higher\n stopLoss: Math.max(pos.stopLoss, newTrailingStop),\n };\n }\n\n return pos;\n });\n }\n\n /** Check if any positions should be closed */\n checkExits(\n positions: Position[],\n currentPrices: Record<string, number>,\n ): { toClose: Position[]; reasons: Record<string, string> } {\n const toClose: Position[] = [];\n const reasons: Record<string, string> = {};\n\n for (const pos of positions) {\n const price = currentPrices[pos.tokenId];\n if (price === undefined) continue;\n\n const updatedPos = { ...pos, currentPrice: price };\n const isShort = pos.side === 'short';\n\n // PnL calculation — direction-aware\n const pnlPercent = isShort\n ? (pos.entryPrice - price) / (pos.entryPrice || 1)\n : (price - pos.entryPrice) / (pos.entryPrice || 1);\n\n // Hard stop loss check (direction-aware via pnlPercent)\n if (pnlPercent <= -this.config.hardStopPercent) {\n toClose.push(updatedPos);\n reasons[pos.tokenId] = `Hard stop hit: ${(pnlPercent * 100).toFixed(1)}% loss (limit: -${(this.config.hardStopPercent * 100).toFixed(0)}%)`;\n continue;\n }\n\n // Stop loss check — for shorts, stop is ABOVE entry (price >= stopLoss)\n // for longs, stop is BELOW entry (price <= stopLoss)\n const stopHit = isShort ? price >= pos.stopLoss : price <= pos.stopLoss;\n if (stopHit) {\n toClose.push(updatedPos);\n reasons[pos.tokenId] = `Stop loss hit at $${pos.stopLoss.toFixed(4)} (price: $${price.toFixed(4)})`;\n continue;\n }\n\n // Trailing stop check — same direction logic as stop loss\n if (pos.trailingStop !== undefined) {\n const trailHit = isShort ? price >= pos.trailingStop : price <= pos.trailingStop;\n if (trailHit) {\n toClose.push(updatedPos);\n reasons[pos.tokenId] = `Trailing stop hit at $${pos.trailingStop.toFixed(4)} (price: $${price.toFixed(4)})`;\n continue;\n }\n }\n\n // Take profit check — for shorts, TP is BELOW entry (price <= takeProfit)\n // for longs, TP is ABOVE entry (price >= takeProfit)\n const tpHit = isShort ? price <= pos.takeProfit : price >= pos.takeProfit;\n if (tpHit) {\n toClose.push(updatedPos);\n reasons[pos.tokenId] = `Take profit hit at $${pos.takeProfit.toFixed(4)} (price: $${price.toFixed(4)})`;\n continue;\n }\n\n // Time-based exit: close after 48h if PnL is flat (<1%)\n const holdingHours = (Date.now() - pos.entryTimestamp) / (1000 * 60 * 60);\n // Autoresearch (Apr 24): 96h→84h — kill dead positions sooner for capital recycling.\n if (holdingHours > 84 && pnlPercent < 0.01 && pnlPercent > -0.01) {\n toClose.push(updatedPos);\n reasons[pos.tokenId] = `Time stop: held ${(holdingHours / 24).toFixed(1)} days with only ${(pnlPercent * 100).toFixed(1)}% PnL`;\n continue;\n }\n }\n\n return { toClose, reasons };\n }\n\n /** Update portfolio state */\n updatePortfolio(portfolio: Partial<PortfolioState>): void {\n this.portfolio = { ...this.portfolio, ...portfolio };\n }\n\n /** Get current risk exposure summary */\n getRiskSummary(): string {\n const lines: string[] = [];\n const pv = this.portfolio.totalValue || this.portfolio.cash;\n\n lines.push(chalk.bold('Risk Summary'));\n lines.push(chalk.dim('─'.repeat(40)));\n lines.push(`Portfolio Value: $${pv.toFixed(2)}`);\n lines.push(`Cash: $${this.portfolio.cash.toFixed(2)}`);\n lines.push(`Open Positions: ${this.portfolio.positions.length}/${this.config.maxConcurrentTrades}`);\n\n const totalExposure = this.portfolio.positions.reduce(\n (sum, p) => sum + p.quantity * p.currentPrice,\n 0,\n );\n const exposurePct = pv > 0 ? (totalExposure / pv) * 100 : 0;\n lines.push(`Total Exposure: $${totalExposure.toFixed(2)} (${exposurePct.toFixed(1)}%)`);\n\n const totalUnrealizedPnl = this.portfolio.positions.reduce((sum, p) => sum + p.pnlUsd, 0);\n const pnlColor = totalUnrealizedPnl >= 0 ? chalk.green : chalk.red;\n lines.push(`Unrealized PnL: ${pnlColor('$' + totalUnrealizedPnl.toFixed(2))}`);\n\n lines.push(chalk.dim('─'.repeat(40)));\n lines.push(`Daily PnL: ${this.portfolio.dailyPnl >= 0 ? '+' : ''}$${this.portfolio.dailyPnl.toFixed(2)}`);\n lines.push(`Weekly PnL: ${this.portfolio.weeklyPnl >= 0 ? '+' : ''}$${this.portfolio.weeklyPnl.toFixed(2)}`);\n lines.push(`Monthly PnL: ${this.portfolio.monthlyPnl >= 0 ? '+' : ''}$${this.portfolio.monthlyPnl.toFixed(2)}`);\n\n const drawdown = this.isDrawdownLimitHit();\n if (drawdown.paused) {\n lines.push(chalk.red(`TRADING PAUSED: ${drawdown.message}`));\n }\n\n return lines.join('\\n');\n }\n}\n","/**\n * Reporting module — formats cycle results, portfolio summaries, trade alerts, and metrics.\n */\n\nimport chalk from 'chalk';\nimport type { CycleResult } from './loop.js';\nimport type { PortfolioState, Position } from './risk.js';\nimport type { TradeRecord } from './portfolio.js';\n\nexport class Reporter {\n /** Format a cycle result as a nicely-formatted console report */\n formatCycleReport(result: CycleResult): string {\n const lines: string[] = [];\n const ts = new Date(result.timestamp).toLocaleTimeString();\n\n lines.push('');\n lines.push(chalk.bold(` ┌─ Cycle #${result.cycleNumber} ─ ${ts} ─────────────────────────────┐`));\n lines.push(` │ Duration: ${result.duration}ms | Tokens analyzed: ${result.tokensAnalyzed}`);\n lines.push(` │ Trades executed: ${result.tradesExecuted} | Exits processed: ${result.exitsProcessed}`);\n lines.push(chalk.dim(' ├──────────────────────────────────────────────────┤'));\n\n // Signals\n if (result.signals.length > 0) {\n lines.push(chalk.bold(' │ Signals:'));\n for (const sig of result.signals) {\n const color = sig.action.includes('BUY')\n ? chalk.green\n : sig.action.includes('SELL')\n ? chalk.red\n : chalk.yellow;\n const scoreStr = sig.score >= 0 ? `+${sig.score.toFixed(3)}` : sig.score.toFixed(3);\n lines.push(` │ ${sig.token.padEnd(14)} ${color(sig.action.padEnd(12))} ${scoreStr}`);\n }\n } else {\n lines.push(' │ No actionable signals this cycle.');\n }\n\n lines.push(chalk.dim(' ├──────────────────────────────────────────────────┤'));\n\n // Portfolio — show total PnL% since inception + realized (drawdown-gate driver) + unrealized (mark-to-market).\n const realized = result.dailyRealizedPnl;\n const unrealized = result.unrealizedPnl;\n const total = result.totalPnlUsd;\n const totalPct = result.totalPnlPct;\n const fmt = (v: number) => (v >= 0 ? '+$' : '-$') + Math.abs(v).toFixed(2);\n const fmtPct = (v: number) => (v >= 0 ? '+' : '') + (v * 100).toFixed(2) + '%';\n const rColor = realized >= 0 ? chalk.green : chalk.red;\n const uColor = unrealized >= 0 ? chalk.green : chalk.red;\n const tColor = total >= 0 ? chalk.green : chalk.red;\n lines.push(` │ Portfolio: $${result.portfolioValue.toFixed(2)} Total: ${tColor(fmt(total) + ' (' + fmtPct(totalPct) + ')')}`);\n lines.push(` │ Realized: ${rColor(fmt(realized))} Unrealized: ${uColor(fmt(unrealized))}`);\n\n // Errors\n if (result.errors.length > 0) {\n lines.push(chalk.dim(' ├──────────────────────────────────────────────────┤'));\n lines.push(chalk.red(` │ Errors (${result.errors.length}):`));\n for (const err of result.errors.slice(0, 3)) {\n lines.push(chalk.red(` │ ${err.slice(0, 60)}`));\n }\n }\n\n lines.push(chalk.bold(' └──────────────────────────────────────────────────┘'));\n lines.push('');\n\n return lines.join('\\n');\n }\n\n /** Format portfolio state as a detailed summary */\n formatPortfolioReport(state: PortfolioState): string {\n const lines: string[] = [];\n\n lines.push('');\n lines.push(chalk.bold(' Portfolio Summary'));\n lines.push(chalk.dim(' ' + '═'.repeat(60)));\n lines.push(` Total Value: $${state.totalValue.toFixed(2)}`);\n lines.push(` Cash: $${state.cash.toFixed(2)}`);\n lines.push(` Positions: ${state.positions.length}`);\n lines.push('');\n\n if (state.positions.length > 0) {\n lines.push(chalk.bold(' Open Positions:'));\n lines.push(chalk.dim(` ${'Symbol'.padEnd(10)} ${'Qty'.padEnd(12)} ${'Entry'.padEnd(12)} ${'Current'.padEnd(12)} ${'PnL %'.padEnd(10)} PnL $`));\n lines.push(chalk.dim(' ' + '─'.repeat(68)));\n\n for (const p of state.positions) {\n const pnlColor = p.pnlUsd >= 0 ? chalk.green : chalk.red;\n const pctStr = `${(p.pnlPercent * 100).toFixed(1)}%`;\n const usdStr = `${p.pnlUsd >= 0 ? '+' : ''}$${p.pnlUsd.toFixed(2)}`;\n lines.push(\n ` ${p.symbol.padEnd(10)} ${p.quantity.toFixed(4).padEnd(12)} $${p.entryPrice.toFixed(2).padEnd(11)} $${p.currentPrice.toFixed(2).padEnd(11)} ${pnlColor(pctStr.padEnd(10))} ${pnlColor(usdStr)}`,\n );\n }\n lines.push('');\n }\n\n const dailyColor = state.dailyPnl >= 0 ? chalk.green : chalk.red;\n const weeklyColor = state.weeklyPnl >= 0 ? chalk.green : chalk.red;\n const monthlyColor = state.monthlyPnl >= 0 ? chalk.green : chalk.red;\n\n lines.push(` Daily PnL: ${dailyColor((state.dailyPnl >= 0 ? '+' : '') + '$' + state.dailyPnl.toFixed(2))}`);\n lines.push(` Weekly PnL: ${weeklyColor((state.weeklyPnl >= 0 ? '+' : '') + '$' + state.weeklyPnl.toFixed(2))}`);\n lines.push(` Monthly PnL: ${monthlyColor((state.monthlyPnl >= 0 ? '+' : '') + '$' + state.monthlyPnl.toFixed(2))}`);\n lines.push('');\n\n return lines.join('\\n');\n }\n\n /** Format a trade execution alert */\n formatTradeAlert(trade: {\n token: string;\n side: string;\n price: number;\n amount: number;\n strategy: string;\n }): string {\n const emoji = trade.side === 'buy' ? '🟢' : '🔴';\n const sideStr = trade.side.toUpperCase();\n const lines: string[] = [];\n\n lines.push(`${emoji} ${sideStr} ${trade.token.toUpperCase()}`);\n lines.push(` Price: $${trade.price.toFixed(4)}`);\n lines.push(` Amount: $${trade.amount.toFixed(2)}`);\n lines.push(` Strategy: ${trade.strategy}`);\n lines.push(` Time: ${new Date().toISOString()}`);\n\n return lines.join('\\n');\n }\n\n /** Format performance metrics */\n formatMetrics(metrics: {\n totalTrades: number;\n winRate: number;\n avgPnlPercent: number;\n totalPnlUsd: number;\n sharpeRatio: number;\n maxDrawdown: number;\n bestTrade: TradeRecord;\n worstTrade: TradeRecord;\n }): string {\n const lines: string[] = [];\n\n lines.push('');\n lines.push(chalk.bold(' Performance Metrics'));\n lines.push(chalk.dim(' ' + '═'.repeat(50)));\n\n lines.push(` Total Trades: ${metrics.totalTrades}`);\n\n // Win rate with bar\n const winPct = (metrics.winRate * 100).toFixed(1);\n const winBarLen = Math.round(metrics.winRate * 20);\n const winBar = chalk.green('█'.repeat(winBarLen)) + chalk.dim('░'.repeat(20 - winBarLen));\n const winColor = metrics.winRate >= 0.5 ? chalk.green : chalk.red;\n lines.push(` Win Rate: ${winBar} ${winColor(winPct + '%')}`);\n\n // Avg PnL\n const avgColor = metrics.avgPnlPercent >= 0 ? chalk.green : chalk.red;\n lines.push(` Avg PnL: ${avgColor((metrics.avgPnlPercent * 100).toFixed(2) + '%')}`);\n\n // Total PnL\n const totalColor = metrics.totalPnlUsd >= 0 ? chalk.green : chalk.red;\n lines.push(` Total PnL: ${totalColor('$' + metrics.totalPnlUsd.toFixed(2))}`);\n\n // Sharpe\n const sharpeColor = metrics.sharpeRatio >= 1 ? chalk.green : metrics.sharpeRatio >= 0 ? chalk.yellow : chalk.red;\n lines.push(` Sharpe Ratio: ${sharpeColor(metrics.sharpeRatio.toFixed(2))}`);\n\n // Max Drawdown\n const ddBar = chalk.red('█'.repeat(Math.round(Math.min(metrics.maxDrawdown, 1) * 20))) +\n chalk.dim('░'.repeat(20 - Math.round(Math.min(metrics.maxDrawdown, 1) * 20)));\n lines.push(` Max Drawdown: ${ddBar} ${chalk.red((metrics.maxDrawdown * 100).toFixed(1) + '%')}`);\n\n lines.push('');\n\n // Best/worst trade\n if (metrics.bestTrade?.tokenId) {\n const best = metrics.bestTrade;\n lines.push(chalk.green(` Best Trade: ${best.symbol || best.tokenId} ${best.pnlUsd >= 0 ? '+' : ''}$${best.pnlUsd.toFixed(2)} (${(best.pnlPercent * 100).toFixed(1)}%)`));\n }\n if (metrics.worstTrade?.tokenId) {\n const worst = metrics.worstTrade;\n lines.push(chalk.red(` Worst Trade: ${worst.symbol || worst.tokenId} ${worst.pnlUsd >= 0 ? '+' : ''}$${worst.pnlUsd.toFixed(2)} (${(worst.pnlPercent * 100).toFixed(1)}%)`));\n }\n\n lines.push('');\n\n return lines.join('\\n');\n }\n}\n","/**\n * Per-token last-known-good-price cache with two validation rules:\n *\n * 1. **Floor**: reject prices below PRICE_FLOOR. A zero or near-zero tick\n * collapses risk-per-unit (entry - stop) and produces absurd position\n * sizes; any division against it risks Infinity/NaN propagation.\n *\n * 2. **Delta cap**: if a prior anchor exists and the new tick deviates from\n * it by more than MAX_DELTA_PCT, reject the new price and retain the\n * prior anchor. Genuine moves beyond 20% intra-cycle are rare; an\n * orders-of-magnitude typo is catastrophic.\n *\n * Anchors older than STALE_ANCHOR_MS are treated as absent — a fresh tick\n * is more useful than a day-old reference.\n *\n * Persistence mirrors {@link HyperliquidProvider.persistOiCache}: atomic\n * tmp-rename writes, fire-and-forget, errors logged as warnings so a slow\n * disk never blocks the signal hot path.\n */\n\nimport { mkdir, readFile, writeFile, rename } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\n\n/** Prices below this are rejected outright (USD). */\nexport const PRICE_FLOOR = 1e-9;\n\n/** Max allowed relative deviation from the prior anchor (fraction, 0.20 = 20%). */\nexport const MAX_DELTA_PCT = 0.20;\n\n/** Anchors older than this are ignored — better a fresh tick than a stale anchor. */\nexport const STALE_ANCHOR_MS = 2 * 60 * 60 * 1000; // 2 hours\n\ninterface AnchorEntry {\n price: number;\n timestamp: number;\n}\n\nexport type ValidatorResult =\n | { ok: true; price: number }\n | { ok: false; reason: string };\n\nexport class PriceValidator {\n private anchors = new Map<string, AnchorEntry>();\n private cacheDir: string;\n private cacheFile: string;\n private loaded = false;\n private loadPromise: Promise<void> | null = null;\n /** Monotonic counter so concurrent persist() calls don't collide on the tmp filename. */\n private persistSeq = 0;\n /** Serializes persist() writes. Without this, two back-to-back check()\n * calls fire two parallel mkdir → writeFile → rename chains; whichever\n * rename lands last wins, and that's not necessarily the latest snapshot.\n * Chaining onto this promise forces them to run in scheduling order so\n * the final on-disk state always reflects the most recent check(). */\n private persistChain: Promise<void> = Promise.resolve();\n /** Override Date.now() for tests. */\n private clock: () => number;\n\n constructor(opts?: { cacheDir?: string; clock?: () => number }) {\n this.cacheDir = opts?.cacheDir ?? join(homedir(), '.sherwood', 'agent', 'cache');\n this.cacheFile = join(this.cacheDir, 'price-validator.json');\n this.clock = opts?.clock ?? (() => Date.now());\n // Eager load so first check() finds the anchors populated.\n this.loadPromise = this.load();\n }\n\n /** Load anchors from disk. Called once at construction. Never throws. */\n private async load(): Promise<void> {\n try {\n const raw = await readFile(this.cacheFile, 'utf-8');\n const parsed = JSON.parse(raw) as Record<string, AnchorEntry>;\n if (parsed && typeof parsed === 'object') {\n for (const [token, entry] of Object.entries(parsed)) {\n if (\n entry\n && Number.isFinite(entry.price)\n && Number.isFinite(entry.timestamp)\n && entry.price >= PRICE_FLOOR\n ) {\n this.anchors.set(token, entry);\n }\n }\n }\n } catch {\n // No file, corrupt JSON, or permission error — start empty.\n } finally {\n this.loaded = true;\n }\n }\n\n /** Awaited by tests that need a deterministic load — not required for normal use. */\n async ensureLoaded(): Promise<void> {\n if (this.loaded) return;\n if (this.loadPromise) await this.loadPromise;\n }\n\n /**\n * Validate a new tick for `tokenId`. On ok, the anchor is updated and a\n * persist is queued fire-and-forget. On reject, the prior anchor is kept\n * untouched.\n */\n check(tokenId: string, newPrice: number): ValidatorResult {\n // Floor + finite guard — covers 0, negatives, NaN, Infinity.\n if (!Number.isFinite(newPrice) || newPrice < PRICE_FLOOR) {\n return { ok: false, reason: `price ${newPrice} below floor ${PRICE_FLOOR}` };\n }\n\n const now = this.clock();\n const prior = this.anchors.get(tokenId);\n const priorIsFresh = prior !== undefined && now - prior.timestamp < STALE_ANCHOR_MS;\n\n if (priorIsFresh) {\n const oldPrice = prior!.price;\n const delta = Math.abs(newPrice - oldPrice) / oldPrice;\n if (delta > MAX_DELTA_PCT) {\n return {\n ok: false,\n reason:\n `delta ${(delta * 100).toFixed(1)}% from anchor $${oldPrice} to $${newPrice} exceeds ${(MAX_DELTA_PCT * 100).toFixed(0)}%`,\n };\n }\n }\n\n // Accept: update anchor and queue a persist.\n this.anchors.set(tokenId, { price: newPrice, timestamp: now });\n this.persist();\n return { ok: true, price: newPrice };\n }\n\n /**\n * Persist anchors to disk via atomic tmp-rename. Fire-and-forget — callers\n * do NOT await. Errors are logged as warnings.\n */\n private persist(): void {\n const snapshot: Record<string, AnchorEntry> = {};\n for (const [token, entry] of this.anchors.entries()) {\n snapshot[token] = entry;\n }\n // Unique tmp path per persist() call — keeps the tmp filenames distinct\n // even though the chain serializes their actual writes.\n const seq = ++this.persistSeq;\n const tmp = `${this.cacheFile}.tmp.${process.pid}.${seq}`;\n this.persistChain = this.persistChain.then(async () => {\n try {\n await mkdir(this.cacheDir, { recursive: true });\n await writeFile(tmp, JSON.stringify(snapshot), 'utf-8');\n await rename(tmp, this.cacheFile);\n } catch (err) {\n console.warn(`[price-validator] persist failed: ${(err as Error).message}`);\n }\n });\n }\n}\n","/**\n * Simple backtesting framework — replays historical data through strategies.\n */\n\nimport chalk from 'chalk';\nimport { CoinGeckoProvider } from '../providers/data/coingecko.js';\nimport { SentimentProvider } from '../providers/data/sentiment.js';\nimport { getLatestSignals, calculateATR } from './technical.js';\nimport type { Candle } from './technical.js';\nimport { runStrategies } from './strategies/index.js';\nimport type { StrategyContext } from './strategies/types.js';\nimport { computeTradeDecision } from './scoring.js';\nimport type { TradeDecision } from './scoring.js';\nimport { MarketRegimeDetector } from './regime.js';\nimport type { MarketRegime } from './regime.js';\nimport { SignalSmoother, MemorySmootherStorage, DEFAULT_SMOOTHER_CONFIG } from './signal-smoother.js';\n\nexport interface BacktestConfig {\n tokenId: string;\n startDate: string; // ISO date\n endDate: string;\n initialCapital: number;\n strategies: string[]; // strategy names to test\n cycle: '1h' | '4h' | '1d';\n verbose?: boolean; // show detailed decision logs\n /** When true, classify regime per-candle and apply regime-conditional thresholds.\n * Default false → flat ±0.3/±0.6 thresholds (matches old backtest behavior). */\n useRegime?: boolean;\n /** Trailing-stop percent (e.g. 0.05 = 5%). When set and a long position\n * exists, exit if price drops trailingStopPct from the high-water mark\n * since entry. Independent of SELL signal — stops fire first if hit.\n * Default undefined → SELL-signal-only exit. */\n trailingStopPct?: number;\n /** When true, smooth fast/noisy signals with rolling window (in-memory\n * per-simulation, no disk IO). Default false. */\n smoothFastSignals?: boolean;\n /** Override scoring weights (for calibration sweeps). */\n customWeights?: import('./scoring.js').ScoringWeights;\n /** Override BUY threshold (applied symmetrically to STRONG_BUY as buy+0.3).\n * Used by calibrator to sweep threshold values. When set, takes precedence\n * over regime-based thresholds. */\n buyThreshold?: number;\n /** Override SELL threshold (applied symmetrically to STRONG_SELL as sell-0.3). */\n sellThreshold?: number;\n}\n\nexport interface BacktestTrade {\n entryDate: string;\n exitDate: string;\n entryPrice: number;\n exitPrice: number;\n pnlPercent: number;\n signal: string;\n strategies?: string[]; // Which strategies contributed to this trade\n}\n\nexport interface BacktestResult {\n config: BacktestConfig;\n totalReturn: number;\n totalReturnPercent: number;\n sharpeRatio: number;\n maxDrawdown: number;\n winRate: number;\n totalTrades: number;\n trades: BacktestTrade[];\n equityCurve: Array<{ date: string; value: number }>;\n}\n\nexport interface WalkForwardConfig {\n tokenId: string;\n totalDays: number; // e.g. 180\n trainWindow: number; // e.g. 90 days\n testWindow: number; // e.g. 30 days\n stepSize: number; // e.g. 30 days (how much to advance each fold)\n capital: number;\n strategies: string[];\n /** Forwarded to per-fold Backtester. Default false. */\n useRegime?: boolean;\n /** Forwarded to per-fold Backtester. Default undefined (no trailing stop). */\n trailingStopPct?: number;\n /** Forwarded to per-fold Backtester. Default false. */\n smoothFastSignals?: boolean;\n}\n\nexport interface WalkForwardResult {\n folds: Array<{\n trainPeriod: { from: Date; to: Date };\n testPeriod: { from: Date; to: Date };\n trainSharpe: number;\n testSharpe: number;\n trainReturn: number;\n testReturn: number;\n testMaxDrawdown: number;\n tradesInTest: number;\n }>;\n aggregateTestSharpe: number;\n aggregateTestReturn: number;\n avgTestMaxDrawdown: number;\n overfit: boolean; // true if train sharpe >> test sharpe\n overfitRatio: number; // train sharpe / test sharpe\n}\n\n// Strategies that can work with candles-only data in backtest mode\n// Strategies that work with candle data + F&G available in the backtester.\n// Previously limited to 3 candle-only strategies — expanded to include all\n// that fire with the data the backtester provides (candles + fearAndGreed).\nconst BACKTEST_STRATEGIES = [\n 'breakoutOnChain', // 95% fire rate — candle-based\n 'multiTimeframe', // 78% fire rate — candle-based\n 'sentimentContrarian', // 100% fire rate — uses fearAndGreed from context\n 'momentum', // computed from candles directly in the backtest loop\n 'fundingRate', // fires when ctx.fundingRateData is set (won't fire in backtest — ok)\n 'dexFlow', // fires when ctx.dexData is set (won't fire in backtest — ok)\n 'hyperliquidFlow', // fires when ctx.hyperliquidData is set (won't fire — ok)\n];\n\nexport class Backtester {\n private config: BacktestConfig;\n private cg: CoinGeckoProvider;\n private sentimentProvider: SentimentProvider;\n\n constructor(config: BacktestConfig) {\n this.config = config;\n this.cg = new CoinGeckoProvider();\n this.sentimentProvider = new SentimentProvider();\n }\n\n /** Run backtest using historical data from CoinGecko. */\n async run(): Promise<BacktestResult> {\n const data = await this.fetchData();\n return this.simulate(data.candles, data.fearAndGreedData);\n }\n\n /**\n * Fetch historical OHLC + Fear & Greed data for the configured token/date range.\n * Extracted so callers (e.g. the calibrator) can fetch once and replay many\n * configurations against the same dataset without re-hitting CoinGecko.\n */\n async fetchData(): Promise<{ candles: Candle[]; fearAndGreedData: Record<string, number> }> {\n // 1. Fetch historical OHLC data\n const startMs = new Date(this.config.startDate).getTime();\n const endMs = new Date(this.config.endDate).getTime();\n const totalDays = Math.ceil((endMs - startMs) / (24 * 60 * 60 * 1000));\n\n // Use market_chart endpoint (daily prices) — works for any range up to 365 days.\n // OHLC endpoint only returns 12 candles for >30 day ranges on free tier.\n const cgDays = Math.min(Math.max(totalDays, 1), 365);\n const marketData = await this.cg.getMarketData(this.config.tokenId, cgDays);\n\n if (!marketData?.prices?.length) {\n throw new Error(`No price data returned from CoinGecko for ${this.config.tokenId}`);\n }\n\n // Build daily candles from market_chart prices + volumes\n const prices: number[][] = marketData.prices;\n const volumes: number[][] = marketData.total_volumes ?? [];\n\n // Group prices and volumes by day to create OHLCV candles\n const dayMap = new Map<string, { prices: number[]; totalVolume: number; timestamp: number }>();\n\n // Process prices first to establish daily buckets\n for (const [ts, price] of prices) {\n if (ts! < startMs || ts! > endMs) continue;\n const dayKey = new Date(ts!).toISOString().slice(0, 10);\n if (!dayMap.has(dayKey)) {\n dayMap.set(dayKey, { prices: [], totalVolume: 0, timestamp: ts! });\n }\n dayMap.get(dayKey)!.prices.push(price!);\n }\n\n // Sum volumes for each day (instead of averaging)\n for (const [ts, vol] of volumes) {\n if (ts! < startMs || ts! > endMs) continue;\n const dayKey = new Date(ts!).toISOString().slice(0, 10);\n const dayData = dayMap.get(dayKey);\n if (dayData) {\n dayData.totalVolume += vol!;\n }\n }\n\n const allCandles: Candle[] = [...dayMap.entries()]\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([, day]) => {\n const p = day.prices;\n return {\n timestamp: day.timestamp,\n open: p[0]!,\n high: Math.max(...p),\n low: Math.min(...p),\n close: p[p.length - 1]!,\n volume: day.totalVolume, // Use summed volume for the day\n };\n });\n\n if (allCandles.length < 30) {\n throw new Error(`Insufficient data: only ${allCandles.length} candles in date range (need 30+)`);\n }\n\n // 1.5. Fetch Fear & Greed historical data for sentiment-contrarian strategy\n let fearAndGreedData: Record<string, number> = {};\n try {\n const fgData = await this.sentimentProvider.getFearAndGreed();\n for (const entry of fgData) {\n // Convert timestamp to date string and store\n const date = new Date(Number(entry.timestamp) * 1000).toISOString().split('T')[0]!;\n fearAndGreedData[date] = entry.value;\n }\n if (this.config.verbose && Object.keys(fearAndGreedData).length > 0) {\n console.log(chalk.dim(` Loaded ${Object.keys(fearAndGreedData).length} Fear & Greed historical data points`));\n }\n } catch (error) {\n if (this.config.verbose) {\n console.log(chalk.yellow(` Warning: Could not fetch Fear & Greed data: ${(error as Error).message}`));\n }\n }\n\n return { candles: allCandles, fearAndGreedData };\n }\n\n /**\n * Pre-compute per-candle strategy signals and regime classification.\n * Called once per token by the calibrator; result is fed back into\n * `simulate(..., precomputed)` to skip the network-bound strategy stack.\n *\n * Filters signals to BACKTEST_STRATEGIES (the calibrator does not vary\n * `config.strategies`). Regime is always computed and returned —\n * `simulate()` will only consume it when `config.useRegime` is set.\n */\n async precomputeSignals(\n allCandles: Candle[],\n fearAndGreedData: Record<string, number>,\n ): Promise<Array<{ signals: import('./scoring.js').Signal[]; regime: MarketRegime } | null>> {\n const stepSize = this.config.cycle === '1h' ? 1 : this.config.cycle === '4h' ? 4 : 24;\n const step = Math.max(1, Math.floor(stepSize / 24));\n const windowSize = 30;\n\n const out: Array<{ signals: import('./scoring.js').Signal[]; regime: MarketRegime } | null> =\n new Array(allCandles.length).fill(null);\n\n for (let i = windowSize; i < allCandles.length; i += step) {\n const windowCandles = allCandles.slice(Math.max(0, i - 200), i);\n if (windowCandles.length < windowSize) continue;\n const currentCandle = allCandles[i]!;\n const currentDate = new Date(currentCandle.timestamp).toISOString().split('T')[0]!;\n const currentPrice = currentCandle.close;\n\n try {\n const technicals = getLatestSignals(windowCandles);\n\n let fearAndGreed: { value: number; classification: string } | undefined;\n const fgValue = fearAndGreedData[currentDate];\n if (fgValue !== undefined) {\n const classification = fgValue < 25 ? 'Extreme Fear' :\n fgValue < 40 ? 'Fear' :\n fgValue < 60 ? 'Neutral' :\n fgValue < 75 ? 'Greed' : 'Extreme Greed';\n fearAndGreed = { value: fgValue, classification };\n }\n\n const ctx: StrategyContext = {\n tokenId: this.config.tokenId,\n candles: windowCandles,\n technicals,\n fearAndGreed,\n };\n\n const rawSignals = await runStrategies(ctx);\n\n // Same momentum injection as simulate()\n if (windowCandles.length >= 24) {\n const recent = windowCandles.slice(-24);\n const recentLow = Math.min(...recent.map((c) => c.low));\n const recentHigh = Math.max(...recent.map((c) => c.high));\n const pctFromLow = recentLow > 0 ? (currentPrice - recentLow) / recentLow : 0;\n const pctFromHigh = recentHigh > 0 ? (recentHigh - currentPrice) / recentHigh : 0;\n\n let momentumValue = 0;\n if (pctFromHigh < 0.01) momentumValue = Math.min(0.6, pctFromLow * 5);\n else if (pctFromLow < 0.01) momentumValue = -Math.min(0.6, pctFromHigh * 5);\n else if (recentHigh > recentLow) {\n const rangePosition = (currentPrice - recentLow) / (recentHigh - recentLow);\n momentumValue = (rangePosition - 0.5) * 0.4;\n }\n\n if (Math.abs(momentumValue) > 0.02) {\n rawSignals.push({\n name: 'momentum',\n value: momentumValue,\n confidence: Math.min(0.7, 0.3 + Math.abs(momentumValue)),\n source: 'Price Momentum',\n details: `Backtest momentum`,\n });\n }\n }\n\n // Calibrator path: filter to BACKTEST_STRATEGIES (config.strategies is [])\n const filtered = rawSignals.filter((s) => BACKTEST_STRATEGIES.includes(s.name));\n const regime = MarketRegimeDetector.classifyFromCandles(windowCandles).regime;\n\n out[i] = { signals: filtered.length > 0 ? filtered : rawSignals, regime };\n } catch {\n out[i] = null; // skip — same as simulate()\n }\n }\n\n return out;\n }\n\n /**\n * Run the simulation loop on pre-fetched data. Pure of network IO.\n * The calibrator uses this directly after fetchData() to replay many\n * config permutations without re-fetching per permutation.\n *\n * When `precomputed` is supplied, the inner per-candle strategy stack\n * (network-bound) is skipped and the cached signals/regime are used\n * instead. Smoothing is also skipped on the precomputed path — the\n * caller is responsible for applying it before precomputing if needed.\n */\n async simulate(\n allCandles: Candle[],\n fearAndGreedData: Record<string, number>,\n precomputed?: Array<{ signals: import('./scoring.js').Signal[]; regime: MarketRegime } | null>,\n ): Promise<BacktestResult> {\n // 2. Determine step size based on cycle\n const stepSize = this.config.cycle === '1h' ? 1 : this.config.cycle === '4h' ? 4 : 24;\n // CoinGecko OHLC for 90+ days gives daily candles, so step = 1 candle for '1d'\n const step = Math.max(1, Math.floor(stepSize / 24)); // approximate\n\n // 3. Simulate trading\n let capital = this.config.initialCapital;\n // `extremePrice` tracks the high-water mark for longs and the low-water\n // mark for shorts — i.e. the most favorable price seen since entry, used\n // for trailing-stop calculations.\n let position: {\n side: 'long' | 'short';\n entryPrice: number;\n entryDate: string;\n signal: string;\n extremePrice: number;\n entryTimestamp: number;\n } | null = null;\n // Per-simulation in-memory smoother (no disk IO). Maintains rolling\n // buffers across candles so smoothing reflects the same window logic\n // as live runs.\n const smoother = this.config.smoothFastSignals\n ? new SignalSmoother(new MemorySmootherStorage(), DEFAULT_SMOOTHER_CONFIG)\n : null;\n const trades: BacktestTrade[] = [];\n const equityCurve: Array<{ date: string; value: number }> = [];\n const returns: number[] = [];\n\n const windowSize = 30; // min candles needed for indicators\n\n // Show strategy filtering info\n if (this.config.verbose) {\n if (this.config.strategies.length > 0) {\n console.log(chalk.dim(` Using user-specified strategies: ${this.config.strategies.join(', ')}`));\n } else {\n console.log(chalk.dim(` Using backtest strategies: ${BACKTEST_STRATEGIES.join(', ')} + momentum`));\n if (Object.keys(fearAndGreedData).length > 0) {\n console.log(chalk.dim(` Fear & Greed data available — sentimentContrarian active`));\n }\n }\n console.log();\n }\n\n for (let i = windowSize; i < allCandles.length; i += step) {\n // Use historical data only (excluding current candle to avoid look-ahead bias)\n const windowCandles = allCandles.slice(Math.max(0, i - 200), i);\n const currentCandle = allCandles[i]!;\n const currentDate = new Date(currentCandle.timestamp).toISOString().split('T')[0]!;\n const currentPrice = currentCandle.close;\n\n // Skip if insufficient data for indicators\n if (windowCandles.length < windowSize) continue;\n\n // Track equity (direction-aware: shorts profit when price falls)\n const equity = position\n ? capital * (1 + (position.side === 'short'\n ? (position.entryPrice - currentPrice) / position.entryPrice\n : (currentPrice - position.entryPrice) / position.entryPrice))\n : capital;\n equityCurve.push({ date: currentDate, value: equity });\n\n // Compute indicators using only historical data\n let decision: TradeDecision;\n try {\n let filtered: import('./scoring.js').Signal[];\n let regime: MarketRegime | undefined;\n\n if (precomputed) {\n // Fast path — use cached signals + regime from a prior precomputeSignals() pass.\n // Skips the network-bound runStrategies call. Used by the calibrator to replay\n // hundreds of weight/threshold combinations against the same token in seconds.\n const entry = precomputed[i];\n if (!entry) continue;\n filtered = entry.signals;\n regime = this.config.useRegime ? entry.regime : undefined;\n } else {\n const technicals = getLatestSignals(windowCandles);\n\n // Add Fear & Greed data for the current date if available\n let fearAndGreed: { value: number; classification: string } | undefined;\n const fgValue = fearAndGreedData[currentDate];\n if (fgValue !== undefined) {\n const classification = fgValue < 25 ? 'Extreme Fear' :\n fgValue < 40 ? 'Fear' :\n fgValue < 60 ? 'Neutral' :\n fgValue < 75 ? 'Greed' : 'Extreme Greed';\n fearAndGreed = { value: fgValue, classification };\n }\n\n const ctx: StrategyContext = {\n tokenId: this.config.tokenId,\n candles: windowCandles,\n technicals,\n fearAndGreed,\n };\n\n // Run strategies\n let signals = await runStrategies(ctx);\n\n // Add momentum signal (same logic as live in index.ts — computed from candles)\n if (windowCandles.length >= 24) {\n const recent = windowCandles.slice(-24);\n const recentLow = Math.min(...recent.map((c) => c.low));\n const recentHigh = Math.max(...recent.map((c) => c.high));\n const pctFromLow = recentLow > 0 ? (currentPrice - recentLow) / recentLow : 0;\n const pctFromHigh = recentHigh > 0 ? (recentHigh - currentPrice) / recentHigh : 0;\n\n let momentumValue = 0;\n if (pctFromHigh < 0.01) {\n momentumValue = Math.min(0.6, pctFromLow * 5);\n } else if (pctFromLow < 0.01) {\n momentumValue = -Math.min(0.6, pctFromHigh * 5);\n } else if (recentHigh > recentLow) {\n const rangePosition = (currentPrice - recentLow) / (recentHigh - recentLow);\n momentumValue = (rangePosition - 0.5) * 0.4;\n }\n\n if (Math.abs(momentumValue) > 0.02) {\n signals.push({\n name: 'momentum',\n value: momentumValue,\n confidence: Math.min(0.7, 0.3 + Math.abs(momentumValue)),\n source: 'Price Momentum',\n details: `Backtest momentum`,\n });\n }\n }\n\n // Optional smoothing — uses per-simulation in-memory buffer so each\n // candle adds to a rolling window matching live behavior.\n if (smoother) {\n signals = await smoother.smooth(this.config.tokenId, signals, currentCandle.timestamp);\n }\n\n // Filter strategies: if user specified strategies, honor their choice\n // Otherwise, filter to candle-based strategies only\n if (this.config.strategies.length > 0) {\n // User specified strategies — use their selection\n filtered = signals.filter((s) => this.config.strategies.some(\n (name) => s.name.toLowerCase().includes(name.toLowerCase()),\n ));\n } else {\n // No user selection — filter to candle-based strategies only\n filtered = signals.filter((s) => BACKTEST_STRATEGIES.includes(s.name));\n }\n if (filtered.length === 0) filtered = signals;\n\n // Classify regime from the current rolling window (no look-ahead)\n // when --regime is enabled. The backtester operates on a single\n // token's candles; for a true cross-asset regime read you'd swap\n // BTC candles in here, but per-candle BTC/ETH/SOL trends are\n // highly correlated so the asset's own candles are a fair proxy.\n if (this.config.useRegime) {\n regime = MarketRegimeDetector.classifyFromCandles(windowCandles).regime;\n }\n }\n\n decision = computeTradeDecision(\n filtered,\n this.config.customWeights,\n undefined,\n undefined,\n regime,\n );\n\n // Calibrator uses scalar threshold overrides — re-derive action from\n // score if buyThreshold/sellThreshold are set. STRONG_* thresholds\n // extrapolate ±0.3 from the base (matches the regime threshold spread).\n if (this.config.buyThreshold !== undefined || this.config.sellThreshold !== undefined) {\n const buy = this.config.buyThreshold ?? decision.thresholds?.buy ?? 0.3;\n const sell = this.config.sellThreshold ?? decision.thresholds?.sell ?? -0.3;\n // Invariant: buy must strictly exceed sell. Otherwise a score at\n // the collision point fires BUY (>= runs first) while a symmetric\n // reading of the same market condition would fire SELL — the\n // action becomes asymmetrically path-dependent on comparison order.\n if (buy <= sell) {\n throw new Error(\n `Invalid threshold override: buyThreshold (${buy}) must be strictly greater ` +\n `than sellThreshold (${sell}). Collision would produce ambiguous actions at score == threshold.`,\n );\n }\n const strongBuy = buy + 0.3;\n const strongSell = sell - 0.3;\n let action: typeof decision.action;\n if (decision.score >= strongBuy) action = 'STRONG_BUY';\n else if (decision.score >= buy) action = 'BUY';\n else if (decision.score <= strongSell) action = 'STRONG_SELL';\n else if (decision.score <= sell) action = 'SELL';\n else action = 'HOLD';\n decision = { ...decision, action, thresholds: { strongBuy, buy, sell, strongSell } };\n }\n\n // Verbose logging\n if (this.config.verbose) {\n const activeSignals = filtered.filter(s => Math.abs(s.value) > 0.01);\n const signalSummary = activeSignals.map(s =>\n `${s.name}:${s.value >= 0 ? '+' : ''}${s.value.toFixed(2)}`\n ).join(' ');\n const regimeTag = regime ? ` [${regime}]` : '';\n console.log(`${currentDate}: score=${decision.score.toFixed(2)} ${decision.action}${regimeTag} (${signalSummary || 'no signals'})`);\n }\n } catch {\n continue; // skip candle if analysis fails\n }\n\n // ── Execute paper trades with FULL exit logic ──\n // Matches the live system: stop-loss, take-profit, time stop, trailing,\n // AND signal-based exits. Previously only checked SELL signals, so\n // positions rode forever without stops.\n // ATR-adaptive exits — match executor's 3.5×ATR with 3-15% clamp (autoresearch-tuned)\n const atrValues = calculateATR(windowCandles, 14);\n const currentAtr = atrValues.length > 0 ? atrValues[atrValues.length - 1]! : 0;\n const atrPct = currentPrice > 0 && !isNaN(currentAtr) && currentAtr > 0\n ? currentAtr / currentPrice\n : 0.03; // fallback to 3% (floor) when no ATR data\n const STOP_LOSS_PCT = Math.min(0.15, Math.max(0.03, atrPct * 3.5));\n const TAKE_PROFIT_PCT = STOP_LOSS_PCT * 2.0; // maintain 2:1 R:R\n const TIME_STOP_HOURS = 96; // 96h dead-money exit (autoresearch: patience pays)\n const TRAIL_PCT = this.config.trailingStopPct ?? 0.04; // 4% trail (wider)\n\n // Open a long on BUY/STRONG_BUY or a short on SELL/STRONG_SELL\n // (only when flat — pyramid logic lives in the live executor).\n if (!position && (decision.action === 'BUY' || decision.action === 'STRONG_BUY')) {\n position = {\n side: 'long',\n entryPrice: currentPrice,\n entryDate: currentDate,\n signal: decision.action,\n extremePrice: currentPrice,\n entryTimestamp: currentCandle.timestamp,\n };\n } else if (!position && (decision.action === 'SELL' || decision.action === 'STRONG_SELL')) {\n position = {\n side: 'short',\n entryPrice: currentPrice,\n entryDate: currentDate,\n signal: decision.action,\n extremePrice: currentPrice, // for shorts this becomes a low-water mark\n entryTimestamp: currentCandle.timestamp,\n };\n } else if (position) {\n const isShort = position.side === 'short';\n // Track best favorable price: highest for longs, lowest for shorts\n if (isShort) {\n if (currentPrice < position.extremePrice) position.extremePrice = currentPrice;\n } else {\n if (currentPrice > position.extremePrice) position.extremePrice = currentPrice;\n }\n\n // Direction-aware PnL (shorts profit when price falls)\n const pnlPercent = isShort\n ? (position.entryPrice - currentPrice) / position.entryPrice\n : (currentPrice - position.entryPrice) / position.entryPrice;\n const holdingHours = (currentCandle.timestamp - position.entryTimestamp) / (1000 * 60 * 60);\n\n // Check ALL exit conditions (priority order)\n let exitReason: string | null = null;\n\n // 1. Stop loss (3%) — pnlPercent is already direction-aware\n if (pnlPercent <= -STOP_LOSS_PCT) {\n exitReason = `STOP_LOSS (${(pnlPercent * 100).toFixed(1)}%)`;\n }\n // 2. Take profit (6%)\n else if (pnlPercent >= TAKE_PROFIT_PCT) {\n exitReason = `TAKE_PROFIT (+${(pnlPercent * 100).toFixed(1)}%)`;\n }\n // 3. Trailing stop — for longs, exit if price drops X% from high;\n // for shorts, exit if price rises X% from low.\n else if (TRAIL_PCT > 0 && (isShort\n ? currentPrice >= position.extremePrice * (1 + TRAIL_PCT)\n : currentPrice <= position.extremePrice * (1 - TRAIL_PCT))) {\n exitReason = `TRAILING_STOP (extreme $${position.extremePrice.toFixed(2)}, ${isShort ? '+' : '-'}${(TRAIL_PCT * 100).toFixed(1)}%)`;\n }\n // 4. Time stop (48h with <1% PnL)\n else if (holdingHours > TIME_STOP_HOURS && Math.abs(pnlPercent) < 0.01) {\n exitReason = `TIME_STOP (${(holdingHours / 24).toFixed(1)}d, ${(pnlPercent * 100).toFixed(1)}%)`;\n }\n // 5. Signal-flip exit: longs flip on SELL, shorts flip on BUY\n else if (isShort\n ? (decision.action === 'BUY' || decision.action === 'STRONG_BUY')\n : (decision.action === 'SELL' || decision.action === 'STRONG_SELL')) {\n exitReason = decision.action;\n }\n\n if (exitReason) {\n capital *= (1 + pnlPercent);\n returns.push(pnlPercent);\n trades.push({\n entryDate: position.entryDate,\n exitDate: currentDate,\n entryPrice: position.entryPrice,\n exitPrice: currentPrice,\n pnlPercent,\n signal: `${position.signal}${isShort ? ' [SHORT]' : ''} → ${exitReason}`,\n });\n position = null;\n }\n }\n }\n\n // Close any remaining position at last price (direction-aware)\n if (position && allCandles.length > 0) {\n const lastCandle = allCandles[allCandles.length - 1]!;\n const lastPrice = lastCandle.close;\n const lastDate = new Date(lastCandle.timestamp).toISOString().split('T')[0]!;\n const pnlPercent = position.side === 'short'\n ? (position.entryPrice - lastPrice) / position.entryPrice\n : (lastPrice - position.entryPrice) / position.entryPrice;\n capital *= (1 + pnlPercent);\n returns.push(pnlPercent);\n\n trades.push({\n entryDate: position.entryDate,\n exitDate: lastDate,\n entryPrice: position.entryPrice,\n exitPrice: lastPrice,\n pnlPercent,\n signal: `${position.signal}${position.side === 'short' ? ' [SHORT]' : ''} → CLOSE`,\n });\n }\n\n // 4. Compute metrics\n const totalReturn = capital - this.config.initialCapital;\n const totalReturnPercent = totalReturn / this.config.initialCapital;\n const winRate = returns.length > 0\n ? returns.filter((r) => r > 0).length / returns.length\n : 0;\n\n // Sharpe ratio (annualized from daily equity curve returns, not per-trade returns)\n const dailyReturns: number[] = [];\n for (let i = 1; i < equityCurve.length; i++) {\n const prev = equityCurve[i - 1]!.value;\n if (prev > 0) {\n dailyReturns.push((equityCurve[i]!.value - prev) / prev);\n }\n }\n const meanReturn = dailyReturns.length > 0\n ? dailyReturns.reduce((a, b) => a + b, 0) / dailyReturns.length\n : 0;\n const variance = dailyReturns.length > 1\n ? dailyReturns.reduce((sum, r) => sum + (r - meanReturn) ** 2, 0) / (dailyReturns.length - 1)\n : 0;\n const stdDev = Math.sqrt(variance);\n const riskFreeDaily = 0.05 / 252; // 5% annual risk-free rate\n const sharpeRatio = stdDev > 0 ? ((meanReturn - riskFreeDaily) / stdDev) * Math.sqrt(252) : 0;\n\n // Max drawdown\n let maxDrawdown = 0;\n let peak = this.config.initialCapital;\n for (const point of equityCurve) {\n if (point.value > peak) peak = point.value;\n const drawdown = (peak - point.value) / peak;\n if (drawdown > maxDrawdown) maxDrawdown = drawdown;\n }\n\n return {\n config: this.config,\n totalReturn,\n totalReturnPercent,\n sharpeRatio,\n maxDrawdown,\n winRate,\n totalTrades: trades.length,\n trades,\n equityCurve,\n };\n }\n\n /** Walk-forward optimization to prevent overfitting. */\n async walkForwardTest(config: WalkForwardConfig): Promise<WalkForwardResult> {\n const endDate = new Date();\n endDate.setHours(0, 0, 0, 0); // Start of today\n const startDate = new Date(endDate);\n startDate.setDate(startDate.getDate() - config.totalDays);\n\n const folds: WalkForwardResult['folds'] = [];\n const testReturns: number[] = [];\n const testSharpes: number[] = [];\n const testMaxDrawdowns: number[] = [];\n\n let currentStart = new Date(startDate);\n\n while (currentStart.getTime() + (config.trainWindow + config.testWindow) * 24 * 60 * 60 * 1000 <= endDate.getTime()) {\n // Define train period\n const trainStart = new Date(currentStart);\n const trainEnd = new Date(trainStart);\n trainEnd.setDate(trainEnd.getDate() + config.trainWindow);\n\n // Define test period\n const testStart = new Date(trainEnd);\n const testEnd = new Date(testStart);\n testEnd.setDate(testEnd.getDate() + config.testWindow);\n\n try {\n // Run backtest on training period\n const trainBacktester = new Backtester({\n tokenId: config.tokenId,\n startDate: trainStart.toISOString().slice(0, 10),\n endDate: trainEnd.toISOString().slice(0, 10),\n initialCapital: config.capital,\n strategies: config.strategies,\n cycle: '1d',\n verbose: false, // Don't spam verbose output during walk-forward\n useRegime: config.useRegime,\n trailingStopPct: config.trailingStopPct,\n smoothFastSignals: config.smoothFastSignals,\n });\n const trainResult = await trainBacktester.run();\n\n // Run backtest on test period\n const testBacktester = new Backtester({\n tokenId: config.tokenId,\n startDate: testStart.toISOString().slice(0, 10),\n endDate: testEnd.toISOString().slice(0, 10),\n initialCapital: config.capital,\n strategies: config.strategies,\n cycle: '1d',\n verbose: false, // Don't spam verbose output during walk-forward\n useRegime: config.useRegime,\n trailingStopPct: config.trailingStopPct,\n smoothFastSignals: config.smoothFastSignals,\n });\n const testResult = await testBacktester.run();\n\n const fold = {\n trainPeriod: { from: trainStart, to: trainEnd },\n testPeriod: { from: testStart, to: testEnd },\n trainSharpe: trainResult.sharpeRatio,\n testSharpe: testResult.sharpeRatio,\n trainReturn: trainResult.totalReturnPercent,\n testReturn: testResult.totalReturnPercent,\n testMaxDrawdown: testResult.maxDrawdown,\n tradesInTest: testResult.totalTrades,\n };\n\n folds.push(fold);\n testReturns.push(testResult.totalReturnPercent);\n testSharpes.push(testResult.sharpeRatio);\n testMaxDrawdowns.push(testResult.maxDrawdown);\n } catch (error) {\n // Skip fold if data is insufficient\n console.warn(`Skipping fold ${trainStart.toISOString().slice(0, 10)} - ${testEnd.toISOString().slice(0, 10)}: ${error}`);\n }\n\n // Advance to next fold\n currentStart.setDate(currentStart.getDate() + config.stepSize);\n }\n\n if (folds.length === 0) {\n throw new Error('No valid folds could be generated - insufficient data or invalid parameters');\n }\n\n // Calculate aggregate metrics\n const aggregateTestReturn = testReturns.reduce((sum, ret) => sum + ret, 0) / testReturns.length;\n const aggregateTestSharpe = testSharpes.filter(s => !isNaN(s) && isFinite(s)).reduce((sum, s) => sum + s, 0) / testSharpes.filter(s => !isNaN(s) && isFinite(s)).length || 0;\n const avgTestMaxDrawdown = testMaxDrawdowns.reduce((sum, dd) => sum + dd, 0) / testMaxDrawdowns.length;\n\n // Calculate overfitting metrics\n const avgTrainSharpe = folds.map(f => f.trainSharpe).filter(s => !isNaN(s) && isFinite(s)).reduce((sum, s) => sum + s, 0) / folds.filter(f => !isNaN(f.trainSharpe) && isFinite(f.trainSharpe)).length || 0;\n const overfitRatio = avgTrainSharpe > 0 && aggregateTestSharpe > 0 ? avgTrainSharpe / aggregateTestSharpe : 1;\n const overfit = overfitRatio > 2.0; // Flag if train performance is more than 2x better than test\n\n return {\n folds,\n aggregateTestSharpe,\n aggregateTestReturn,\n avgTestMaxDrawdown,\n overfit,\n overfitRatio,\n };\n }\n\n /** Format results for display. */\n formatResults(result: BacktestResult): string {\n const lines: string[] = [];\n\n lines.push('');\n lines.push(chalk.bold(' ┌──────────────────────────────────────────────┐'));\n lines.push(chalk.bold(' │ Backtest Results │'));\n lines.push(chalk.bold(' └──────────────────────────────────────────────┘'));\n lines.push('');\n lines.push(` Token: ${result.config.tokenId}`);\n lines.push(` Period: ${result.config.startDate} → ${result.config.endDate}`);\n lines.push(` Initial: $${result.config.initialCapital.toLocaleString()}`);\n lines.push(` Strategies: ${result.config.strategies.join(', ') || 'all'}`);\n lines.push(` Cycle: ${result.config.cycle}`);\n lines.push('');\n lines.push(chalk.dim(' ' + '═'.repeat(50)));\n\n // Performance metrics\n const retColor = result.totalReturnPercent >= 0 ? chalk.green : chalk.red;\n lines.push(` Total Return: ${retColor('$' + result.totalReturn.toFixed(2))} (${retColor((result.totalReturnPercent * 100).toFixed(2) + '%')})`);\n lines.push(` Final Capital: $${(result.config.initialCapital + result.totalReturn).toFixed(2)}`);\n lines.push(` Sharpe Ratio: ${result.sharpeRatio.toFixed(2)}`);\n lines.push(` Max Drawdown: ${chalk.red((result.maxDrawdown * 100).toFixed(2) + '%')}`);\n lines.push(` Win Rate: ${(result.winRate * 100).toFixed(1)}%`);\n lines.push(` Total Trades: ${result.totalTrades}`);\n\n // Volume quality warning\n const zeroVolumeTrades = result.trades.filter(t => {\n // Check if trades occurred during periods with potentially missing volume data\n return true; // For now, we'll always show this warning since CoinGecko OHLC had volume issues\n }).length;\n const volumeWarning = result.equityCurve.length > 0; // We have equity data, so we can check volume quality\n if (volumeWarning) {\n lines.push('');\n lines.push(chalk.yellow(' ⚠️ Volume Data Quality:'));\n lines.push(chalk.dim(' Volume data sourced from market_chart endpoint and aggregated daily.'));\n lines.push(chalk.dim(' Intraday volume patterns may not be fully captured.'));\n }\n\n // Monthly returns breakdown\n if (result.trades.length > 0) {\n const monthlyReturns = this.calculateMonthlyReturns(result.equityCurve);\n if (Object.keys(monthlyReturns).length > 1) {\n lines.push('');\n lines.push(chalk.bold(' Monthly Returns:'));\n lines.push(chalk.dim(` ${'Month'.padEnd(12)} ${'Return %'.padEnd(10)} Performance`));\n lines.push(chalk.dim(' ' + '─'.repeat(35)));\n\n for (const [month, returnPct] of Object.entries(monthlyReturns).slice(-12)) { // Show last 12 months\n const returnColor = returnPct >= 0 ? chalk.green : chalk.red;\n const bar = this.renderMiniBar(returnPct / 100, 10);\n lines.push(\n ` ${month.padEnd(12)} ${returnColor((returnPct > 0 ? '+' : '') + returnPct.toFixed(1) + '%').padEnd(18)} ${bar}`,\n );\n }\n }\n }\n\n // Strategy performance breakdown (simplified version)\n if (result.trades.length > 0 && result.config.strategies.length > 0) {\n lines.push('');\n lines.push(chalk.bold(' Strategy Performance:'));\n lines.push(chalk.dim(` ${'Strategy'.padEnd(20)} ${'Trades'.padEnd(8)} Win Rate`));\n lines.push(chalk.dim(' ' + '─'.repeat(40)));\n\n // For now, show overall win rate per configured strategy filter\n // In a future enhancement, we could track which specific strategies triggered each trade\n for (const strategy of result.config.strategies.slice(0, 5)) { // Top 5 strategies\n const strategyTrades = Math.floor(result.totalTrades / result.config.strategies.length); // Approximate distribution\n const winRate = result.winRate; // Use overall win rate as approximation\n const winRateColor = winRate >= 0.6 ? chalk.green : winRate >= 0.4 ? chalk.yellow : chalk.red;\n lines.push(\n ` ${strategy.slice(0, 19).padEnd(20)} ${strategyTrades.toString().padEnd(8)} ${winRateColor((winRate * 100).toFixed(1) + '%')}`,\n );\n }\n if (result.config.strategies.length === 0) {\n lines.push(chalk.dim(' All strategies combined'));\n }\n }\n\n // Trade list\n if (result.trades.length > 0) {\n lines.push('');\n lines.push(chalk.bold(' Trades:'));\n lines.push(chalk.dim(` ${'Entry'.padEnd(12)} ${'Exit'.padEnd(12)} ${'Entry$'.padEnd(10)} ${'Exit$'.padEnd(10)} ${'PnL%'.padEnd(10)} Signal`));\n lines.push(chalk.dim(' ' + '─'.repeat(65)));\n\n for (const t of result.trades.slice(-20)) { // show last 20\n const pnlColor = t.pnlPercent >= 0 ? chalk.green : chalk.red;\n lines.push(\n ` ${t.entryDate.padEnd(12)} ${t.exitDate.padEnd(12)} $${t.entryPrice.toFixed(2).padEnd(9)} $${t.exitPrice.toFixed(2).padEnd(9)} ${pnlColor((t.pnlPercent * 100).toFixed(2).padStart(7) + '%')} ${t.signal}`,\n );\n }\n if (result.trades.length > 20) {\n lines.push(chalk.dim(` ... and ${result.trades.length - 20} more trades`));\n }\n }\n\n // Equity curve ASCII art\n if (result.equityCurve.length > 2) {\n lines.push('');\n lines.push(chalk.bold(' Equity Curve:'));\n lines.push(this.renderEquityCurve(result.equityCurve, result.config.initialCapital));\n }\n\n lines.push('');\n return lines.join('\\n');\n }\n\n /** Format walk-forward results for display. */\n formatWalkForwardResults(result: WalkForwardResult, config: WalkForwardConfig): string {\n const lines: string[] = [];\n\n lines.push('');\n lines.push(chalk.bold(' ┌──────────────────────────────────────────────┐'));\n lines.push(chalk.bold(' │ Walk-Forward Optimization Results │'));\n lines.push(chalk.bold(' └──────────────────────────────────────────────┘'));\n lines.push('');\n lines.push(` Token: ${config.tokenId}`);\n lines.push(` Train Window: ${config.trainWindow} days`);\n lines.push(` Test Window: ${config.testWindow} days`);\n lines.push(` Step Size: ${config.stepSize} days`);\n lines.push(` Total Folds: ${result.folds.length}`);\n lines.push(` Strategies: ${config.strategies.join(', ') || 'all'}`);\n lines.push('');\n lines.push(chalk.dim(' ' + '═'.repeat(50)));\n\n // Aggregate performance\n const retColor = result.aggregateTestReturn >= 0 ? chalk.green : chalk.red;\n const overfitColor = result.overfit ? chalk.red : chalk.green;\n lines.push(` Aggregate Test Return: ${retColor((result.aggregateTestReturn * 100).toFixed(2) + '%')}`);\n lines.push(` Aggregate Test Sharpe: ${result.aggregateTestSharpe.toFixed(2)}`);\n lines.push(` Avg Test Max Drawdown: ${chalk.red((result.avgTestMaxDrawdown * 100).toFixed(2) + '%')}`);\n lines.push(` Overfitting Ratio: ${overfitColor(result.overfitRatio.toFixed(2) + (result.overfit ? ' ⚠️ OVERFIT' : ' ✓'))}`);\n\n // Fold-by-fold breakdown\n if (result.folds.length > 0) {\n lines.push('');\n lines.push(chalk.bold(' Fold-by-Fold Results:'));\n lines.push(chalk.dim(` ${'Train Period'.padEnd(23)} ${'Test Period'.padEnd(23)} ${'Train'.padEnd(8)} ${'Test'.padEnd(8)} ${'Test DD%'.padEnd(9)} Trades`));\n lines.push(chalk.dim(' ' + '─'.repeat(90)));\n\n for (const fold of result.folds) {\n const trainPeriodStr = `${fold.trainPeriod.from.toISOString().slice(0, 10)} to ${fold.trainPeriod.to.toISOString().slice(0, 10)}`;\n const testPeriodStr = `${fold.testPeriod.from.toISOString().slice(0, 10)} to ${fold.testPeriod.to.toISOString().slice(0, 10)}`;\n const trainSharpeStr = isFinite(fold.trainSharpe) ? fold.trainSharpe.toFixed(2) : 'N/A';\n const testSharpeStr = isFinite(fold.testSharpe) ? fold.testSharpe.toFixed(2) : 'N/A';\n const testReturnColor = fold.testReturn >= 0 ? chalk.green : chalk.red;\n const testDdStr = (fold.testMaxDrawdown * 100).toFixed(1) + '%';\n\n lines.push(\n ` ${trainPeriodStr.padEnd(23)} ${testPeriodStr.padEnd(23)} ${trainSharpeStr.padEnd(8)} ${testReturnColor(testSharpeStr.padEnd(8))} ${chalk.red(testDdStr.padEnd(9))} ${fold.tradesInTest}`,\n );\n }\n\n // Summary stats\n const winningFolds = result.folds.filter(f => f.testReturn > 0).length;\n const winRate = result.folds.length > 0 ? (winningFolds / result.folds.length * 100).toFixed(1) : '0.0';\n\n lines.push('');\n lines.push(chalk.dim(' ' + '─'.repeat(50)));\n lines.push(` Winning Folds: ${winningFolds}/${result.folds.length} (${winRate}%)`);\n\n if (result.overfit) {\n lines.push('');\n lines.push(chalk.red(' ⚠️ WARNING: Strategy shows signs of overfitting!'));\n lines.push(chalk.red(' Train performance significantly exceeds test performance.'));\n lines.push(chalk.red(' Consider simplifying the strategy or using longer test periods.'));\n }\n }\n\n lines.push('');\n return lines.join('\\n');\n }\n\n /** Calculate monthly returns from equity curve. */\n private calculateMonthlyReturns(equityCurve: Array<{ date: string; value: number }>): Record<string, number> {\n const monthlyReturns: Record<string, number> = {};\n\n if (equityCurve.length === 0) return monthlyReturns;\n\n let currentMonth = '';\n let monthStartValue = equityCurve[0]!.value;\n let monthEndValue = equityCurve[0]!.value;\n\n for (const point of equityCurve) {\n const pointMonth = point.date.slice(0, 7); // YYYY-MM format\n\n if (currentMonth === '') {\n currentMonth = pointMonth;\n monthStartValue = point.value;\n } else if (pointMonth !== currentMonth) {\n // Month changed, calculate return for previous month\n if (monthStartValue > 0) {\n const returnPct = ((monthEndValue - monthStartValue) / monthStartValue) * 100;\n monthlyReturns[currentMonth] = returnPct;\n }\n\n // Start new month\n currentMonth = pointMonth;\n monthStartValue = point.value;\n }\n\n monthEndValue = point.value;\n }\n\n // Don't forget the last month\n if (currentMonth && monthStartValue > 0) {\n const returnPct = ((monthEndValue - monthStartValue) / monthStartValue) * 100;\n monthlyReturns[currentMonth] = returnPct;\n }\n\n return monthlyReturns;\n }\n\n /** Render a mini bar chart for monthly returns. */\n private renderMiniBar(value: number, width: number): string {\n const magnitude = Math.min(Math.abs(value), 0.3); // Cap at 30% for display\n const filled = Math.round(magnitude * width / 0.3);\n\n if (value > 0) {\n return chalk.green('█'.repeat(filled)) + chalk.dim('░'.repeat(width - filled));\n } else if (value < 0) {\n return chalk.red('█'.repeat(filled)) + chalk.dim('░'.repeat(width - filled));\n }\n return chalk.dim('░'.repeat(width));\n }\n\n /** Render a simple ASCII equity curve using box-drawing chars. */\n private renderEquityCurve(\n curve: Array<{ date: string; value: number }>,\n initialCapital: number,\n ): string {\n const width = 60;\n const height = 15;\n\n // Sample curve to fit width\n const sampled: number[] = [];\n const step = Math.max(1, Math.floor(curve.length / width));\n for (let i = 0; i < curve.length; i += step) {\n sampled.push(curve[i]!.value);\n }\n if (sampled.length > width) sampled.length = width;\n\n const minVal = Math.min(...sampled);\n const maxVal = Math.max(...sampled);\n const range = maxVal - minVal || 1;\n\n // Build grid\n const grid: string[][] = [];\n for (let row = 0; row < height; row++) {\n grid.push(new Array(sampled.length).fill(' '));\n }\n\n // Plot points\n for (let col = 0; col < sampled.length; col++) {\n const normalized = (sampled[col]! - minVal) / range;\n const row = height - 1 - Math.round(normalized * (height - 1));\n grid[row]![col] = sampled[col]! >= initialCapital ? '█' : '▓';\n }\n\n // Render with Y-axis labels\n const lines: string[] = [];\n for (let row = 0; row < height; row++) {\n const yVal = maxVal - (row / (height - 1)) * range;\n const label = `$${yVal.toFixed(0)}`.padStart(8);\n const lineStr = grid[row]!.join('');\n const rowColor = yVal >= initialCapital ? chalk.green : chalk.red;\n lines.push(` ${chalk.dim(label)} │${rowColor(lineStr)}`);\n }\n lines.push(` ${''.padStart(8)} └${'─'.repeat(sampled.length)}`);\n\n // X-axis labels\n const firstDate = curve[0]?.date ?? '';\n const lastDate = curve[curve.length - 1]?.date ?? '';\n const axisLabel = ` ${''.padStart(9)}${firstDate}${''.padStart(Math.max(0, sampled.length - firstDate.length - lastDate.length))}${lastDate}`;\n lines.push(chalk.dim(axisLabel));\n\n return lines.join('\\n');\n }\n}\n","/**\n * Multi-token scoring calibration backtester.\n * Fetches data ONCE per token, then replays with different weight/threshold combos.\n */\n\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { Backtester } from \"./backtest.js\";\nimport type { ScoringWeights } from \"./scoring.js\";\nimport type { Candle } from \"./technical.js\";\n\n// ── Weight Profiles ──\n//\n// IMPORTANT: this set deliberately includes BOTH the live production\n// profiles (default-live, majors-live, altcoin-live — pulled from\n// scoring.ts WEIGHT_PROFILES) AND exploratory profiles for tuning.\n// Without the live profiles in the sweep, the calibrator can never tell\n// you \"the current production weights are the best\" — you'd only see\n// rankings of variants. The live entries are SUFFIXED `-live` so they're\n// obvious in the ranked output.\n//\n// To keep this in sync after a scoring.ts weight change: update the\n// `*-live` rows below to match the new production weights.\n\nexport const WEIGHT_PROFILES: Record<string, ScoringWeights> = {\n // ── Live production profiles (from scoring.ts; keep in sync) ──\n \"default-live\": { smartMoney: 0.15, technical: 0.10, sentiment: 0.40, onchain: 0.20, fundamental: 0.10, event: 0.05 },\n \"majors-live\": { smartMoney: 0.15, technical: 0.10, sentiment: 0.40, onchain: 0.35, fundamental: 0.00, event: 0.00 },\n \"altcoin-live\": { smartMoney: 0.15, technical: 0.15, sentiment: 0.30, onchain: 0.20, fundamental: 0.10, event: 0.10 },\n\n // ── Exploratory profiles (historical baselines + variants) ──\n default: { smartMoney: 0.25, technical: 0.20, sentiment: 0.20, onchain: 0.15, fundamental: 0.10, event: 0.10 },\n techHeavy: { smartMoney: 0.10, technical: 0.40, sentiment: 0.15, onchain: 0.15, fundamental: 0.10, event: 0.10 },\n sentHeavy: { smartMoney: 0.10, technical: 0.15, sentiment: 0.40, onchain: 0.15, fundamental: 0.10, event: 0.10 },\n onchainHvy: { smartMoney: 0.10, technical: 0.20, sentiment: 0.15, onchain: 0.35, fundamental: 0.10, event: 0.10 },\n balanced: { smartMoney: 0.17, technical: 0.17, sentiment: 0.17, onchain: 0.17, fundamental: 0.16, event: 0.16 },\n momentum: { smartMoney: 0.15, technical: 0.35, sentiment: 0.10, onchain: 0.25, fundamental: 0.10, event: 0.05 },\n contrarian: { smartMoney: 0.10, technical: 0.10, sentiment: 0.40, onchain: 0.10, fundamental: 0.15, event: 0.15 },\n flowBased: { smartMoney: 0.20, technical: 0.15, sentiment: 0.10, onchain: 0.35, fundamental: 0.10, event: 0.10 },\n};\n\n// Added 0.25 / -0.25 so the calibrator can validate the lowered ranging-regime\n// BUY threshold against historical data. Grid is now 8 profiles × 5 × 5 = 200.\nexport const BUY_THRESHOLDS = [0.2, 0.25, 0.3, 0.4, 0.5];\nexport const SELL_THRESHOLDS = [-0.2, -0.25, -0.3, -0.4, -0.5];\n\nexport const DEFAULT_CALIBRATION_TOKENS = [\n \"bitcoin\", \"ethereum\", \"solana\", \"aave\", \"uniswap\", \"chainlink\", \"arbitrum\",\n];\n\n// ── Types ──\n\nexport interface CalibrationConfig {\n profileName: string;\n weights: ScoringWeights;\n buyThreshold: number;\n sellThreshold: number;\n}\n\nexport interface TokenResult {\n tokenId: string;\n totalReturn: number;\n sharpeRatio: number;\n maxDrawdown: number;\n winRate: number;\n numTrades: number;\n}\n\nexport interface CalibrationResult {\n config: CalibrationConfig;\n tokenResults: TokenResult[];\n avgReturn: number;\n avgSharpe: number;\n worstDrawdown: number;\n totalTrades: number;\n}\n\nexport interface CalibratorOptions {\n tokens: string[];\n days: number;\n capital: number;\n onProgress?: (msg: string) => void;\n}\n\n// ── Build the configuration grid ──\n\nexport function buildCalibrationConfigs(): CalibrationConfig[] {\n const configs: CalibrationConfig[] = [];\n\n for (const [profileName, weights] of Object.entries(WEIGHT_PROFILES)) {\n for (const buyThreshold of BUY_THRESHOLDS) {\n for (const sellThreshold of SELL_THRESHOLDS) {\n configs.push({ profileName, weights, buyThreshold, sellThreshold });\n }\n }\n }\n\n return configs;\n}\n\n// ── Calibrator ──\n\nexport async function runCalibration(opts: CalibratorOptions): Promise<CalibrationResult[]> {\n const { tokens, days, capital, onProgress } = opts;\n const configs = buildCalibrationConfigs();\n const log = onProgress ?? (() => {});\n\n const endDate = new Date();\n endDate.setDate(endDate.getDate() - 1);\n const startDate = new Date(endDate);\n startDate.setDate(startDate.getDate() - days);\n\n const startStr = startDate.toISOString().slice(0, 10);\n const endStr = endDate.toISOString().slice(0, 10);\n\n // Map: configKey -> CalibrationResult\n const resultMap = new Map<string, CalibrationResult>();\n for (const cfg of configs) {\n const key = configKey(cfg);\n resultMap.set(key, {\n config: cfg,\n tokenResults: [],\n avgReturn: 0,\n avgSharpe: 0,\n worstDrawdown: 0,\n totalTrades: 0,\n });\n }\n\n // Process tokens SEQUENTIALLY — fetch data ONCE per token, then replay all configs\n for (let ti = 0; ti < tokens.length; ti++) {\n const tokenId = tokens[ti]!;\n log(`[${ti + 1}/${tokens.length}] Fetching data for ${tokenId}...`);\n\n // Fetch data once using a baseline backtester\n const baseBt = new Backtester({\n tokenId,\n startDate: startStr,\n endDate: endStr,\n initialCapital: capital,\n strategies: [],\n cycle: \"1d\",\n verbose: false,\n });\n\n let candles: Candle[];\n let fearAndGreedData: Record<string, number>;\n try {\n const data = await baseBt.fetchData();\n candles = data.candles;\n fearAndGreedData = data.fearAndGreedData;\n log(` Got ${candles.length} candles + ${Object.keys(fearAndGreedData).length} F&G points`);\n } catch (err) {\n log(` SKIP ${tokenId}: ${(err as Error).message}`);\n // Record zeros for all configs\n for (const cfg of configs) {\n const key = configKey(cfg);\n resultMap.get(key)!.tokenResults.push({\n tokenId, totalReturn: 0, sharpeRatio: 0, maxDrawdown: 0, winRate: 0, numTrades: 0,\n });\n }\n continue;\n }\n\n // Pre-compute strategy signals + regime ONCE per candle (this is the\n // network-bound work — most strategies hit their own APIs). Each subsequent\n // simulate() call replays only the scoring math against these cached\n // signals, turning a multi-hour calibration into seconds.\n log(` Precomputing signals for ${candles.length} candles...`);\n let precomputed: Awaited<ReturnType<Backtester['precomputeSignals']>>;\n try {\n precomputed = await baseBt.precomputeSignals(candles, fearAndGreedData);\n const valid = precomputed.filter((p) => p !== null).length;\n log(` Precomputed ${valid} candle signal sets`);\n } catch (err) {\n log(` SKIP ${tokenId}: precompute failed: ${(err as Error).message}`);\n for (const cfg of configs) {\n const key = configKey(cfg);\n resultMap.get(key)!.tokenResults.push({\n tokenId, totalReturn: 0, sharpeRatio: 0, maxDrawdown: 0, winRate: 0, numTrades: 0,\n });\n }\n continue;\n }\n\n // Now replay all configs using cached signals (zero API calls!)\n log(` Running ${configs.length} configs on ${tokenId}...`);\n for (let ci = 0; ci < configs.length; ci++) {\n const cfg = configs[ci]!;\n const key = configKey(cfg);\n\n try {\n const bt = new Backtester({\n tokenId,\n startDate: startStr,\n endDate: endStr,\n initialCapital: capital,\n strategies: [],\n cycle: \"1d\",\n verbose: false,\n customWeights: cfg.weights,\n buyThreshold: cfg.buyThreshold,\n sellThreshold: cfg.sellThreshold,\n });\n\n // Use simulate() with precomputed signals — pure of network IO\n const result = await bt.simulate(candles, fearAndGreedData, precomputed);\n\n resultMap.get(key)!.tokenResults.push({\n tokenId,\n totalReturn: result.totalReturnPercent,\n sharpeRatio: result.sharpeRatio,\n maxDrawdown: result.maxDrawdown,\n winRate: result.winRate,\n numTrades: result.totalTrades,\n });\n } catch {\n resultMap.get(key)!.tokenResults.push({\n tokenId, totalReturn: 0, sharpeRatio: 0, maxDrawdown: 0, winRate: 0, numTrades: 0,\n });\n }\n\n if ((ci + 1) % 50 === 0) {\n log(` ${tokenId}: ${ci + 1}/${configs.length} configs done`);\n }\n }\n\n log(` ${tokenId} complete`);\n\n // Rate limit pause between tokens\n if (ti < tokens.length - 1) {\n log(` Pausing 5s for CoinGecko rate limits...`);\n await sleep(5000);\n }\n }\n\n // Aggregate results\n const results: CalibrationResult[] = [];\n for (const entry of resultMap.values()) {\n const tr = entry.tokenResults;\n if (tr.length === 0) continue;\n\n entry.avgReturn = tr.reduce((s, r) => s + r.totalReturn, 0) / tr.length;\n entry.avgSharpe = tr.reduce((s, r) => s + r.sharpeRatio, 0) / tr.length;\n entry.worstDrawdown = Math.max(...tr.map(r => r.maxDrawdown));\n entry.totalTrades = tr.reduce((s, r) => s + r.numTrades, 0);\n results.push(entry);\n }\n\n // Sort: 0-trade configs sink to the bottom (vacuously \"safe\" — sharpe=0\n // ties at the top otherwise, masking real winners). Among configs that\n // actually trade, sort by avgSharpe desc, then avgReturn desc.\n results.sort((a, b) => {\n const aZero = a.totalTrades === 0;\n const bZero = b.totalTrades === 0;\n if (aZero !== bZero) return aZero ? 1 : -1;\n if (Math.abs(b.avgSharpe - a.avgSharpe) > 0.001) return b.avgSharpe - a.avgSharpe;\n return b.avgReturn - a.avgReturn;\n });\n\n await saveResults(results);\n return results;\n}\n\n// ── Helpers ──\n\nfunction configKey(cfg: CalibrationConfig): string {\n return `${cfg.profileName}|buy=${cfg.buyThreshold}|sell=${cfg.sellThreshold}`;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\nasync function saveResults(results: CalibrationResult[]): Promise<void> {\n const dir = join(homedir(), \".sherwood\", \"agent\");\n await mkdir(dir, { recursive: true });\n const path = join(dir, \"calibration-results.json\");\n const payload = {\n timestamp: new Date().toISOString(),\n totalConfigs: results.length,\n results: results.map((r, i) => ({\n rank: i + 1,\n profile: r.config.profileName,\n buyThreshold: r.config.buyThreshold,\n sellThreshold: r.config.sellThreshold,\n weights: r.config.weights,\n avgReturn: r.avgReturn,\n avgSharpe: r.avgSharpe,\n worstDrawdown: r.worstDrawdown,\n totalTrades: r.totalTrades,\n tokenResults: r.tokenResults,\n })),\n };\n await writeFile(path, JSON.stringify(payload, null, 2), \"utf-8\");\n}\n\n// ── Format Results Table ──\n\nexport function formatCalibrationTable(results: CalibrationResult[], topN = 20): string {\n const lines: string[] = [];\n lines.push(\"\");\n lines.push(\" Calibration Results (ranked by Sharpe ratio)\");\n lines.push(\" \" + \"=\".repeat(110));\n lines.push(\n \" \" +\n \"Rank\".padEnd(6) +\n \"Profile\".padEnd(13) +\n \"Buy\".padEnd(7) +\n \"Sell\".padEnd(7) +\n \"Avg Return\".padEnd(13) +\n \"Avg Sharpe\".padEnd(13) +\n \"Worst DD\".padEnd(11) +\n \"Trades\".padEnd(9) +\n \"Tokens\"\n );\n lines.push(\" \" + \"-\".repeat(110));\n\n const top = results.slice(0, topN);\n for (let i = 0; i < top.length; i++) {\n const r = top[i]!;\n const rank = String(i + 1).padEnd(6);\n const profile = r.config.profileName.padEnd(13);\n const buy = String(r.config.buyThreshold).padEnd(7);\n const sell = String(r.config.sellThreshold).padEnd(7);\n const avgRet = (r.avgReturn.toFixed(2) + \"%\").padEnd(13);\n const avgSharpe = r.avgSharpe.toFixed(3).padEnd(13);\n const worstDD = (r.worstDrawdown.toFixed(2) + \"%\").padEnd(11);\n const trades = String(r.totalTrades).padEnd(9);\n const tokenCount = String(r.tokenResults.length);\n\n lines.push(\" \" + rank + profile + buy + sell + avgRet + avgSharpe + worstDD + trades + tokenCount);\n }\n\n lines.push(\" \" + \"-\".repeat(110));\n\n if (results.length > 0) {\n const best = results[0]!;\n lines.push(\"\");\n lines.push(\" BEST CONFIGURATION:\");\n lines.push(` Profile: ${best.config.profileName}`);\n lines.push(` Buy threshold: ${best.config.buyThreshold}`);\n lines.push(` Sell threshold: ${best.config.sellThreshold}`);\n lines.push(` Weights: smartMoney=${best.config.weights.smartMoney} technical=${best.config.weights.technical} sentiment=${best.config.weights.sentiment} onchain=${best.config.weights.onchain} fundamental=${best.config.weights.fundamental} event=${best.config.weights.event}`);\n lines.push(` Avg return: ${best.avgReturn.toFixed(2)}%`);\n lines.push(` Avg Sharpe: ${best.avgSharpe.toFixed(3)}`);\n lines.push(` Worst drawdown: ${best.worstDrawdown.toFixed(2)}%`);\n\n // Show per-token breakdown for best config\n lines.push(\"\");\n lines.push(\" Per-token breakdown (best config):\");\n for (const tr of best.tokenResults) {\n lines.push(` ${tr.tokenId.padEnd(12)} return: ${tr.totalReturn.toFixed(2).padStart(8)}% sharpe: ${tr.sharpeRatio.toFixed(3).padStart(7)} trades: ${tr.numTrades}`);\n }\n }\n\n lines.push(\"\");\n const savePath = join(homedir(), \".sherwood\", \"agent\", \"calibration-results.json\");\n lines.push(` Full results saved to ${savePath}`);\n lines.push(\"\");\n\n return lines.join(\"\\n\");\n}\n","/**\n * Replay calibrator — sweeps weight profiles × thresholds against\n * `signal-history.jsonl` (production captures) instead of recomputing\n * signals from candles.\n *\n * Why this exists: the candle-only `Backtester` cannot replay the\n * production signal stack. Strategies like `fundingRate`, `dexFlow`,\n * `hyperliquidFlow`, and Nansen-sourced `smartMoney` need live HL/DEX\n * data feeds that aren't in OHLC. `signal-history.jsonl` already\n * captured those signal values per scan — replaying through the scoring\n * math (which IS pure) gives a true-to-production calibration.\n *\n * Caveat: PnL accuracy is bounded by the scan cadence. If the scanner\n * fired every ~30min on a token, exit prices used for stop/TP detection\n * are sampled at the same interval. Intra-bar wicks are missed. Sharpe\n * computed from these results is a lower bound on what a tighter exit\n * loop would achieve.\n */\n\nimport { readFile, mkdir, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { computeTradeDecision } from './scoring.js';\nimport type { ScoringWeights, Signal, TradeDecision } from './scoring.js';\nimport type { MarketRegime } from './regime.js';\nimport {\n WEIGHT_PROFILES,\n BUY_THRESHOLDS,\n SELL_THRESHOLDS,\n type CalibrationConfig,\n type CalibrationResult,\n type TokenResult,\n} from './calibrator.js';\n\n// ── Types ──\n\n/** A single row from signal-history.jsonl — matches SignalLogEntry. */\ninterface SignalHistoryRow {\n timestamp: string;\n tokenId: string;\n tokenSymbol: string;\n price: number;\n decision: 'STRONG_BUY' | 'BUY' | 'HOLD' | 'SELL' | 'STRONG_SELL';\n score: number;\n confidence: number;\n signals: Array<{ name: string; value: number; confidence: number }>;\n regime: string;\n btcCorrelation: number;\n weights: ScoringWeights;\n}\n\nexport interface ReplayCalibratorOptions {\n /** Path to signal-history.jsonl. Defaults to ~/.sherwood/agent/signal-history.jsonl. */\n historyPath?: string;\n /** Initial capital per token (informational — we track returns, not capital). */\n capital?: number;\n /** Filter to specific tokens; defaults to all tokens in the dataset. */\n tokens?: string[];\n /** Only replay rows captured within the last N days. Useful after a scoring\n * change to avoid contaminating calibration with stale signal values from\n * the prior code path. Default: replay all rows in the file. */\n lastDays?: number;\n onProgress?: (msg: string) => void;\n /** Whether to honor the regime field on each row when applying thresholds.\n * Defaults to true — rows record the regime that gated the original\n * decision, replaying with the same regime preserves real-world behavior. */\n useRegime?: boolean;\n}\n\n// ── Exit logic constants — match backtest.ts and executor.ts ──\n\nconst STOP_LOSS_PCT = 0.03; // 3% hard stop\nconst TAKE_PROFIT_PCT = 0.06; // 6% TP (2:1 R:R)\nconst TRAIL_PCT = 0.025; // 2.5% trailing stop\nconst TIME_STOP_HOURS = 48; // 48h stale-trade exit\n\n/** Open position state for replay. Mirrors the `position` struct in backtest.ts. */\ninterface ReplayPosition {\n side: 'long' | 'short';\n entryPrice: number;\n entryTimestamp: number;\n /** High-water mark for longs, low-water mark for shorts. */\n extremePrice: number;\n signal: string;\n}\n\n/**\n * Trade record kept inline during replay so we can compute realistic\n * Sharpe annualization from actual trade cadence rather than guessing.\n */\ninterface ReplayTrade {\n pnlPercent: number;\n durationMs: number;\n}\n\n// ── Loader ──\n\n/** Read signal-history.jsonl into typed rows. Skips malformed lines. */\nexport async function loadSignalHistory(path: string): Promise<SignalHistoryRow[]> {\n const raw = await readFile(path, 'utf-8');\n const rows: SignalHistoryRow[] = [];\n for (const line of raw.split('\\n')) {\n if (!line.trim()) continue;\n try {\n rows.push(JSON.parse(line) as SignalHistoryRow);\n } catch {\n // Skip malformed lines silently — log file may have been mid-write\n }\n }\n return rows;\n}\n\n/** Group rows by tokenId, sorted chronologically within each group. */\nfunction groupByToken(rows: SignalHistoryRow[]): Map<string, SignalHistoryRow[]> {\n const grouped = new Map<string, SignalHistoryRow[]>();\n for (const r of rows) {\n if (!grouped.has(r.tokenId)) grouped.set(r.tokenId, []);\n grouped.get(r.tokenId)!.push(r);\n }\n for (const list of grouped.values()) {\n list.sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp));\n }\n return grouped;\n}\n\n// ── Per-token replay ──\n\n/**\n * Replay a single token's signal history through one config.\n * Returns {trades, returns, totalReturn, sharpe, maxDrawdown, winRate}.\n */\nfunction replayToken(\n tokenRows: SignalHistoryRow[],\n config: CalibrationConfig,\n useRegime: boolean,\n): TokenResult {\n let position: ReplayPosition | null = null;\n const returns: number[] = [];\n const trades: ReplayTrade[] = []; // hold-duration tracked for Sharpe annualization\n const equityCurve: number[] = [1.0]; // normalized; multiply by (1+ret) per realized trade\n let equity = 1.0;\n\n for (let i = 0; i < tokenRows.length; i++) {\n const row = tokenRows[i]!;\n const ts = Date.parse(row.timestamp);\n const price = row.price;\n\n // Guard: rows with missing/zero/non-finite prices appear in early\n // production captures before the price feed was wired. Skip them\n // entirely — both entry and exit math would produce NaN and poison\n // the equity curve. We do NOT close any open position here; we wait\n // for the next valid price tick.\n if (!Number.isFinite(price) || price <= 0) continue;\n\n // Build Signal[] from row — mirrors what computeTradeDecision expects.\n // Source/details aren't needed for scoring; pass empty strings.\n const signals: Signal[] = row.signals.map((s) => ({\n name: s.name,\n value: s.value,\n confidence: s.confidence,\n source: '',\n details: '',\n }));\n\n const regime = useRegime\n ? (row.regime && row.regime !== 'unknown' ? (row.regime as MarketRegime) : undefined)\n : undefined;\n\n let decision: TradeDecision = computeTradeDecision(\n signals,\n config.weights,\n undefined,\n undefined,\n regime,\n );\n\n // Override action based on calibrator's threshold sweep — same logic as backtest.ts\n const buy = config.buyThreshold;\n const sell = config.sellThreshold;\n if (buy <= sell) continue; // invalid — caller should have filtered, but be safe\n const strongBuy = buy + 0.3;\n const strongSell = sell - 0.3;\n let action: TradeDecision['action'];\n if (decision.score >= strongBuy) action = 'STRONG_BUY';\n else if (decision.score >= buy) action = 'BUY';\n else if (decision.score <= strongSell) action = 'STRONG_SELL';\n else if (decision.score <= sell) action = 'SELL';\n else action = 'HOLD';\n decision = { ...decision, action };\n\n // ── Position management — exit conditions first, then optional entry ──\n if (position) {\n const isShort = position.side === 'short';\n // Track best favorable price\n if (isShort) {\n if (price < position.extremePrice) position.extremePrice = price;\n } else {\n if (price > position.extremePrice) position.extremePrice = price;\n }\n\n const pnlPercent = isShort\n ? (position.entryPrice - price) / position.entryPrice\n : (price - position.entryPrice) / position.entryPrice;\n const holdingHours = (ts - position.entryTimestamp) / (1000 * 60 * 60);\n\n let exitReason: string | null = null;\n if (pnlPercent <= -STOP_LOSS_PCT) exitReason = 'STOP';\n else if (pnlPercent >= TAKE_PROFIT_PCT) exitReason = 'TP';\n else if (TRAIL_PCT > 0 && (isShort\n ? price >= position.extremePrice * (1 + TRAIL_PCT)\n : price <= position.extremePrice * (1 - TRAIL_PCT))) {\n exitReason = 'TRAIL';\n } else if (holdingHours > TIME_STOP_HOURS && Math.abs(pnlPercent) < 0.01) {\n exitReason = 'TIME';\n } else if (isShort\n ? (action === 'BUY' || action === 'STRONG_BUY')\n : (action === 'SELL' || action === 'STRONG_SELL')) {\n exitReason = 'FLIP';\n }\n\n if (exitReason) {\n equity *= 1 + pnlPercent;\n equityCurve.push(equity);\n returns.push(pnlPercent);\n trades.push({ pnlPercent, durationMs: ts - position.entryTimestamp });\n position = null;\n }\n }\n\n // Entries only when flat\n if (!position) {\n if (action === 'BUY' || action === 'STRONG_BUY') {\n position = { side: 'long', entryPrice: price, entryTimestamp: ts, extremePrice: price, signal: action };\n } else if (action === 'SELL' || action === 'STRONG_SELL') {\n position = { side: 'short', entryPrice: price, entryTimestamp: ts, extremePrice: price, signal: action };\n }\n }\n }\n\n // Close any open position at the last VALID observed price.\n // Walking back ensures we don't close on a zero-price row.\n if (position) {\n let lastValidPrice: number | null = null;\n for (let i = tokenRows.length - 1; i >= 0; i--) {\n const p = tokenRows[i]!.price;\n if (Number.isFinite(p) && p > 0) { lastValidPrice = p; break; }\n }\n if (lastValidPrice !== null) {\n const pnlPercent = position.side === 'short'\n ? (position.entryPrice - lastValidPrice) / position.entryPrice\n : (lastValidPrice - position.entryPrice) / position.entryPrice;\n const lastTs = Date.parse(tokenRows[tokenRows.length - 1]!.timestamp);\n equity *= 1 + pnlPercent;\n equityCurve.push(equity);\n returns.push(pnlPercent);\n trades.push({ pnlPercent, durationMs: lastTs - position.entryTimestamp });\n }\n }\n\n // Metrics\n const totalReturn = equity - 1.0;\n const winRate = returns.length > 0 ? returns.filter((r) => r > 0).length / returns.length : 0;\n\n // Sharpe from per-trade returns, annualized using ACTUAL average hold\n // duration (not a fixed 52/year guess). Same formula as PortfolioTracker.\n let sharpeRatio = 0;\n if (returns.length > 1) {\n const mean = returns.reduce((a, b) => a + b, 0) / returns.length;\n const variance = returns.reduce((s, r) => s + (r - mean) ** 2, 0) / (returns.length - 1);\n const stdDev = Math.sqrt(variance);\n if (stdDev > 0) {\n const avgHoldDays = trades.length > 0\n ? trades.reduce((s, t) => s + t.durationMs, 0) / trades.length / (1000 * 60 * 60 * 24)\n : 1;\n const tradesPerYear = Math.max(252 / Math.max(avgHoldDays, 1), 1);\n sharpeRatio = (mean / stdDev) * Math.sqrt(tradesPerYear);\n }\n }\n\n // Max drawdown from equity curve\n let peak = equityCurve[0]!;\n let maxDrawdown = 0;\n for (const v of equityCurve) {\n if (v > peak) peak = v;\n const dd = (peak - v) / peak;\n if (dd > maxDrawdown) maxDrawdown = dd;\n }\n\n return {\n tokenId: tokenRows[0]?.tokenId ?? '',\n totalReturn,\n sharpeRatio,\n maxDrawdown,\n winRate,\n numTrades: returns.length,\n };\n}\n\n// ── Public driver ──\n\n/**\n * Sweep all WEIGHT_PROFILES × BUY_THRESHOLDS × SELL_THRESHOLDS against\n * a captured signal-history.jsonl. Returns the same CalibrationResult[]\n * shape as the candle-based calibrator so `formatCalibrationTable` works.\n */\nexport async function runHistoryReplay(\n options: ReplayCalibratorOptions = {},\n): Promise<CalibrationResult[]> {\n const historyPath = options.historyPath ?? join(homedir(), '.sherwood', 'agent', 'signal-history.jsonl');\n const log = options.onProgress ?? (() => {});\n const useRegime = options.useRegime ?? true;\n\n log(`Loading ${historyPath}...`);\n let allRows = await loadSignalHistory(historyPath);\n log(`Loaded ${allRows.length} rows`);\n\n if (options.lastDays !== undefined && options.lastDays > 0) {\n const cutoff = Date.now() - options.lastDays * 24 * 60 * 60 * 1000;\n const before = allRows.length;\n allRows = allRows.filter((r) => Date.parse(r.timestamp) >= cutoff);\n log(`Filtered to last ${options.lastDays}d: ${allRows.length} rows (dropped ${before - allRows.length})`);\n }\n\n const grouped = groupByToken(allRows);\n let tokens = options.tokens ?? Array.from(grouped.keys());\n // Drop tokens with too few observations to produce meaningful trades\n const MIN_ROWS = 5;\n tokens = tokens.filter((t) => (grouped.get(t)?.length ?? 0) >= MIN_ROWS);\n log(`Replaying ${tokens.length} tokens (>=${MIN_ROWS} observations each)`);\n\n const configs: CalibrationConfig[] = [];\n for (const [profileName, weights] of Object.entries(WEIGHT_PROFILES)) {\n for (const buyThreshold of BUY_THRESHOLDS) {\n for (const sellThreshold of SELL_THRESHOLDS) {\n if (buyThreshold <= sellThreshold) continue; // skip invalid\n configs.push({ profileName, weights, buyThreshold, sellThreshold });\n }\n }\n }\n log(`Sweeping ${configs.length} configs across ${tokens.length} tokens`);\n\n const results: CalibrationResult[] = configs.map((config) => ({\n config,\n tokenResults: [],\n avgReturn: 0,\n avgSharpe: 0,\n worstDrawdown: 0,\n totalTrades: 0,\n }));\n\n // Replay loop — pure of network IO, runs in seconds even for 200 × 15.\n let processed = 0;\n for (const token of tokens) {\n const rows = grouped.get(token)!;\n for (let ci = 0; ci < configs.length; ci++) {\n const cfg = configs[ci]!;\n const tokenResult = replayToken(rows, cfg, useRegime);\n results[ci]!.tokenResults.push(tokenResult);\n }\n processed++;\n log(` ${token}: ${rows.length} rows, ${configs.length} configs done (${processed}/${tokens.length})`);\n }\n\n // Aggregate\n for (const r of results) {\n const tr = r.tokenResults;\n if (tr.length === 0) continue;\n r.avgReturn = tr.reduce((s, x) => s + x.totalReturn, 0) / tr.length;\n r.avgSharpe = tr.reduce((s, x) => s + x.sharpeRatio, 0) / tr.length;\n r.worstDrawdown = Math.max(...tr.map((x) => x.maxDrawdown));\n r.totalTrades = tr.reduce((s, x) => s + x.numTrades, 0);\n }\n\n // Sort: 0-trade configs sink to bottom (same logic as candle calibrator).\n results.sort((a, b) => {\n const aZero = a.totalTrades === 0;\n const bZero = b.totalTrades === 0;\n if (aZero !== bZero) return aZero ? 1 : -1;\n if (Math.abs(b.avgSharpe - a.avgSharpe) > 0.001) return b.avgSharpe - a.avgSharpe;\n return b.avgReturn - a.avgReturn;\n });\n\n await saveResults(results);\n return results;\n}\n\nasync function saveResults(results: CalibrationResult[]): Promise<void> {\n const dir = join(homedir(), '.sherwood', 'agent');\n await mkdir(dir, { recursive: true });\n const path = join(dir, 'replay-calibration-results.json');\n const payload = {\n timestamp: new Date().toISOString(),\n totalConfigs: results.length,\n source: 'signal-history.jsonl replay',\n results: results.map((r, i) => ({\n rank: i + 1,\n profile: r.config.profileName,\n buyThreshold: r.config.buyThreshold,\n sellThreshold: r.config.sellThreshold,\n weights: r.config.weights,\n avgReturn: r.avgReturn,\n avgSharpe: r.avgSharpe,\n worstDrawdown: r.worstDrawdown,\n totalTrades: r.totalTrades,\n tokenResults: r.tokenResults,\n })),\n };\n await writeFile(path, JSON.stringify(payload, null, 2), 'utf-8');\n}\n","/**\n * Telegram-optimized alert formatting for trading notifications.\n * Formats scan results, regime changes, and alerts with emoji indicators.\n */\n\nimport type { Alert, TokenAnalysis } from \"./index.js\";\nimport type { MarketRegime } from \"./regime.js\";\n\nexport class AlertFormatter {\n\n /**\n * Format alerts for Telegram with priority grouping and emoji indicators.\n * Returns null if no alerts to display.\n */\n static formatForTelegram(alerts: Alert[]): string | null {\n if (alerts.length === 0) return null;\n\n // Group by priority\n const grouped: Record<string, Alert[]> = {\n critical: [],\n high: [],\n medium: [],\n low: []\n };\n\n for (const alert of alerts) {\n grouped[alert.priority].push(alert);\n }\n\n const lines: string[] = [];\n lines.push(\"🚨 *TRADING ALERTS*\");\n\n // Critical alerts\n if (grouped.critical.length > 0) {\n lines.push(\"\\n🔴 *CRITICAL*\");\n for (const alert of grouped.critical.slice(0, 3)) {\n lines.push(`• ${alert.title}`);\n if (alert.tokenId !== 'bitcoin') {\n lines.push(` _${alert.tokenId.toUpperCase()}_`);\n }\n }\n }\n\n // High priority alerts\n if (grouped.high.length > 0) {\n lines.push(\"\\n🟡 *HIGH*\");\n for (const alert of grouped.high.slice(0, 3)) {\n lines.push(`• ${alert.title}`);\n if (alert.tokenId !== 'bitcoin') {\n lines.push(` _${alert.tokenId.toUpperCase()}_`);\n }\n }\n }\n\n // Medium priority alerts\n if (grouped.medium.length > 0) {\n lines.push(\"\\n🔵 *MEDIUM*\");\n for (const alert of grouped.medium.slice(0, 2)) {\n lines.push(`• ${alert.title}`);\n }\n }\n\n // Low priority alerts (only show count)\n if (grouped.low.length > 0) {\n lines.push(`\\n⚫ ${grouped.low.length} low priority alerts`);\n }\n\n const result = lines.join(\"\\n\");\n\n // Telegram message limit is 4096 chars\n if (result.length > 4000) {\n return result.substring(0, 3900) + \"\\n\\n_...truncated_\";\n }\n\n return result;\n }\n\n /**\n * Format comprehensive scan summary with opportunities, top movers, and alerts.\n */\n static formatScanSummary(\n results: TokenAnalysis[],\n alerts: Alert[],\n regime?: MarketRegime\n ): string {\n const lines: string[] = [];\n\n // Header with timestamp\n lines.push(\"📊 *MARKET SCAN SUMMARY*\");\n lines.push(`_${new Date().toLocaleTimeString()}_`);\n\n // Market regime\n if (regime) {\n const regimeEmoji = regime === \"trending-up\" ? \"📈\" :\n regime === \"trending-down\" ? \"📉\" :\n regime === \"ranging\" ? \"↔️\" :\n regime === \"high-volatility\" ? \"🌋\" : \"🔄\";\n lines.push(`\\n${regimeEmoji} *Regime:* ${regime.toUpperCase().replace('-', ' ')}`);\n }\n\n // Check if all signals are HOLD (brief mode)\n const allHold = results.every(r => r.decision.action === \"HOLD\");\n\n if (allHold) {\n // Brief format for all HOLD scenario\n const strongest = results.reduce((max, r) =>\n r.decision.score > max.decision.score ? r : max\n );\n const weakest = results.reduce((min, r) =>\n r.decision.score < min.decision.score ? r : min\n );\n\n const fearGreed = results[0]?.data.fearAndGreed;\n const fgText = fearGreed ?\n (fearGreed < 25 ? \"Extreme Fear\" :\n fearGreed < 40 ? \"Fear\" :\n fearGreed < 60 ? \"Neutral\" :\n fearGreed < 75 ? \"Greed\" : \"Extreme Greed\") : \"Unknown\";\n\n lines.push(`\\n🔄 *All positions HOLD*`);\n lines.push(`💪 Strongest: ${strongest.token.toUpperCase()} (+${strongest.decision.score.toFixed(2)})`);\n lines.push(`💤 Weakest: ${weakest.token.toUpperCase()} (${weakest.decision.score.toFixed(2)})`);\n if (fearGreed) {\n lines.push(`😱 F&G: ${fearGreed} (${fgText})`);\n }\n } else {\n // Full format with opportunities and movers\n\n // Opportunities (score > 0.3)\n const opportunities = results.filter(r =>\n r.decision.score > 0.3 && (r.decision.action === \"BUY\" || r.decision.action === \"STRONG_BUY\")\n ).sort((a, b) => b.decision.score - a.decision.score);\n\n if (opportunities.length > 0) {\n lines.push(\"\\n💡 *OPPORTUNITIES*\");\n for (const opp of opportunities.slice(0, 3)) {\n const action = opp.decision.action === \"STRONG_BUY\" ? \"🚀 STRONG BUY\" : \"📈 BUY\";\n lines.push(`${action} ${opp.token.toUpperCase()} (${opp.decision.score.toFixed(2)})`);\n\n // Top signal\n const topSignal = opp.decision.signals\n .filter(s => Math.abs(s.value) > 0.1)\n .sort((a, b) => Math.abs(b.value) - Math.abs(a.value))[0];\n\n if (topSignal) {\n const signalEmoji = topSignal.value > 0 ? \"⬆️\" : \"⬇️\";\n lines.push(` ${signalEmoji} ${topSignal.source}: ${topSignal.value > 0 ? \"+\" : \"\"}${topSignal.value.toFixed(2)}`);\n }\n }\n }\n\n // Top movers (biggest absolute score changes)\n const topMovers = results\n .filter(r => Math.abs(r.decision.score) > 0.2)\n .sort((a, b) => Math.abs(b.decision.score) - Math.abs(a.decision.score))\n .slice(0, 3);\n\n if (topMovers.length > 0) {\n lines.push(\"\\n🎯 *TOP MOVERS*\");\n for (const mover of topMovers) {\n const scoreEmoji = mover.decision.score > 0 ? \"📈\" : \"📉\";\n lines.push(`${scoreEmoji} ${mover.token.toUpperCase()}: ${mover.decision.score > 0 ? \"+\" : \"\"}${mover.decision.score.toFixed(2)}`);\n }\n }\n }\n\n // Alerts section\n const alertText = this.formatForTelegram(alerts);\n if (alertText) {\n lines.push(`\\n${alertText}`);\n }\n\n const result = lines.join(\"\\n\");\n\n // Respect Telegram limit\n if (result.length > 4000) {\n return result.substring(0, 3900) + \"\\n\\n_...truncated_\";\n }\n\n return result;\n }\n}","/**\n * Execution pipeline - bridges scanner results to actionable trade proposals.\n * Converts high-confidence analysis into sized position recommendations.\n */\n\nimport type { TokenAnalysis } from \"./index.js\";\nimport type { TechnicalSignals } from \"./technical.js\";\nimport type { Candle } from \"./technical.js\";\nimport { calculateATR } from \"./technical.js\";\n\nexport type TradeAction = \"LONG\" | \"SHORT\";\n\nexport interface TradeProposal {\n tokenId: string;\n symbol: string;\n action: TradeAction;\n entryPrice: number;\n stopLoss: number;\n takeProfit: number;\n positionSizeUsd: number;\n leverage: number;\n confidence: number;\n signals: string[]; // Top 3 signal names\n regime: string;\n timestamp: number;\n}\n\n/**\n * Score-weighted position sizing config.\n *\n * When enabled, risk percentage scales linearly with score magnitude\n * across the [floor, ceiling] band. Score at the floor → minRiskPct.\n * Score at or above the ceiling → maxRiskPct.\n */\nexport interface ScaledSizingConfig {\n enabled: boolean;\n floor: number;\n ceiling: number;\n minRiskPct: number;\n maxRiskPct: number;\n}\n\nexport const DEFAULT_SCALED_SIZING: ScaledSizingConfig = {\n enabled: false,\n floor: 0.30,\n ceiling: 0.60,\n minRiskPct: 0.005,\n maxRiskPct: 0.02,\n};\n\nexport interface ProposalOptions {\n portfolioValueUsd?: number;\n scaledSizing?: ScaledSizingConfig;\n}\n\nexport class ExecutionPipeline {\n /**\n * Generate trade proposals from analysis results.\n * Only creates proposals for high-confidence opportunities — uses the\n * regime-conditional BUY/SELL threshold from the decision (defaults to\n * ±0.4 if the decision lacks regime context).\n *\n * Optional ProposalOptions.scaledSizing enables score-weighted sizing.\n * Previously this was a mutable static field — fixed per code review\n * to eliminate cross-invocation leakage in long-lived processes.\n */\n static generateProposals(\n analyses: TokenAnalysis[],\n portfolioValueOrOpts: number | ProposalOptions = 10000,\n optsArg?: ProposalOptions,\n ): TradeProposal[] {\n // Backward-compatible call shape: either generateProposals(analyses, 10000)\n // or generateProposals(analyses, { portfolioValueUsd, scaledSizing }).\n const opts: ProposalOptions =\n typeof portfolioValueOrOpts === 'number'\n ? { portfolioValueUsd: portfolioValueOrOpts, ...(optsArg ?? {}) }\n : (portfolioValueOrOpts ?? {});\n const portfolioValueUsd = opts.portfolioValueUsd ?? 10000;\n const scaledSizing = opts.scaledSizing ?? DEFAULT_SCALED_SIZING;\n const proposals: TradeProposal[] = [];\n\n for (const analysis of analyses) {\n const { decision, token, data } = analysis;\n\n // Skip HOLD signals\n if (decision.action === \"HOLD\") {\n continue;\n }\n\n // Filter: score must clear the regime-conditional action threshold\n // and confidence must exceed 50%. Falls back to ±0.4 for backward compat.\n // When scaledSizing is on, the floor drops to scaledSizing.floor — the\n // point is to take sub-threshold trades at proportionally smaller size,\n // not to filter them out before sizing kicks in.\n const baseBuyFloor = decision.thresholds?.buy ?? 0.4;\n const baseSellFloor = decision.thresholds?.sell ?? -0.4;\n const buyFloor = scaledSizing.enabled\n ? Math.min(baseBuyFloor, scaledSizing.floor)\n : baseBuyFloor;\n const sellFloor = scaledSizing.enabled\n ? Math.max(baseSellFloor, -scaledSizing.floor)\n : baseSellFloor;\n const clearsBuy = decision.score >= buyFloor;\n const clearsSell = decision.score <= sellFloor;\n if (!clearsBuy && !clearsSell) {\n continue;\n }\n if (decision.confidence <= 0.5) {\n continue;\n }\n\n // Determine trade direction\n const isLong = decision.score > 0;\n const action: TradeAction = isLong ? \"LONG\" : \"SHORT\";\n\n // Get current price (use entry price from technical data or mock)\n const entryPrice = this.getEntryPrice(data.technicalSignals);\n if (!entryPrice) continue;\n\n // Calculate ATR for stop loss\n const atr = this.getATR(data.technicalSignals);\n if (!atr) continue;\n\n // Calculate stop loss (entry +/- 2*ATR)\n const stopLoss = isLong\n ? entryPrice - (2 * atr)\n : entryPrice + (2 * atr);\n\n // Calculate take profit (2.5:1 R:R)\n const riskAmount = Math.abs(entryPrice - stopLoss);\n const takeProfit = isLong\n ? entryPrice + (2.5 * riskAmount)\n : entryPrice - (2.5 * riskAmount);\n\n // Position sizing — fixed 2% by default, score-scaled if enabled.\n // Score-scaled: bigger conviction → bigger size, smaller conviction → smaller size.\n // The point: take more sub-0.4 trades but with proportionally smaller exposure.\n let riskPercentage: number;\n if (scaledSizing.enabled) {\n const cfg = scaledSizing;\n const absScore = Math.abs(decision.score);\n const t = Math.min(1, Math.max(0, (absScore - cfg.floor) / (cfg.ceiling - cfg.floor)));\n riskPercentage = cfg.minRiskPct + t * (cfg.maxRiskPct - cfg.minRiskPct);\n } else {\n riskPercentage = 0.02;\n }\n const positionSizeUsd = (portfolioValueUsd * riskPercentage) / (Math.abs(entryPrice - stopLoss) / entryPrice);\n\n // Calculate leverage (size / max 10% portfolio allocation, capped at 5x)\n const maxAllocation = portfolioValueUsd * 0.10;\n const leverage = Math.min(5.0, positionSizeUsd / maxAllocation);\n\n // Get top 3 signals\n const topSignals = decision.signals\n .filter(s => Math.abs(s.value) > 0.05)\n .sort((a, b) => Math.abs(b.value) - Math.abs(a.value))\n .slice(0, 3)\n .map(s => s.source);\n\n // Get regime\n const regime = analysis.regime?.regime || \"unknown\";\n\n // Resolve token symbol (fallback to tokenId if not available)\n const symbol = this.resolveSymbol(token);\n\n const proposal: TradeProposal = {\n tokenId: token,\n symbol,\n action,\n entryPrice,\n stopLoss,\n takeProfit,\n positionSizeUsd,\n leverage,\n confidence: decision.confidence,\n signals: topSignals,\n regime,\n timestamp: Date.now(),\n };\n\n proposals.push(proposal);\n }\n\n return proposals.sort((a, b) => b.confidence - a.confidence);\n }\n\n /**\n * Format trade proposals for human review.\n */\n static formatProposals(proposals: TradeProposal[]): string {\n if (proposals.length === 0) {\n return \"📝 *TRADE PROPOSALS*\\n\\n_No actionable opportunities found._\\n_Criteria: Score > 0.4, Confidence > 50%_\";\n }\n\n const lines: string[] = [];\n lines.push(\"📝 *TRADE PROPOSALS*\");\n lines.push(\"_⚠️ For review only - NO auto-execution_\");\n\n for (const proposal of proposals.slice(0, 5)) {\n const directionEmoji = proposal.action === \"LONG\" ? \"🟢\" : \"🔴\";\n const confidenceBar = \"█\".repeat(Math.floor(proposal.confidence * 10));\n\n lines.push(`\\n${directionEmoji} *${proposal.action} ${proposal.symbol}*`);\n lines.push(`📈 Entry: $${proposal.entryPrice.toFixed(4)}`);\n lines.push(`🛑 Stop: $${proposal.stopLoss.toFixed(4)} (${this.calculatePctChange(proposal.entryPrice, proposal.stopLoss)}%)`);\n lines.push(`🎯 Target: $${proposal.takeProfit.toFixed(4)} (${this.calculatePctChange(proposal.entryPrice, proposal.takeProfit)}%)`);\n lines.push(`💰 Size: $${proposal.positionSizeUsd.toFixed(0)} (${proposal.leverage.toFixed(1)}x leverage)`);\n lines.push(`✅ Confidence: ${(proposal.confidence * 100).toFixed(0)}% ${confidenceBar}`);\n\n // Top signals\n if (proposal.signals.length > 0) {\n lines.push(`🔍 Signals: ${proposal.signals.slice(0, 2).join(\", \")}`);\n }\n\n // Market regime\n const regimeEmoji = proposal.regime === \"trending-up\" ? \"📈\" :\n proposal.regime === \"trending-down\" ? \"📉\" :\n proposal.regime === \"ranging\" ? \"↔️\" :\n proposal.regime === \"high-volatility\" ? \"🌋\" : \"🔄\";\n lines.push(`${regimeEmoji} Regime: ${proposal.regime}`);\n }\n\n if (proposals.length > 5) {\n lines.push(`\\n_...${proposals.length - 5} more proposals available_`);\n }\n\n lines.push(`\\n_Generated: ${new Date().toLocaleTimeString()}_`);\n\n const result = lines.join(\"\\n\");\n\n // Respect character limits\n if (result.length > 4000) {\n return result.substring(0, 3900) + \"\\n\\n_...truncated_\";\n }\n\n return result;\n }\n\n /**\n * Extract entry price from technical signals or use fallback.\n */\n private static getEntryPrice(technicalSignals?: TechnicalSignals): number | null {\n if (!technicalSignals) return null;\n\n // Use VWAP as entry price if available, fallback to EMA8\n if (technicalSignals.vwap > 0) {\n return technicalSignals.vwap;\n }\n\n if (technicalSignals.ema.ema8 > 0) {\n return technicalSignals.ema.ema8;\n }\n\n return null;\n }\n\n /**\n * Extract ATR from technical signals.\n */\n private static getATR(technicalSignals?: TechnicalSignals): number | null {\n if (!technicalSignals) return null;\n return technicalSignals.atr > 0 ? technicalSignals.atr : null;\n }\n\n /**\n * Resolve token symbol from token ID.\n */\n private static resolveSymbol(tokenId: string): string {\n const symbolMap: Record<string, string> = {\n \"bitcoin\": \"BTC\",\n \"ethereum\": \"ETH\",\n \"solana\": \"SOL\",\n \"aave\": \"AAVE\",\n \"uniswap\": \"UNI\",\n \"chainlink\": \"LINK\",\n \"polygon\": \"MATIC\",\n \"avalanche-2\": \"AVAX\",\n \"cardano\": \"ADA\",\n \"polkadot\": \"DOT\",\n };\n\n return symbolMap[tokenId] || tokenId.toUpperCase().slice(0, 6);\n }\n\n /**\n * Calculate percentage change between two prices.\n */\n private static calculatePctChange(from: number, to: number): string {\n const change = ((to - from) / from) * 100;\n return change >= 0 ? `+${change.toFixed(1)}` : change.toFixed(1);\n }\n}","/**\n * Signal activity audit — reads signal-history.jsonl and reports which\n * signal categories are actually firing, so dead weights can be pruned.\n *\n * A signal that contributes ~0 on every run isn't \"neutral information\" —\n * it's noise that compresses the aggregate score. Drop it from the weight\n * vector and renormalize so real signals aren't diluted.\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { getSignalFilePath } from \"./signal-logger.js\";\nimport type { SignalLogEntry } from \"./signal-logger.js\";\n\nconst FIRE_EPSILON = 0.05; // |value| below this counts as \"silent\"\n\n/** Category each strategy signal maps to (mirrors scoring.ts SIGNAL_CATEGORY_MAP). */\nconst SIGNAL_TO_CATEGORY: Record<string, string> = {\n // Direct categories\n technical: \"technical\",\n sentiment: \"sentiment\",\n onchain: \"onchain\",\n fundamental: \"fundamental\",\n event: \"event\",\n smartMoney: \"smartMoney\",\n\n // Active strategy signals\n breakoutOnChain: \"technical\",\n multiTimeframe: \"technical\",\n crossSectionalMomentum: \"technical\",\n tradingviewSignal: \"technical\",\n btcNetworkHealth: \"technical\",\n kronosVolForecast: \"technical\",\n dexFlow: \"onchain\",\n fundingRate: \"onchain\",\n hyperliquidFlow: \"onchain\",\n narrativeVacuum: \"onchain\",\n sentimentContrarian: \"sentiment\",\n socialVolume: \"sentiment\",\n predictionMarket: \"event\",\n whaleIntent: \"smartMoney\",\n flowIntelligence: \"smartMoney\",\n};\n\nexport interface SignalStats {\n name: string;\n category: string;\n observations: number;\n fireRate: number; // fraction of observations where |value| > epsilon\n meanAbsValue: number; // average |value|\n meanConfidence: number;\n directionalBias: number; // mean(value) — negative = mostly bearish, positive = bullish\n}\n\nexport interface CategoryStats {\n category: string;\n signalNames: string[];\n observations: number; // total signal-observations (multiple signals per category possible)\n fireRate: number;\n meanAbsValue: number;\n recommendation: \"keep\" | \"drop\" | \"review\";\n}\n\nexport interface AuditResult {\n filePath: string;\n totalEntries: number;\n dateRange: { from: string; to: string } | null;\n perSignal: SignalStats[];\n perCategory: CategoryStats[];\n}\n\n/**\n * Read the signal-history JSONL file and compute per-signal + per-category\n * activity statistics.\n */\nexport async function auditSignalHistory(\n filePath: string = getSignalFilePath(),\n): Promise<AuditResult> {\n let raw: string;\n try {\n raw = await readFile(filePath, \"utf-8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n return {\n filePath,\n totalEntries: 0,\n dateRange: null,\n perSignal: [],\n perCategory: [],\n };\n }\n throw err;\n }\n\n const lines = raw.split(\"\\n\").filter((l) => l.trim().length > 0);\n const entries: SignalLogEntry[] = [];\n for (const line of lines) {\n try {\n entries.push(JSON.parse(line) as SignalLogEntry);\n } catch {\n // skip malformed lines\n }\n }\n\n if (entries.length === 0) {\n return {\n filePath,\n totalEntries: 0,\n dateRange: null,\n perSignal: [],\n perCategory: [],\n };\n }\n\n // Per-signal aggregation\n const bySignal = new Map<\n string,\n { values: number[]; confidences: number[] }\n >();\n\n for (const entry of entries) {\n for (const sig of entry.signals) {\n if (!bySignal.has(sig.name)) {\n bySignal.set(sig.name, { values: [], confidences: [] });\n }\n const bucket = bySignal.get(sig.name)!;\n bucket.values.push(sig.value);\n bucket.confidences.push(sig.confidence);\n }\n }\n\n const perSignal: SignalStats[] = [];\n for (const [name, bucket] of bySignal) {\n const obs = bucket.values.length;\n const fires = bucket.values.filter((v) => Math.abs(v) > FIRE_EPSILON).length;\n const meanAbsValue =\n bucket.values.reduce((a, v) => a + Math.abs(v), 0) / obs;\n const meanConfidence =\n bucket.confidences.reduce((a, c) => a + c, 0) / obs;\n const directionalBias = bucket.values.reduce((a, v) => a + v, 0) / obs;\n\n perSignal.push({\n name,\n category: SIGNAL_TO_CATEGORY[name] ?? \"unknown\",\n observations: obs,\n fireRate: fires / obs,\n meanAbsValue,\n meanConfidence,\n directionalBias,\n });\n }\n\n perSignal.sort((a, b) => b.fireRate - a.fireRate);\n\n // Per-category aggregation\n const byCategory = new Map<\n string,\n { signals: Set<string>; obs: number; fires: number; absSum: number }\n >();\n\n for (const sig of perSignal) {\n if (!byCategory.has(sig.category)) {\n byCategory.set(sig.category, {\n signals: new Set(),\n obs: 0,\n fires: 0,\n absSum: 0,\n });\n }\n const bucket = byCategory.get(sig.category)!;\n bucket.signals.add(sig.name);\n bucket.obs += sig.observations;\n bucket.fires += sig.fireRate * sig.observations;\n bucket.absSum += sig.meanAbsValue * sig.observations;\n }\n\n const perCategory: CategoryStats[] = [];\n for (const [category, bucket] of byCategory) {\n const fireRate = bucket.obs > 0 ? bucket.fires / bucket.obs : 0;\n const meanAbsValue = bucket.obs > 0 ? bucket.absSum / bucket.obs : 0;\n\n let recommendation: \"keep\" | \"drop\" | \"review\";\n if (fireRate >= 0.4) recommendation = \"keep\";\n else if (fireRate < 0.1) recommendation = \"drop\";\n else recommendation = \"review\";\n\n perCategory.push({\n category,\n signalNames: [...bucket.signals],\n observations: bucket.obs,\n fireRate,\n meanAbsValue,\n recommendation,\n });\n }\n\n perCategory.sort((a, b) => b.fireRate - a.fireRate);\n\n const timestamps = entries.map((e) => e.timestamp).sort();\n\n return {\n filePath,\n totalEntries: entries.length,\n dateRange: {\n from: timestamps[0]!,\n to: timestamps[timestamps.length - 1]!,\n },\n perSignal,\n perCategory,\n };\n}\n\n// ── Diff against a saved baseline ──\n\nexport interface SignalDiffEntry {\n name: string;\n category: string;\n baseline: { fireRate: number; meanAbsValue: number; observations: number };\n current: { fireRate: number; meanAbsValue: number; observations: number };\n fireRateDelta: number;\n meanAbsValueDelta: number;\n status: \"improved\" | \"regressed\" | \"stable\" | \"new\" | \"removed\";\n}\n\nexport interface AuditDiff {\n baselineDateRange: { from: string; to: string } | null;\n currentDateRange: { from: string; to: string } | null;\n perSignal: SignalDiffEntry[];\n perCategory: SignalDiffEntry[];\n}\n\nconst DELTA_EPSILON = 0.05; // fire-rate deltas under 5pp are \"stable\"\n\nfunction classifyStatus(\n baselineRate: number | null,\n currentRate: number | null,\n): SignalDiffEntry[\"status\"] {\n if (baselineRate === null) return \"new\";\n if (currentRate === null) return \"removed\";\n const delta = currentRate - baselineRate;\n if (Math.abs(delta) < DELTA_EPSILON) return \"stable\";\n return delta > 0 ? \"improved\" : \"regressed\";\n}\n\n/** Compare a current audit result against a saved baseline. */\nexport function diffAudits(baseline: AuditResult, current: AuditResult): AuditDiff {\n const baseSignals = new Map(baseline.perSignal.map((s) => [s.name, s]));\n const currSignals = new Map(current.perSignal.map((s) => [s.name, s]));\n const allSignalNames = new Set([...baseSignals.keys(), ...currSignals.keys()]);\n\n const perSignal: SignalDiffEntry[] = [];\n for (const name of allSignalNames) {\n const b = baseSignals.get(name);\n const c = currSignals.get(name);\n perSignal.push({\n name,\n category: (c ?? b)?.category ?? \"unknown\",\n baseline: {\n fireRate: b?.fireRate ?? 0,\n meanAbsValue: b?.meanAbsValue ?? 0,\n observations: b?.observations ?? 0,\n },\n current: {\n fireRate: c?.fireRate ?? 0,\n meanAbsValue: c?.meanAbsValue ?? 0,\n observations: c?.observations ?? 0,\n },\n fireRateDelta: (c?.fireRate ?? 0) - (b?.fireRate ?? 0),\n meanAbsValueDelta: (c?.meanAbsValue ?? 0) - (b?.meanAbsValue ?? 0),\n status: classifyStatus(b?.fireRate ?? null, c?.fireRate ?? null),\n });\n }\n perSignal.sort((a, b) => Math.abs(b.fireRateDelta) - Math.abs(a.fireRateDelta));\n\n const baseCats = new Map(baseline.perCategory.map((c) => [c.category, c]));\n const currCats = new Map(current.perCategory.map((c) => [c.category, c]));\n const allCats = new Set([...baseCats.keys(), ...currCats.keys()]);\n\n const perCategory: SignalDiffEntry[] = [];\n for (const cat of allCats) {\n const b = baseCats.get(cat);\n const c = currCats.get(cat);\n perCategory.push({\n name: cat,\n category: cat,\n baseline: {\n fireRate: b?.fireRate ?? 0,\n meanAbsValue: b?.meanAbsValue ?? 0,\n observations: b?.observations ?? 0,\n },\n current: {\n fireRate: c?.fireRate ?? 0,\n meanAbsValue: c?.meanAbsValue ?? 0,\n observations: c?.observations ?? 0,\n },\n fireRateDelta: (c?.fireRate ?? 0) - (b?.fireRate ?? 0),\n meanAbsValueDelta: (c?.meanAbsValue ?? 0) - (b?.meanAbsValue ?? 0),\n status: classifyStatus(b?.fireRate ?? null, c?.fireRate ?? null),\n });\n }\n perCategory.sort((a, b) => Math.abs(b.fireRateDelta) - Math.abs(a.fireRateDelta));\n\n return {\n baselineDateRange: baseline.dateRange,\n currentDateRange: current.dateRange,\n perSignal,\n perCategory,\n };\n}\n\n/** Renormalize weights after dropping categories with fireRate below threshold. */\nexport function suggestRenormalizedWeights(\n currentWeights: Record<string, number>,\n perCategory: CategoryStats[],\n dropThreshold: number = 0.1,\n): Record<string, number> {\n const keepCategories = new Set(\n perCategory\n .filter((c) => c.fireRate >= dropThreshold && c.category !== \"unknown\")\n .map((c) => c.category),\n );\n\n // Sum retained weight\n let retainedSum = 0;\n for (const [cat, w] of Object.entries(currentWeights)) {\n if (keepCategories.has(cat)) retainedSum += w;\n }\n\n if (retainedSum === 0) return currentWeights; // nothing to renormalize\n\n const out: Record<string, number> = {};\n for (const [cat, w] of Object.entries(currentWeights)) {\n out[cat] = keepCategories.has(cat) ? w / retainedSum : 0;\n }\n return out;\n}\n"],"mappings":"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","names":["chalk","readFile","writeFile","mkdir","join","homedir","chalk","cacheKey","mkdir","readFile","writeFile","join","homedir","BASE_URL","sharedLastCallTime","cacheKey","mkdir","readFile","writeFile","join","homedir","join","cache","cacheKey","cache","join","CACHE_TTL","CACHE_TTL","execFile","join","existsSync","join","existsSync","CACHE_TTL","cache","cacheKey","execFile","CACHE_TTL","mkdir","join","homedir","createHash","readFile","writeFile","mkdir","join","homedir","join","homedir","createHash","readFile","mkdir","writeFile","mkdir","readFile","writeFile","dirname","cache","rename","cutoff","readFile","writeFile","mkdir","join","homedir","join","homedir","readFile","mkdir","cache","writeFile","readFile","writeFile","mkdir","join","homedir","join","homedir","readFile","mkdir","cache","writeFile","readFile","writeFile","mkdir","join","homedir","createHash","chalk","marketData","STOP_COOLDOWN_MS","mkdir","readFile","writeFile","join","homedir","join","homedir","readFile","mkdir","writeFile","chalk","mkdir","appendFile","join","homedir","chalk","chalk","mkdir","readFile","writeFile","rename","dirname","join","homedir","chalk","chalk","MARGIN_FRACTION","newTrailingStop","chalk","chalk","mkdir","readFile","writeFile","rename","join","homedir","join","homedir","mkdir","chalk","appendFile","chalk","chalk","STOP_LOSS_PCT","TAKE_PROFIT_PCT","TIME_STOP_HOURS","TRAIL_PCT","mkdir","writeFile","join","homedir","join","homedir","mkdir","writeFile","readFile","mkdir","writeFile","join","homedir","readFile","join","homedir","saveResults","mkdir","writeFile","fearGreed","readFile","readFile","spinner","alerts","chalk","join","homedir","readFile","mkdir","writeFile","tokens"]}
1
+ {"version":3,"sources":["../src/commands/agent.ts","../src/agent/index.ts","../src/providers/data/defillama.ts","../src/providers/data/coingecko.ts","../src/providers/data/feargreed.ts","../src/providers/data/sentiment.ts","../src/agent/strategies/breakout-onchain.ts","../src/agent/strategies/funding-rate.ts","../src/providers/data/dexscreener.ts","../src/agent/strategies/hyperliquid-flow.ts","../src/agent/strategies/multi-timeframe.ts","../src/agent/strategies/cross-sectional-momentum.ts","../src/providers/data/tradingview.ts","../src/agent/strategies/tradingview-signal.ts","../src/agent/strategies/btc-network-health.ts","../src/agent/strategies/prediction-market.ts","../src/agent/strategies/social-volume.ts","../src/agent/strategies/kronos-vol-forecast.ts","../src/agent/strategies/narrative-vacuum.ts","../src/agent/strategies/whale-intent.ts","../src/agent/strategies/index.ts","../src/providers/data/funding-rate.ts","../src/providers/data/token-unlocks.ts","../src/providers/data/twitter.ts","../src/providers/fincept/bridge.ts","../src/providers/fincept/messari.ts","../src/providers/fincept/blockchain.ts","../src/providers/fincept/polymarket.ts","../src/providers/fincept/sentiment.ts","../src/providers/fincept/cryptocompare.ts","../src/providers/fincept/kronos.ts","../src/providers/fincept/defillama.ts","../src/providers/fincept/dexscreener.ts","../src/agent/signal-logger.ts","../src/agent/judge.ts","../src/agent/signal-smoother.ts","../src/agent/entry-gates.ts","../src/agent/regime.ts","../src/agent/correlation.ts","../src/agent/alerts.ts","../src/agent/token-selector.ts","../src/agent/loop.ts","../src/agent/executor.ts","../src/agent/portfolio.ts","../src/agent/risk.ts","../src/agent/reporter.ts","../src/agent/price-validator.ts","../src/agent/backtest.ts","../src/agent/calibrator.ts","../src/agent/replay-calibrator.ts","../src/agent/alert-formatter.ts","../src/agent/execution-pipeline.ts","../src/agent/signal-audit.ts"],"sourcesContent":["/**\n * CLI commands for the autonomous trading agent.\n * sherwood agent <analyze|signals|start|status|history|config|backtest>\n */\n\nimport { Command } from \"commander\";\nimport chalk from \"chalk\";\nimport { readFile, writeFile, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport ora from \"ora\";\nimport { TradingAgent } from \"../agent/index.js\";\nimport type { AgentConfig, TokenAnalysis, Alert } from \"../agent/index.js\";\nimport { getLatestSignals } from \"../agent/technical.js\";\nimport type { Candle } from \"../agent/technical.js\";\nimport { CoinGeckoProvider } from \"../providers/data/coingecko.js\";\nimport { DynamicTokenSelector } from \"../agent/token-selector.js\";\nimport { SentimentProvider } from \"../providers/data/sentiment.js\";\nimport {\n scoreTechnical,\n scoreSentiment,\n scoreOnChain,\n scoreFundamental,\n scoreEvent,\n} from \"../agent/scoring.js\";\nimport { AgentLoop } from \"../agent/loop.js\";\nimport type { LoopConfig } from \"../agent/loop.js\";\nimport { PortfolioTracker } from \"../agent/portfolio.js\";\nimport { RiskManager, DEFAULT_RISK_CONFIG } from \"../agent/risk.js\";\nimport type { RiskConfig } from \"../agent/risk.js\";\nimport { Reporter } from \"../agent/reporter.js\";\nimport { Backtester } from \"../agent/backtest.js\";\nimport type { BacktestConfig, WalkForwardConfig } from \"../agent/backtest.js\";\nimport { runCalibration, formatCalibrationTable, DEFAULT_CALIBRATION_TOKENS } from \"../agent/calibrator.js\";\nimport { runHistoryReplay } from \"../agent/replay-calibrator.js\";\nimport { AlertFormatter } from \"../agent/alert-formatter.js\";\nimport { ExecutionPipeline, DEFAULT_SCALED_SIZING } from \"../agent/execution-pipeline.js\";\nimport { auditSignalHistory, suggestRenormalizedWeights, diffAudits } from \"../agent/signal-audit.js\";\nimport type { AuditResult } from \"../agent/signal-audit.js\";\nimport { DEFAULT_WEIGHTS } from \"../agent/scoring.js\";\n\n// Expanded default universe — covers majors, L1/L2s, DeFi blue-chips, top\n// memes, and HL-listed alts so the scanner has more shots on goal each cycle.\n// Every token here has Hyperliquid perp coverage (executor.ts TOKEN_TO_ASSET_INDEX)\n// and CoinGecko OHLC. Use --auto for fully dynamic selection from HL volume.\nconst DEFAULT_TOKENS = [\n \"bitcoin\", \"ethereum\", \"solana\", // majors\n \"ripple\", \"dogecoin\", \"avalanche-2\", \"polkadot\", \"near\", // L1s\n \"arbitrum\", // L2\n \"aave\", \"uniswap\", \"chainlink\", // DeFi\n \"hyperliquid\", \"bittensor\", \"fartcoin\", // HL natives / themes\n];\n\nfunction makeConfig(overrides?: Partial<AgentConfig>): AgentConfig {\n return {\n tokens: DEFAULT_TOKENS,\n cycle: \"4h\",\n dryRun: true,\n maxPositionPct: 5,\n maxRiskPct: 20,\n useX402: true, // Paid signals (Nansen + Messari) on by default — opt out with --no-x402\n x402TopN: 1, // Only run x402 on top 1 token by free-signal score — 96 calls/day vs 288\n smoothFastSignals: true, // Rolling-window smoothing for noisy signals — prevents single-scan flicker\n // judge: undefined — off by default, enable with --use-judge\n ...overrides,\n };\n}\n\nexport function registerAgentCommands(program: Command): void {\n const agent = program.command(\"agent\").description(\"Autonomous trading agent\");\n\n // ── analyze ──\n agent\n .command(\"analyze\")\n .description(\"Analyze token(s) using multi-signal scoring\")\n .argument(\"[tokens...]\", \"Token IDs to analyze (e.g., ethereum bitcoin)\")\n .option(\"--all\", \"Analyze full watchlist\")\n .option(\"--auto\", \"Use dynamic token selection from Hyperliquid market data\")\n .option(\"--json\", \"Output as JSON\")\n .option(\"--no-x402\", \"Skip paid x402 data (Nansen smart-money, Messari fundamentals) — on by default\")\n .option(\"--telegram\", \"Output formatted summary for Telegram\")\n .option(\"--proposals\", \"Generate trade proposals for high-confidence opportunities\")\n .option(\"--weight-profile <name>\", \"Weight profile: default | majors | altcoin | sentHeavy | techHeavy (auto: majors for BTC/ETH/SOL)\")\n .option(\"--scaled-sizing\", \"Score-weighted position sizing (smaller positions for marginal scores)\")\n .option(\"--smooth\", \"Smooth fast signals with rolling 3-reading window\")\n .option(\"--use-judge\", \"Enable LLM judge to confirm/veto borderline trades (requires Anthropic API key)\")\n .option(\"--judge-model <model>\", \"Judge model ID (default: claude-haiku-4-5-20251001)\")\n .option(\"--judge-top-n <n>\", \"Max tokens to judge per cycle (default: 3)\", parseInt)\n .action(async (tokens: string[], options: { all?: boolean; auto?: boolean; json?: boolean; x402: boolean; telegram?: boolean; proposals?: boolean; weightProfile?: string; scaledSizing?: boolean; smooth?: boolean; useJudge?: boolean; judgeModel?: string; judgeTopN?: number }) => {\n let tokenList: string[];\n let selectionSummary: string | undefined;\n\n if (options.auto) {\n const spinner = ora(\"Fetching dynamic token selection from Hyperliquid...\").start();\n try {\n const selector = new DynamicTokenSelector();\n const selection = await selector.selectTokens();\n tokenList = selection.tokens;\n selectionSummary = selector.formatSelectionSummary(selection);\n spinner.stop();\n\n // Show selection summary\n console.log();\n console.log(selectionSummary);\n console.log();\n } catch (error) {\n spinner.fail(`Dynamic selection failed: ${(error as Error).message}`);\n console.log(\"Falling back to default tokens...\");\n tokenList = DEFAULT_TOKENS;\n }\n } else {\n tokenList = options.all ? DEFAULT_TOKENS : tokens.length > 0 ? tokens : DEFAULT_TOKENS;\n }\n\n const scaledSizing = options.scaledSizing\n ? { ...DEFAULT_SCALED_SIZING, enabled: true }\n : undefined;\n const config = makeConfig({\n tokens: tokenList, useX402: options.x402, weightProfile: options.weightProfile, smoothFastSignals: options.smooth,\n ...(options.useJudge ? {\n judge: {\n enabled: true,\n ...(options.judgeModel ? { model: options.judgeModel } : {}),\n ...(options.judgeTopN ? { topN: options.judgeTopN } : {}),\n },\n } : {}),\n });\n const tradingAgent = new TradingAgent(config);\n const spinner = ora(\"Analyzing tokens...\").start();\n\n try {\n spinner.text = \"Analyzing tokens and checking for alerts...\";\n const { analyses: results, alerts } = await tradingAgent.analyzeAllWithAlerts();\n spinner.stop();\n\n if (options.telegram) {\n // Output Telegram-formatted summary\n const regime = results[0]?.regime?.regime;\n const summary = AlertFormatter.formatScanSummary(results, alerts, regime);\n console.log(summary);\n return;\n }\n\n if (options.proposals) {\n // Output trade proposals\n const proposals = ExecutionPipeline.generateProposals(results, { scaledSizing });\n const formatted = ExecutionPipeline.formatProposals(proposals);\n console.log(formatted);\n return;\n }\n\n if (options.json) {\n console.log(JSON.stringify(results.map((r) => ({ token: r.token, decision: r.decision })), null, 2));\n } else {\n console.log(tradingAgent.formatAnalysis(results));\n\n // Show alerts if any\n if (alerts.length > 0) {\n console.log(chalk.bold(\"\\n\" + tradingAgent.formatAlerts(alerts)));\n console.log();\n }\n\n // Detailed signal breakdown\n for (const r of results) {\n console.log(chalk.bold(`\\n ${r.token.toUpperCase()} — Signal Breakdown`));\n console.log(chalk.dim(\" \" + \"─\".repeat(50)));\n\n // Show regime info if available\n if (r.regime) {\n const regimeColor = r.regime.regime === \"trending-up\" ? chalk.green :\n r.regime.regime === \"trending-down\" ? chalk.red :\n r.regime.regime === \"ranging\" ? chalk.yellow :\n r.regime.regime === \"high-volatility\" ? chalk.magenta :\n chalk.cyan;\n console.log(\n ` ${\"Market Regime\".padEnd(22)} ${regimeColor(\"▮\".repeat(10))} ${regimeColor(r.regime.regime.replace('-', ' '))} ${chalk.dim(r.regime.details.slice(0, 60))}`\n );\n }\n\n for (const signal of r.decision.signals) {\n const bar = renderBar(signal.value);\n const color =\n signal.value > 0.1\n ? chalk.green\n : signal.value < -0.1\n ? chalk.red\n : chalk.yellow;\n console.log(\n ` ${signal.source.padEnd(22)} ${bar} ${color(\n (signal.value >= 0 ? \"+\" : \"\") + signal.value.toFixed(3),\n )} ${chalk.dim(signal.details.slice(0, 60))}`,\n );\n }\n }\n console.log();\n }\n } catch (err) {\n spinner.fail(`Analysis failed: ${(err as Error).message}`);\n process.exitCode = 1;\n }\n });\n\n // ── signals ──\n agent\n .command(\"signals\")\n .description(\"Show current signal scores for a token\")\n .argument(\"<token>\", \"Token ID\")\n .action(async (token: string) => {\n const spinner = ora(`Fetching signals for ${token}...`).start();\n\n try {\n const cg = new CoinGeckoProvider();\n const sentimentProvider = new SentimentProvider();\n\n // Fetch OHLC\n spinner.text = \"Fetching OHLC data...\";\n const ohlcRaw = await cg.getOHLC(token, 30);\n const candles: Candle[] = ohlcRaw.map((c: number[]) => ({\n timestamp: c[0]!,\n open: c[1]!,\n high: c[2]!,\n low: c[3]!,\n close: c[4] ?? c[3]!,\n volume: 0,\n }));\n\n // Get volume data\n try {\n spinner.text = \"Fetching volume data...\";\n const marketData = await cg.getMarketData(token, 30);\n if (marketData?.total_volumes) {\n for (const candle of candles) {\n const nearest = marketData.total_volumes.reduce(\n (best: number[], v: number[]) =>\n Math.abs(v[0]! - candle.timestamp) < Math.abs(best[0]! - candle.timestamp)\n ? v\n : best,\n marketData.total_volumes[0]!,\n );\n candle.volume = nearest[1] ?? 0;\n }\n }\n } catch {\n // optional\n }\n\n const techSignals = getLatestSignals(candles);\n\n // Fetch sentiment\n spinner.text = \"Fetching sentiment...\";\n let fgValue = 50;\n let zScore = 0;\n try {\n const fgData = await sentimentProvider.getFearAndGreed();\n if (fgData.length > 0) {\n fgValue = fgData[0]!.value;\n zScore = sentimentProvider.computeSentimentZScore(fgData.map((d) => d.value));\n }\n } catch {\n // default values\n }\n\n spinner.stop();\n\n // Display\n console.log();\n console.log(chalk.bold(` ${token.toUpperCase()} — Detailed Signals`));\n console.log(chalk.dim(\" \" + \"═\".repeat(55)));\n\n // Technical\n console.log(chalk.bold.cyan(\"\\n Technical Indicators\"));\n console.log(` RSI(14): ${formatValue(techSignals.rsi, 30, 70, true)}`);\n console.log(` MACD: ${techSignals.macd.value.toFixed(4)}`);\n console.log(` MACD Signal: ${techSignals.macd.signal.toFixed(4)}`);\n console.log(` MACD Hist: ${formatValue(techSignals.macd.histogram, 0, 0, false)}`);\n console.log(` BB Upper: ${techSignals.bb.upper.toFixed(2)}`);\n console.log(` BB Middle: ${techSignals.bb.middle.toFixed(2)}`);\n console.log(` BB Lower: ${techSignals.bb.lower.toFixed(2)}`);\n console.log(` BB Width: ${techSignals.bb.width.toFixed(4)}`);\n console.log(` BB Squeeze: ${techSignals.bb.squeeze ? chalk.yellow(\"YES\") : \"No\"}`);\n console.log(` EMA(8): ${techSignals.ema.ema8.toFixed(2)}`);\n console.log(` EMA(21): ${techSignals.ema.ema21.toFixed(2)}`);\n console.log(` EMA(50): ${isNaN(techSignals.ema.ema50) ? chalk.dim(\"N/A\") : techSignals.ema.ema50.toFixed(2)}`);\n console.log(` EMA(200): ${isNaN(techSignals.ema.ema200) ? chalk.dim(\"N/A\") : techSignals.ema.ema200.toFixed(2)}`);\n console.log(` ATR(14): ${techSignals.atr.toFixed(2)}`);\n console.log(` VWAP: ${techSignals.vwap.toFixed(2)}`);\n console.log(` Vol Ratio: ${techSignals.volume.ratio.toFixed(2)}x avg`);\n\n // Sentiment\n console.log(chalk.bold.cyan(\"\\n Sentiment\"));\n console.log(` Fear & Greed: ${formatFearGreed(fgValue)}`);\n console.log(` Z-Score: ${zScore.toFixed(2)}`);\n\n // Signal scores\n const techScore = scoreTechnical(techSignals);\n const sentScore = scoreSentiment(fgValue, zScore);\n const onchainScore = scoreOnChain({});\n const fundScore = scoreFundamental({});\n const eventScore = scoreEvent({});\n\n console.log(chalk.bold.cyan(\"\\n Signal Scores\"));\n console.log(` Technical: ${renderBar(techScore.value)} ${colorValue(techScore.value)}`);\n console.log(` Sentiment: ${renderBar(sentScore.value)} ${colorValue(sentScore.value)}`);\n console.log(` On-Chain: ${renderBar(onchainScore.value)} ${colorValue(onchainScore.value)}`);\n console.log(` Fundamental: ${renderBar(fundScore.value)} ${colorValue(fundScore.value)}`);\n console.log(` Event: ${renderBar(eventScore.value)} ${colorValue(eventScore.value)}`);\n console.log();\n } catch (err) {\n spinner.fail(`Failed: ${(err as Error).message}`);\n process.exitCode = 1;\n }\n });\n\n // ── start ──\n agent\n .command(\"start\")\n .description(\"Start autonomous trading loop\")\n .option(\"--cycle <interval>\", \"Cycle interval (15m, 1h, 4h)\", \"4h\")\n .option(\"--dry-run\", \"Paper trading mode\", true)\n .option(\"--tokens <tokens>\", \"Comma-separated token list\")\n .option(\"--auto\", \"Use dynamic token selection from Hyperliquid market data\")\n .option(\"--log <path>\", \"Path to write cycle logs\")\n .option(\"--mode <mode>\", \"Execution mode: dry-run (default), hyperliquid-perp\", \"dry-run\")\n .option(\"--strategy-clone <address>\", \"Strategy clone address on HyperEVM (required for hyperliquid-perp)\")\n .option(\"--chain <chain>\", \"Chain for live execution (hyperevm, hyperevm-testnet)\", \"ethereum\")\n .option(\"--asset-index <n>\", \"HyperCore perp asset index (default: 3 = ETH)\")\n .option(\"--no-x402\", \"Skip paid x402 data (Nansen smart-money, Messari fundamentals) — on by default\")\n .option(\"--weight-profile <name>\", \"Weight profile: default | majors | altcoin | sentHeavy | techHeavy (auto: majors for BTC/ETH/SOL)\")\n .option(\"--scaled-sizing\", \"Score-weighted position sizing (smaller positions for marginal scores)\")\n .option(\"--smooth\", \"Smooth fast signals with rolling 3-reading window\")\n .option(\"--use-judge\", \"Enable LLM judge to confirm/veto borderline trades (requires Anthropic API key)\")\n .option(\"--judge-model <model>\", \"Judge model ID (default: claude-haiku-4-5-20251001)\")\n .option(\"--judge-top-n <n>\", \"Max tokens to judge per cycle (default: 3)\", parseInt)\n .action(async (options: { cycle?: string; dryRun?: boolean; tokens?: string; auto?: boolean; log?: string; mode?: string; strategyClone?: string; chain?: string; assetIndex?: string; x402: boolean; weightProfile?: string; scaledSizing?: boolean; smooth?: boolean; useJudge?: boolean; judgeModel?: string; judgeTopN?: number }) => {\n let tokenList: string[];\n\n if (options.auto) {\n console.log(chalk.dim(\" Using dynamic token selection from Hyperliquid (refreshes every 30 min)\"));\n // For the loop, we'll create a placeholder list that gets replaced dynamically\n // The actual selection happens in the loop itself to allow periodic refresh\n tokenList = ['bitcoin', 'ethereum']; // Minimal list, will be replaced\n } else {\n tokenList = options.tokens ? options.tokens.split(\",\").map((t) => t.trim()) : DEFAULT_TOKENS;\n }\n\n const cycle = (options.cycle ?? \"4h\") as AgentConfig[\"cycle\"];\n\n // Load persisted risk config from disk (written by `agent config --set`)\n let savedRiskConfig: Partial<RiskConfig> = {};\n try {\n const configPath = join(homedir(), '.sherwood', 'agent', 'config.json');\n const data = await readFile(configPath, 'utf-8');\n const parsed = JSON.parse(data) as Record<string, unknown>;\n // Only pick known risk keys with valid numeric values\n for (const [k, v] of Object.entries(parsed)) {\n if (k in DEFAULT_RISK_CONFIG && typeof v === 'number' && Number.isFinite(v)) {\n (savedRiskConfig as Record<string, number>)[k] = v;\n }\n }\n if (Object.keys(savedRiskConfig).length > 0) {\n console.log(chalk.dim(` Loaded ${Object.keys(savedRiskConfig).length} risk config overrides from ~/.sherwood/agent/config.json`));\n }\n } catch {\n // No saved config — use defaults\n }\n\n const isLive = options.mode === 'hyperliquid-perp';\n // NOTE: options.scaledSizing is currently a no-op here because the loop\n // does not call ExecutionPipeline.generateProposals. If that changes,\n // pass { scaledSizing } through LoopConfig and into the proposal call.\n if (isLive && !options.strategyClone) {\n console.error(chalk.red(' --strategy-clone is required for hyperliquid-perp mode'));\n process.exitCode = 1;\n return;\n }\n const proposerKey = process.env.SHERWOOD_PROPOSER_KEY as `0x${string}` | undefined;\n if (isLive && !proposerKey) {\n console.error(chalk.red(' SHERWOOD_PROPOSER_KEY env var is required for live execution'));\n process.exitCode = 1;\n return;\n }\n\n const loopConfig: LoopConfig = {\n agent: makeConfig({\n tokens: tokenList, cycle, dryRun: !isLive, useX402: options.x402,\n weightProfile: options.weightProfile, smoothFastSignals: options.smooth,\n ...(options.useJudge ? {\n judge: {\n enabled: true,\n ...(options.judgeModel ? { model: options.judgeModel } : {}),\n ...(options.judgeTopN ? { topN: options.judgeTopN } : {}),\n },\n } : {}),\n }),\n execution: {\n dryRun: !isLive,\n mode: (options.mode ?? 'dry-run') as 'dry-run' | 'hyperliquid-perp',\n mevProtection: false,\n chain: options.chain ?? 'ethereum',\n strategyClone: options.strategyClone as `0x${string}` | undefined,\n proposerPrivateKey: proposerKey,\n assetIndex: options.assetIndex ? (() => {\n const idx = parseInt(options.assetIndex!, 10);\n if (isNaN(idx) || idx < 0) {\n console.error(chalk.red(` Invalid --asset-index: ${options.assetIndex}`));\n process.exit(1);\n }\n return idx;\n })() : undefined,\n },\n riskConfig: savedRiskConfig,\n logPath: options.log,\n autoDynamicSelection: options.auto ?? false,\n };\n\n const loop = new AgentLoop(loopConfig);\n\n // Graceful shutdown\n const shutdown = () => {\n console.log(chalk.yellow(\"\\n Shutting down gracefully...\"));\n loop.stop();\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n\n await loop.start();\n });\n\n // ── status ──\n agent\n .command(\"status\")\n .description(\"Show current portfolio, open positions, daily PnL\")\n .action(async () => {\n const portfolio = new PortfolioTracker();\n const reporter = new Reporter();\n\n try {\n const state = await portfolio.load();\n\n // Try to update prices for open positions\n if (state.positions.length > 0) {\n try {\n const cg = new CoinGeckoProvider();\n const tokenIds = state.positions.map(p => p.tokenId);\n const priceData = await cg.getPrice(tokenIds, ['usd']);\n const prices: Record<string, number> = {};\n for (const id of tokenIds) {\n const price = priceData?.[id]?.usd;\n if (typeof price === 'number') prices[id] = price;\n }\n if (Object.keys(prices).length > 0) {\n const updated = await portfolio.updatePrices(prices);\n console.log(reporter.formatPortfolioReport(updated));\n return;\n }\n } catch {\n // Fall through to show cached state\n }\n }\n\n console.log(reporter.formatPortfolioReport(state));\n } catch (err) {\n console.error(chalk.red(`Failed to load portfolio: ${(err as Error).message}`));\n process.exitCode = 1;\n }\n });\n\n // ── summary ──\n agent\n .command(\"summary\")\n .description(\"Print a formatted cycle summary for XMTP/Telegram (pipe to chat send --stdin)\")\n .action(async () => {\n const { printSummary } = await import(\"../agent/summary-formatter.js\");\n await printSummary();\n });\n\n // ── history ──\n agent\n .command(\"history\")\n .description(\"Show trade history and performance metrics\")\n .option(\"--days <n>\", \"Number of days to look back\", \"30\")\n .action(async (options: { days?: string }) => {\n const portfolio = new PortfolioTracker();\n const reporter = new Reporter();\n const days = Math.max(1, parseInt(options.days ?? \"30\", 10) || 30);\n\n try {\n const trades = await portfolio.getHistory(days);\n const metrics = await portfolio.getMetrics(days);\n\n console.log('');\n console.log(chalk.bold(` Trade History (last ${days} days)`));\n console.log(chalk.dim(' ' + '═'.repeat(70)));\n\n if (trades.length === 0) {\n console.log(chalk.dim(' No trades recorded in this period.'));\n console.log('');\n return;\n }\n\n // Table header\n console.log(chalk.dim(` ${'Symbol'.padEnd(10)} ${'Side'.padEnd(6)} ${'Entry'.padEnd(12)} ${'Exit'.padEnd(12)} ${'PnL $'.padEnd(12)} ${'PnL %'.padEnd(10)} Exit Reason`));\n console.log(chalk.dim(' ' + '─'.repeat(70)));\n\n for (const t of trades) {\n const pnlColor = t.pnlUsd >= 0 ? chalk.green : chalk.red;\n console.log(\n ` ${t.symbol.padEnd(10)} ${t.side.padEnd(6)} $${t.entryPrice.toFixed(2).padEnd(11)} $${t.exitPrice.toFixed(2).padEnd(11)} ${pnlColor(('$' + t.pnlUsd.toFixed(2)).padEnd(12))} ${pnlColor(((t.pnlPercent * 100).toFixed(1) + '%').padEnd(10))} ${t.exitReason.slice(0, 20)}`,\n );\n }\n\n // Performance metrics\n console.log(reporter.formatMetrics(metrics));\n } catch (err) {\n console.error(chalk.red(`Failed to load history: ${(err as Error).message}`));\n process.exitCode = 1;\n }\n });\n\n // ── config ──\n agent\n .command(\"config\")\n .description(\"Show or set risk parameters\")\n .option(\"--set <key=value>\", \"Set a config value (e.g. --set maxSinglePosition=0.15)\")\n .action(async (options: { set?: string }) => {\n const riskConfig = { ...DEFAULT_RISK_CONFIG };\n\n // Bounds for risk config values to prevent disabling guardrails\n const RISK_BOUNDS: Record<string, [number, number]> = {\n maxPortfolioRisk: [0.01, 0.50],\n maxSinglePosition: [0.01, 0.25],\n maxCorrelatedExposure: [0.05, 0.50],\n maxConcurrentTrades: [1, 20],\n hardStopPercent: [0.01, 0.30],\n trailingStopAtr: [0.5, 5.0],\n dailyLossLimit: [0.01, 0.30],\n weeklyLossLimit: [0.01, 0.50],\n monthlyLossLimit: [0.01, 0.60],\n riskPerTrade: [0.005, 0.05],\n };\n\n if (options.set) {\n const [key, value] = options.set.split('=');\n if (key && value && key in riskConfig) {\n if (key === 'maxSlippage') {\n console.log(chalk.red(` maxSlippage must be set per tier (not supported via --set)`));\n console.log('');\n return;\n }\n const numVal = parseFloat(value);\n if (!isNaN(numVal)) {\n // Integer-type fields must not be fractional\n const INTEGER_KEYS = new Set(['maxConcurrentTrades']);\n if (INTEGER_KEYS.has(key) && !Number.isInteger(numVal)) {\n console.log(chalk.red(` ${key} must be a whole number (got ${numVal})`));\n console.log('');\n return;\n }\n const bounds = RISK_BOUNDS[key];\n if (bounds && (numVal < bounds[0] || numVal > bounds[1])) {\n console.log(chalk.red(` Value ${numVal} out of bounds for ${key}: [${bounds[0]}, ${bounds[1]}]`));\n console.log('');\n return;\n }\n (riskConfig as Record<string, unknown>)[key] = numVal;\n console.log(chalk.green(` Set ${key} = ${numVal}`));\n\n // Persist to disk\n const configDir = join(homedir(), '.sherwood', 'agent');\n const configPath = join(configDir, 'config.json');\n let existingConfig: Record<string, unknown> = {};\n try {\n const data = await readFile(configPath, 'utf-8');\n existingConfig = JSON.parse(data);\n } catch {\n // No existing config file\n }\n existingConfig[key] = numVal;\n await mkdir(configDir, { recursive: true });\n await writeFile(configPath, JSON.stringify(existingConfig, null, 2), 'utf-8');\n console.log(chalk.dim(` Saved to ${configPath}`));\n } else {\n console.log(chalk.red(` Invalid value: ${value}`));\n }\n } else {\n console.log(chalk.red(` Unknown config key: ${key}`));\n console.log(chalk.dim(` Available keys: ${Object.keys(riskConfig).join(', ')}`));\n }\n console.log('');\n return;\n }\n\n // Display current config\n console.log('');\n console.log(chalk.bold(' Risk Configuration'));\n console.log(chalk.dim(' ' + '═'.repeat(50)));\n console.log(` Max Portfolio Risk: ${(riskConfig.maxPortfolioRisk * 100).toFixed(0)}%`);\n console.log(` Max Single Position: ${(riskConfig.maxSinglePosition * 100).toFixed(0)}%`);\n console.log(` Max Correlated Exposure: ${(riskConfig.maxCorrelatedExposure * 100).toFixed(0)}%`);\n console.log(` Max Concurrent Trades: ${riskConfig.maxConcurrentTrades}`);\n console.log(` Hard Stop: ${(riskConfig.hardStopPercent * 100).toFixed(0)}%`);\n console.log(` Trailing Stop ATR: ${riskConfig.trailingStopAtr}x`);\n console.log(chalk.dim(' ' + '─'.repeat(50)));\n console.log(` Daily Loss Limit: ${(riskConfig.dailyLossLimit * 100).toFixed(0)}%`);\n console.log(` Weekly Loss Limit: ${(riskConfig.weeklyLossLimit * 100).toFixed(0)}%`);\n console.log(` Monthly Loss Limit: ${(riskConfig.monthlyLossLimit * 100).toFixed(0)}%`);\n console.log(chalk.dim(' ' + '─'.repeat(50)));\n console.log(` Max Slippage (large): ${(riskConfig.maxSlippage.large! * 100).toFixed(1)}%`);\n console.log(` Max Slippage (mid): ${(riskConfig.maxSlippage.mid! * 100).toFixed(1)}%`);\n console.log(` Max Slippage (small): ${(riskConfig.maxSlippage.small! * 100).toFixed(1)}%`);\n console.log('');\n });\n\n // ── backtest ──\n agent\n .command(\"backtest\")\n .description(\"Backtest strategies on historical data\")\n .argument(\"<token>\", \"Token ID (e.g., bitcoin, ethereum)\")\n .option(\"--from <date>\", \"Start date (YYYY-MM-DD)\", \"2024-01-01\")\n .option(\"--to <date>\", \"End date (YYYY-MM-DD)\", \"2024-12-31\")\n .option(\"--strategies <list>\", \"Comma-separated strategy names\", \"\")\n .option(\"--capital <amount>\", \"Initial capital in USD\", \"10000\")\n .option(\"--cycle <interval>\", \"Candle interval (1h, 4h, 1d)\", \"1d\")\n .option(\"--walk-forward\", \"Enable walk-forward optimization\")\n .option(\"--train <days>\", \"Training window in days for walk-forward\", \"90\")\n .option(\"--test <days>\", \"Test window in days for walk-forward\", \"30\")\n .option(\"--verbose\", \"Show detailed decision logs for each candle\")\n .option(\"--regime\", \"Apply regime-conditional thresholds per-candle (matches live behavior)\")\n .option(\"--trailing-stop <pct>\", \"Trailing stop as decimal (e.g. 0.05 = 5%) — exits if price drops X% from high-water mark\")\n .option(\"--scaled-sizing\", \"Score-weighted position sizing (smaller positions for marginal scores)\")\n .option(\"--weight-profile <name>\", \"Named weight profile: default | majors | altcoin | sentHeavy | techHeavy\")\n .option(\"--smooth\", \"Smooth fast signals (HL flow, smartMoney, dexFlow, fundingRate) with rolling 3-reading window\")\n .action(async (token: string, options: { from: string; to: string; strategies: string; capital: string; cycle: string; walkForward?: boolean; train: string; test: string; verbose?: boolean; regime?: boolean; trailingStop?: string; scaledSizing?: boolean; weightProfile?: string; smooth?: boolean }) => {\n const capital = parseFloat(options.capital);\n const strategies = options.strategies ? options.strategies.split(\",\").map((s) => s.trim()) : [];\n const trailingStopPct = options.trailingStop ? parseFloat(options.trailingStop) : undefined;\n if (trailingStopPct !== undefined && (!Number.isFinite(trailingStopPct) || trailingStopPct <= 0 || trailingStopPct > 0.5)) {\n console.error(chalk.red(\"--trailing-stop must be between 0 and 0.5 (e.g. 0.05 = 5%)\"));\n process.exitCode = 1;\n return;\n }\n // NOTE: options.scaledSizing is currently a no-op in backtest because\n // the backtester doesn't generate TradeProposals — it simulates its own\n // BUY/SELL directly from decision.action. Flag is parsed but unused.\n\n if (options.walkForward) {\n // Walk-forward optimization mode\n const trainWindow = parseInt(options.train, 10);\n const testWindow = parseInt(options.test, 10);\n const stepSize = Math.min(testWindow, 30); // Default step size to test window or 30 days, whichever is smaller\n const totalDays = trainWindow + (testWindow * 3); // Ensure we have enough data for multiple folds\n\n const walkConfig: WalkForwardConfig = {\n tokenId: token,\n totalDays,\n trainWindow,\n testWindow,\n stepSize,\n capital,\n strategies,\n useRegime: options.regime,\n trailingStopPct,\n smoothFastSignals: options.smooth,\n };\n\n const spinner = ora(`Walk-forward testing ${token} (${trainWindow}d train, ${testWindow}d test)...`).start();\n\n try {\n const backtester = new Backtester({\n tokenId: token,\n startDate: options.from,\n endDate: options.to,\n initialCapital: capital,\n strategies,\n cycle: (options.cycle as BacktestConfig[\"cycle\"]) || \"1d\",\n verbose: options.verbose,\n useRegime: options.regime,\n trailingStopPct,\n smoothFastSignals: options.smooth,\n });\n const result = await backtester.walkForwardTest(walkConfig);\n spinner.stop();\n console.log(backtester.formatWalkForwardResults(result, walkConfig));\n } catch (err) {\n spinner.fail(`Walk-forward test failed: ${(err as Error).message}`);\n process.exitCode = 1;\n }\n } else {\n // Regular backtest mode\n const config: BacktestConfig = {\n tokenId: token,\n startDate: options.from,\n endDate: options.to,\n initialCapital: capital,\n strategies,\n cycle: (options.cycle as BacktestConfig[\"cycle\"]) || \"1d\",\n verbose: options.verbose,\n useRegime: options.regime,\n trailingStopPct,\n smoothFastSignals: options.smooth,\n };\n\n const spinner = ora(`Backtesting ${token} from ${config.startDate} to ${config.endDate}...`).start();\n\n try {\n const backtester = new Backtester(config);\n const result = await backtester.run();\n spinner.stop();\n console.log(backtester.formatResults(result));\n } catch (err) {\n spinner.fail(`Backtest failed: ${(err as Error).message}`);\n process.exitCode = 1;\n }\n }\n });\n\n // ── calibrate ──\n agent\n .command(\"calibrate\")\n .description(\"Sweep weight profiles × BUY/SELL thresholds against historical data; ranks by Sharpe\")\n .option(\"--tokens <list>\", \"Comma-separated CoinGecko IDs (default: bitcoin,ethereum,solana,aave,uniswap,chainlink,arbitrum)\")\n .option(\"--days <n>\", \"Lookback window in days\", \"60\")\n .option(\"--capital <amount>\", \"Initial capital per token in USD\", \"10000\")\n .option(\"--top <n>\", \"Show top N configs in the ranked table\", \"20\")\n .option(\"--from-history [path]\", \"Replay against signal-history.jsonl (true production signal stack including HL/funding/dexFlow). Path defaults to ~/.sherwood/agent/signal-history.jsonl\")\n .option(\"--last <days>\", \"Replay only rows captured within the last N days (use after a scoring change to ignore stale captures). Replay path only.\")\n .option(\"--no-regime\", \"When using --from-history, ignore the regime field on each row (use DEFAULT_THRESHOLDS instead)\")\n .action(async (options: { tokens?: string; days: string; capital: string; top: string; fromHistory?: string | boolean; last?: string; regime: boolean }) => {\n const topN = parseInt(options.top, 10);\n\n // ── Replay path: signal-history.jsonl ──\n if (options.fromHistory) {\n const historyPath = typeof options.fromHistory === 'string' ? options.fromHistory : undefined;\n const tokens = options.tokens\n ? options.tokens.split(\",\").map((t) => t.trim()).filter(Boolean)\n : undefined; // default = all tokens in dataset\n\n let lastDays: number | undefined;\n if (options.last !== undefined) {\n lastDays = parseFloat(options.last);\n if (!Number.isFinite(lastDays) || lastDays <= 0) {\n console.error(chalk.red(`Invalid --last: ${options.last} (expected positive number of days)`));\n process.exitCode = 1;\n return;\n }\n }\n\n console.log(chalk.bold(`\\n Replaying calibration against signal-history.jsonl${lastDays ? ` (last ${lastDays}d)` : ''}...\\n`));\n try {\n const results = await runHistoryReplay({\n historyPath,\n tokens,\n lastDays,\n useRegime: options.regime,\n onProgress: (msg) => console.log(chalk.dim(` ${msg}`)),\n });\n console.log(formatCalibrationTable(results, topN));\n console.log(chalk.dim(` Source: signal-history replay (production signal stack)`));\n console.log(chalk.dim(` Saved: ~/.sherwood/agent/replay-calibration-results.json\\n`));\n } catch (err) {\n console.error(chalk.red(`\\n Replay calibration failed: ${(err as Error).message}\\n`));\n process.exitCode = 1;\n }\n return;\n }\n\n // ── Candle path: re-fetch from CoinGecko, recompute signals ──\n const tokens = options.tokens\n ? options.tokens.split(\",\").map((t) => t.trim()).filter(Boolean)\n : DEFAULT_CALIBRATION_TOKENS;\n const days = parseInt(options.days, 10);\n const capital = parseFloat(options.capital);\n\n if (!Number.isFinite(days) || days <= 0) {\n console.error(chalk.red(`Invalid --days: ${options.days}`));\n process.exitCode = 1;\n return;\n }\n if (!Number.isFinite(capital) || capital <= 0) {\n console.error(chalk.red(`Invalid --capital: ${options.capital}`));\n process.exitCode = 1;\n return;\n }\n\n console.log(chalk.bold(`\\n Calibrating ${tokens.length} tokens over ${days}d (candle-based, 200 configs per token)...\\n`));\n try {\n const results = await runCalibration({\n tokens,\n days,\n capital,\n onProgress: (msg) => console.log(chalk.dim(` ${msg}`)),\n });\n console.log(formatCalibrationTable(results, topN));\n } catch (err) {\n console.error(chalk.red(`\\n Calibration failed: ${(err as Error).message}\\n`));\n process.exitCode = 1;\n }\n });\n\n // ── autoresearch ──\n agent\n .command(\"autoresearch\")\n .description(\"Run autonomous parameter optimization against production signal history (Karpathy/Nunchi pattern)\")\n .option(\"--experiments <n>\", \"Number of experiments to run\", \"50\")\n .option(\"--last <days>\", \"Only use signal rows from the last N days\")\n .action(async (options: { experiments?: string; last?: string }) => {\n const { runAutoresearch } = await import(\"../agent/autoresearch.js\");\n await runAutoresearch({\n experiments: options.experiments ? parseInt(options.experiments, 10) : 50,\n lastDays: options.last ? parseFloat(options.last) : undefined,\n });\n });\n\n // ── signal-audit ──\n agent\n .command(\"signal-audit\")\n .description(\"Audit signal history — which signal categories actually fire?\")\n .option(\"--drop-threshold <rate>\", \"Fire-rate below which a category is flagged for dropping\", \"0.1\")\n .option(\"--json\", \"Emit raw JSON instead of formatted table\")\n .option(\"--save <path>\", \"Save current audit as a baseline JSON snapshot\")\n .option(\"--baseline <path>\", \"Diff current audit against a saved baseline\")\n .action(async (options: { dropThreshold?: string; json?: boolean; save?: string; baseline?: string }) => {\n const dropThreshold = parseFloat(options.dropThreshold ?? \"0.1\");\n const result = await auditSignalHistory();\n\n // --save: write snapshot and exit\n if (options.save) {\n await mkdir(join(homedir(), '.sherwood', 'agent'), { recursive: true });\n await writeFile(options.save, JSON.stringify(result, null, 2), 'utf-8');\n console.log(chalk.green(` Saved baseline snapshot to ${options.save}`));\n console.log(chalk.dim(` ${result.totalEntries} entries, ${result.perSignal.length} signals`));\n return;\n }\n\n // --baseline: load and diff\n if (options.baseline) {\n let baseline: AuditResult;\n try {\n const raw = await readFile(options.baseline, 'utf-8');\n baseline = JSON.parse(raw) as AuditResult;\n } catch (err) {\n console.error(chalk.red(` Failed to load baseline: ${(err as Error).message}`));\n process.exitCode = 1;\n return;\n }\n\n const diff = diffAudits(baseline, result);\n\n if (options.json) {\n console.log(JSON.stringify(diff, null, 2));\n return;\n }\n\n console.log(\"\");\n console.log(chalk.bold(\" Signal Audit Diff\"));\n console.log(chalk.dim(\" \" + \"═\".repeat(72)));\n if (baseline.dateRange && result.dateRange) {\n console.log(chalk.dim(` Baseline: ${baseline.dateRange.from.slice(0, 10)} → ${baseline.dateRange.to.slice(0, 10)} (${baseline.totalEntries} entries)`));\n console.log(chalk.dim(` Current: ${result.dateRange.from.slice(0, 10)} → ${result.dateRange.to.slice(0, 10)} (${result.totalEntries} entries)`));\n }\n console.log(\"\");\n\n // Per-category diff\n console.log(chalk.bold(\" Per Category\"));\n console.log(chalk.dim(` ${\"Category\".padEnd(14)} ${\"Was\".padEnd(7)} ${\"Now\".padEnd(7)} ${\"Δ\".padEnd(8)} Status`));\n console.log(chalk.dim(\" \" + \"─\".repeat(72)));\n for (const c of diff.perCategory) {\n const arrow = c.status === \"improved\" ? chalk.green(\"↑\") :\n c.status === \"regressed\" ? chalk.red(\"↓\") :\n c.status === \"new\" ? chalk.cyan(\"✦\") :\n c.status === \"removed\" ? chalk.red(\"✗\") : chalk.dim(\"=\");\n const deltaStr = (c.fireRateDelta >= 0 ? \"+\" : \"\") + (c.fireRateDelta * 100).toFixed(0) + \"pp\";\n const deltaColor = c.status === \"improved\" ? chalk.green : c.status === \"regressed\" ? chalk.red : chalk.dim;\n console.log(\n ` ${c.category.padEnd(14)} ${(c.baseline.fireRate * 100).toFixed(0).padStart(5)}% ${(c.current.fireRate * 100).toFixed(0).padStart(5)}% ${deltaColor(deltaStr.padEnd(7))} ${arrow} ${c.status}`,\n );\n }\n\n // Per-signal diff (only show changes)\n const changedSignals = diff.perSignal.filter((s) => s.status !== \"stable\");\n if (changedSignals.length > 0) {\n console.log(\"\");\n console.log(chalk.bold(\" Changed Signals\"));\n console.log(chalk.dim(` ${\"Signal\".padEnd(22)} ${\"Was\".padEnd(7)} ${\"Now\".padEnd(7)} ${\"Δ\".padEnd(8)} Status`));\n console.log(chalk.dim(\" \" + \"─\".repeat(72)));\n for (const s of changedSignals) {\n const arrow = s.status === \"improved\" ? chalk.green(\"↑\") :\n s.status === \"regressed\" ? chalk.red(\"↓\") :\n s.status === \"new\" ? chalk.cyan(\"✦\") :\n s.status === \"removed\" ? chalk.red(\"✗\") : chalk.dim(\"=\");\n const deltaStr = (s.fireRateDelta >= 0 ? \"+\" : \"\") + (s.fireRateDelta * 100).toFixed(0) + \"pp\";\n const deltaColor = s.status === \"improved\" ? chalk.green : s.status === \"regressed\" ? chalk.red : chalk.dim;\n console.log(\n ` ${s.name.padEnd(22)} ${(s.baseline.fireRate * 100).toFixed(0).padStart(5)}% ${(s.current.fireRate * 100).toFixed(0).padStart(5)}% ${deltaColor(deltaStr.padEnd(7))} ${arrow} ${s.status}`,\n );\n }\n } else {\n console.log(\"\");\n console.log(chalk.dim(\" No per-signal changes above 5pp threshold.\"));\n }\n console.log(\"\");\n return;\n }\n\n if (options.json) {\n console.log(JSON.stringify(result, null, 2));\n return;\n }\n\n console.log(\"\");\n console.log(chalk.bold(\" Signal Activity Audit\"));\n console.log(chalk.dim(\" \" + \"═\".repeat(72)));\n\n if (result.totalEntries === 0) {\n console.log(chalk.dim(` No signal history yet at ${result.filePath}`));\n console.log(chalk.dim(\" Run 'sherwood agent analyze' a few times to populate it.\"));\n console.log(\"\");\n return;\n }\n\n console.log(chalk.dim(` Source: ${result.filePath}`));\n console.log(chalk.dim(` Entries: ${result.totalEntries}`));\n if (result.dateRange) {\n console.log(\n chalk.dim(\n ` Range: ${result.dateRange.from.slice(0, 10)} → ${result.dateRange.to.slice(0, 10)}`,\n ),\n );\n }\n console.log(\"\");\n\n // Per-category summary\n console.log(chalk.bold(\" Per Category\"));\n console.log(\n chalk.dim(\n ` ${\"Category\".padEnd(14)} ${\"Fire%\".padEnd(7)} ${\"Obs\".padEnd(6)} ${\"Mean|v|\".padEnd(9)} Signals`,\n ),\n );\n console.log(chalk.dim(\" \" + \"─\".repeat(72)));\n for (const c of result.perCategory) {\n const color =\n c.recommendation === \"keep\"\n ? chalk.green\n : c.recommendation === \"drop\"\n ? chalk.red\n : chalk.yellow;\n const rec = `[${c.recommendation.toUpperCase()}]`;\n console.log(\n ` ${c.category.padEnd(14)} ${(c.fireRate * 100).toFixed(0).padStart(5)}% ${String(c.observations).padEnd(6)} ${c.meanAbsValue.toFixed(3).padEnd(9)} ${c.signalNames.join(\", \")} ${color(rec)}`,\n );\n }\n\n // Per-signal detail\n console.log(\"\");\n console.log(chalk.bold(\" Per Signal\"));\n console.log(\n chalk.dim(\n ` ${\"Signal\".padEnd(22)} ${\"Category\".padEnd(12)} ${\"Fire%\".padEnd(7)} ${\"Obs\".padEnd(6)} ${\"Mean|v|\".padEnd(9)} ${\"Bias\".padEnd(8)} Conf`,\n ),\n );\n console.log(chalk.dim(\" \" + \"─\".repeat(72)));\n for (const s of result.perSignal) {\n const biasColor = s.directionalBias >= 0 ? chalk.green : chalk.red;\n console.log(\n ` ${s.name.padEnd(22)} ${s.category.padEnd(12)} ${(s.fireRate * 100).toFixed(0).padStart(5)}% ${String(s.observations).padEnd(6)} ${s.meanAbsValue.toFixed(3).padEnd(9)} ${biasColor((s.directionalBias >= 0 ? \"+\" : \"\") + s.directionalBias.toFixed(3)).padEnd(17)} ${s.meanConfidence.toFixed(2)}`,\n );\n }\n\n // Renormalized weight suggestion\n const current = DEFAULT_WEIGHTS as unknown as Record<string, number>;\n const suggested = suggestRenormalizedWeights(current, result.perCategory, dropThreshold);\n const changed = Object.keys(current).some(\n (k) => Math.abs((suggested[k] ?? 0) - (current[k] ?? 0)) > 0.001,\n );\n\n if (changed) {\n console.log(\"\");\n console.log(chalk.bold(\" Suggested Weight Renormalization\"));\n console.log(chalk.dim(` (Dropping categories below ${(dropThreshold * 100).toFixed(0)}% fire rate)`));\n console.log(chalk.dim(\" \" + \"─\".repeat(72)));\n console.log(\n chalk.dim(\n ` ${\"Category\".padEnd(14)} ${\"Current\".padEnd(10)} ${\"Suggested\"}`,\n ),\n );\n for (const [cat, w] of Object.entries(current)) {\n const newW = suggested[cat] ?? 0;\n const tag =\n newW === 0 ? chalk.red(\" ← drop\") : newW > w ? chalk.green(\" ← boost\") : \"\";\n console.log(\n ` ${cat.padEnd(14)} ${w.toFixed(3).padEnd(10)} ${newW.toFixed(3)}${tag}`,\n );\n }\n } else {\n console.log(\"\");\n console.log(chalk.dim(\" All categories above drop threshold — no renormalization suggested.\"));\n }\n console.log(\"\");\n });\n\n // ── serve (dashboard API) ──\n agent\n .command(\"serve\")\n .description(\"Start dashboard API server (REST + WebSocket)\")\n .option(\"--port <number>\", \"Port to listen on\", \"3939\")\n .option(\"--host <string>\", \"Host to bind to (default: 127.0.0.1 if no token, 0.0.0.0 with token)\")\n .option(\"--token <string>\", \"Bearer token for remote access\")\n .action(async (opts: { port: string; host?: string; token?: string }) => {\n const { startServe } = await import(\"../agent/serve.js\");\n const port = parseInt(opts.port, 10);\n const host = opts.host ?? (opts.token ? \"0.0.0.0\" : \"127.0.0.1\");\n await startServe({ port, host, token: opts.token });\n });\n\n // ── alerts ──\n const alerts = agent.command(\"alerts\").description(\"Manage trading alerts\");\n\n alerts\n .command(\"list\")\n .alias(\"\")\n .description(\"Show recent alerts\")\n .option(\"--hours <n>\", \"Look back N hours\", \"24\")\n .action(async (options: { hours?: string }) => {\n try {\n const tradingAgent = new TradingAgent(makeConfig());\n const hours = parseInt(options.hours ?? \"24\", 10) || 24;\n const maxAge = hours * 60 * 60 * 1000;\n\n const recentAlerts = await tradingAgent.getRecentAlerts(maxAge);\n\n if (recentAlerts.length === 0) {\n console.log(chalk.dim(\" No alerts in the last \" + hours + \" hours.\"));\n return;\n }\n\n console.log('');\n console.log(chalk.bold(` Recent Alerts (last ${hours} hours)`));\n console.log(chalk.dim(' ' + '═'.repeat(60)));\n\n for (const alert of recentAlerts.slice(0, 20)) {\n const icon = alert.priority === \"critical\" ? \"🔴\" :\n alert.priority === \"high\" ? \"🟡\" :\n alert.priority === \"medium\" ? \"🔵\" : \"⚫\";\n\n const timeStr = new Date(alert.timestamp).toLocaleString();\n const priorityColor = alert.priority === \"critical\" ? chalk.red :\n alert.priority === \"high\" ? chalk.yellow :\n alert.priority === \"medium\" ? chalk.blue : chalk.gray;\n\n console.log(` ${icon} ${priorityColor(alert.priority.toUpperCase())} — ${alert.title}`);\n console.log(chalk.dim(` ${alert.details.slice(0, 80)}${alert.details.length > 80 ? '...' : ''}`));\n console.log(chalk.dim(` Token: ${alert.tokenId}, ${timeStr}`));\n console.log('');\n }\n } catch (err) {\n console.error(chalk.red(`Failed to load alerts: ${(err as Error).message}`));\n process.exitCode = 1;\n }\n });\n\n alerts\n .command(\"clear\")\n .description(\"Clear all alerts\")\n .action(async () => {\n try {\n const tradingAgent = new TradingAgent(makeConfig());\n await tradingAgent.clearAlerts();\n console.log(chalk.green(\" All alerts cleared.\"));\n } catch (err) {\n console.error(chalk.red(`Failed to clear alerts: ${(err as Error).message}`));\n process.exitCode = 1;\n }\n });\n}\n\n// ── Display Helpers ──\n\nfunction renderBar(value: number): string {\n const width = 20;\n const mid = Math.floor(width / 2);\n const bar = new Array(width).fill(\"░\");\n\n const magnitude = Math.min(Math.abs(value), 1.0);\n const filled = Math.round(magnitude * mid);\n\n if (value > 0) {\n for (let i = mid; i < mid + filled; i++) bar[i] = \"█\";\n return chalk.dim(bar.slice(0, mid).join(\"\")) + chalk.green(bar.slice(mid).join(\"\"));\n } else if (value < 0) {\n for (let i = mid - filled; i < mid; i++) bar[i] = \"█\";\n return chalk.red(bar.slice(0, mid).join(\"\")) + chalk.dim(bar.slice(mid).join(\"\"));\n }\n return chalk.dim(bar.join(\"\"));\n}\n\nfunction colorValue(value: number): string {\n const str = (value >= 0 ? \"+\" : \"\") + value.toFixed(3);\n if (value > 0.1) return chalk.green(str);\n if (value < -0.1) return chalk.red(str);\n return chalk.yellow(str);\n}\n\nfunction formatValue(value: number, low: number, high: number, invertColors: boolean): string {\n const str = isNaN(value) ? \"N/A\" : value.toFixed(2);\n if (isNaN(value)) return chalk.dim(str);\n if (invertColors) {\n if (value < low) return chalk.green(str);\n if (value > high) return chalk.red(str);\n } else {\n if (value > high) return chalk.green(str);\n if (value < low) return chalk.red(str);\n }\n return chalk.yellow(str);\n}\n\nfunction formatFearGreed(value: number): string {\n const str = `${value}`;\n if (value < 25) return chalk.green(str + \" (Extreme Fear)\");\n if (value < 40) return chalk.green(str + \" (Fear)\");\n if (value < 60) return chalk.yellow(str + \" (Neutral)\");\n if (value < 75) return chalk.red(str + \" (Greed)\");\n return chalk.red(str + \" (Extreme Greed)\");\n}\n","/**\n * Main trading agent orchestrator.\n * Gathers data from multiple providers, scores signals, produces trade decisions.\n */\n\nimport chalk from \"chalk\";\nimport { DefiLlamaProvider } from \"../providers/data/defillama.js\";\nimport { CoinGeckoProvider } from \"../providers/data/coingecko.js\";\nimport { SentimentProvider } from \"../providers/data/sentiment.js\";\nimport { getResearchProvider } from \"../providers/research/index.js\";\nimport type { Candle, TechnicalSignals } from \"./technical.js\";\nimport { getLatestSignals } from \"./technical.js\";\nimport {\n scoreTechnical,\n scoreSentiment,\n scoreFundamental,\n scoreEvent,\n computeTradeDecision,\n DEFAULT_WEIGHTS,\n profileForToken,\n} from \"./scoring.js\";\nimport type { Signal, ScoringWeights, TradeDecision } from \"./scoring.js\";\nimport { runStrategies } from \"./strategies/index.js\";\nimport { DexScreenerProvider } from \"../providers/data/dexscreener.js\";\nimport { FundingRateProvider } from \"../providers/data/funding-rate.js\";\nimport { TokenUnlocksProvider } from \"../providers/data/token-unlocks.js\";\nimport { TwitterSentimentProvider } from \"../providers/data/twitter.js\";\nimport {\n getMessariFundamentals,\n getBtcNetworkStats,\n getCryptoPredictions,\n getSocialData,\n getCryptoCompareCandles,\n getProtocolTvl,\n getKronosVolForecast,\n} from '../providers/fincept/index.js';\nimport { logSignal } from \"./signal-logger.js\";\nimport type { JudgeLogData } from \"./signal-logger.js\";\nimport { judge, selectJudgeCandidates, DEFAULT_JUDGE_CONFIG } from \"./judge.js\";\nimport type { JudgeConfig, JudgeVerdict, JudgeContext } from \"./judge.js\";\nimport { SignalSmoother, FileSmootherStorage, DEFAULT_SMOOTHER_CONFIG } from \"./signal-smoother.js\";\nimport { applyVelocityGate, applyRegimeGate, applyRealAlphaGate, resolveVelocity, DEFAULT_ENTRY_GATE_CONFIG } from \"./entry-gates.js\";\nimport type { EntryGateConfig } from \"./entry-gates.js\";\nimport { join as joinPath } from \"node:path\";\nimport { homedir as getHomedir } from \"node:os\";\nimport { HyperliquidProvider } from \"../providers/data/hyperliquid.js\";\nimport type { StrategyContext, StrategyConfig } from \"./strategies/index.js\";\nimport { MarketRegimeDetector } from \"./regime.js\";\nimport type { RegimeAnalysis } from \"./regime.js\";\nimport { CorrelationGuard } from \"./correlation.js\";\nimport type { CorrelationCheck } from \"./correlation.js\";\nimport { AlertSystem } from \"./alerts.js\";\nimport type { Alert } from \"./alerts.js\";\nimport type { PortfolioState } from \"./risk.js\";\nimport { isX402WalletFunded } from \"../lib/x402.js\";\n\nexport type { Signal, ScoringWeights, TradeDecision, Alert, TechnicalSignals };\n\nexport interface AgentConfig {\n tokens: string[];\n cycle: \"15m\" | \"1h\" | \"4h\";\n dryRun: boolean;\n maxPositionPct: number;\n maxRiskPct: number;\n weights?: ScoringWeights;\n /** Named weight profile (default | majors | altcoin | sentHeavy | techHeavy).\n * When set, overrides `weights` and is applied per-token via profileForToken().\n * When unset, BTC/ETH/SOL auto-get the \"majors\" profile, others use default. */\n weightProfile?: string;\n /** When true, includes paid x402 data (Nansen smart-money, Messari fundamentals) in analysis. */\n useX402?: boolean;\n /** Only run x402 paid signals on the top N tokens by free-signal score.\n * Remaining tokens use free signals only. Reduces x402 cost from ~$1.60/run\n * (10 tokens × $0.16) to ~$0.48/run (3 tokens × $0.16).\n * Set to 0 or undefined to run x402 on all tokens (old behavior). */\n x402TopN?: number;\n /** When true, smooth fast/noisy signals (HL flow, smartMoney, dexFlow, fundingRate)\n * with a rolling 3-reading average before scoring. Reduces single-scan flicker.\n * Default false. */\n smoothFastSignals?: boolean;\n /** Per-strategy configuration overrides. */\n strategyConfigs?: Record<string, StrategyConfig>;\n /** LLM judge configuration — confirms or vetoes borderline trade decisions. */\n judge?: Partial<JudgeConfig>;\n /** Entry-gate configuration — velocity/freshness checks applied post-scoring. */\n entryGates?: Partial<EntryGateConfig>;\n /** Backwards-compat opt-out: skip the velocity gate entirely. Equivalent to\n * `entryGates: { velocityGateEnabled: false }`. Default false (gate active). */\n disableVelocityGate?: boolean;\n}\n\nexport interface TokenAnalysis {\n token: string;\n decision: TradeDecision;\n data: {\n technicalSignals?: TechnicalSignals;\n fearAndGreed?: number;\n tvl?: number;\n price?: number;\n };\n regime?: RegimeAnalysis;\n correlation?: CorrelationCheck;\n /** LLM judge verdict (present only when judge is enabled and token was judged). */\n judgeResult?: { verdict: JudgeVerdict; cached: boolean; latencyMs: number };\n /** Original action/score before judge veto (present only when judge vetoed). */\n preJudge?: { action: string; score: number };\n /** Original action/score before velocity gate downgraded to HOLD. */\n preVelocity?: { action: string; score: number };\n /** Original action/score before regime gate blocked a short in non-bearish regime. */\n preRegime?: { action: string; score: number };\n /** Original action/score before real-alpha gate blocked a noisy-only entry. */\n preAlpha?: { action: string; score: number };\n /** Kronos ML-predicted per-candle volatility (fraction). Used by executor\n * for dynamic stop-loss width when available. */\n kronosVol4h?: number;\n}\n\nexport class TradingAgent {\n private config: AgentConfig;\n private defillama: DefiLlamaProvider;\n private coingecko: CoinGeckoProvider;\n private sentiment: SentimentProvider;\n private dexscreener: DexScreenerProvider;\n private fundingRate: FundingRateProvider;\n private tokenUnlocks: TokenUnlocksProvider;\n private twitter: TwitterSentimentProvider;\n private hyperliquid: HyperliquidProvider;\n private regimeDetector: MarketRegimeDetector;\n private correlationGuard: CorrelationGuard;\n private alertSystem: AlertSystem;\n private smoother: SignalSmoother | null = null;\n /** Optional getter for current portfolio state. When set, judge receives\n * real portfolio context (openCount, cashPct, etc.); otherwise judge falls\n * back to neutral defaults and its portfolio-veto branch is effectively\n * disabled. Wired in by AgentLoop via setPortfolioStateGetter(). */\n private portfolioStateGetter: (() => PortfolioState | undefined) | undefined;\n /** Warn-once flag when portfolio getter is missing at judge time. */\n private warnedNoPortfolioGetter = false;\n\n constructor(config: AgentConfig) {\n this.config = config;\n this.defillama = new DefiLlamaProvider();\n this.coingecko = new CoinGeckoProvider();\n this.sentiment = new SentimentProvider();\n this.dexscreener = new DexScreenerProvider();\n this.fundingRate = new FundingRateProvider();\n this.tokenUnlocks = new TokenUnlocksProvider();\n this.twitter = new TwitterSentimentProvider();\n this.hyperliquid = new HyperliquidProvider();\n this.regimeDetector = new MarketRegimeDetector();\n this.correlationGuard = new CorrelationGuard();\n this.alertSystem = new AlertSystem();\n }\n\n /** Analyze a single token — gather all data and score. */\n async analyzeToken(tokenId: string, opts?: { skipX402?: boolean; groupReturns?: Record<string, number> }): Promise<TokenAnalysis> {\n const signals: Signal[] = [];\n let technicalSignals: TechnicalSignals | undefined;\n let fearAndGreedValue: number | undefined;\n let tvl: number | undefined;\n let candles: Candle[] | undefined;\n let sentimentZScore: number | undefined;\n\n // Phase 1: Parallel basic data fetching\n // PRIMARY candle source is Hyperliquid (free, no rate limits, includes volume).\n // CoinGecko OHLC is FALLBACK only — the free tier 429s after ~5 tokens and the\n // circuit breaker kills the remaining 9+, leaving them with zero technical data.\n const [hlCandleResult, ohlcResult, fearGreedResult, tvlResult, hyperliquidResult] = await Promise.allSettled([\n // 1a. PRIMARY: Hyperliquid candles (4h over 30 days ≈ 180 bars — more than enough for EMA/RSI/MACD)\n this.hyperliquid.getCandles(tokenId, '4h', 30 * 24 * 60 * 60 * 1000),\n\n // 1b. FALLBACK: CryptoCompare candles via Fincept (replaces CoinGecko OHLC)\n getCryptoCompareCandles(tokenId, 180),\n\n // 2. Fetch Fear & Greed\n this.sentiment.getFearAndGreed(),\n\n // 3. Fetch TVL data (if DeFi protocol) — Fincept DefiLlama wrapper\n getProtocolTvl(tokenId).then((tvlValue) => {\n if (typeof tvlValue === \"number\" && tvlValue > 0) {\n return { tvl: tvlValue, mcapToTvl: undefined }; // mcapToTvl computed in Phase 2b via Messari\n }\n return null;\n }),\n\n // 4. Fetch Hyperliquid data (free, exchange-native) — move to Phase 1 for price substitution\n this.hyperliquid.getHyperliquidData(tokenId)\n ]);\n\n // Process candle results — prefer Hyperliquid (free, unlimited) over CoinGecko (rate-limited)\n const hlCandles = hlCandleResult.status === 'fulfilled' ? hlCandleResult.value : null;\n const cgCandles = ohlcResult.status === 'fulfilled' ? ohlcResult.value : null;\n const resolvedCandles = (hlCandles && hlCandles.length > 10) ? hlCandles : cgCandles;\n\n if (resolvedCandles && resolvedCandles.length > 10) {\n candles = resolvedCandles;\n technicalSignals = getLatestSignals(candles);\n signals.push(scoreTechnical(technicalSignals));\n\n // Price momentum signal — captures \"the market is moving\" before\n // lagging indicators (RSI, MACD, EMA) catch up. Uses the same candle\n // data we already have. Scored as a technical-category signal so it\n // adds to (not replaces) the existing technical analysis.\n if (candles.length >= 24) {\n const recent = candles.slice(-24);\n const currentClose = recent[recent.length - 1]!.close;\n const recentLow = Math.min(...recent.map((c) => c.low));\n const recentHigh = Math.max(...recent.map((c) => c.high));\n const pctFromLow = recentLow > 0 ? (currentClose - recentLow) / recentLow : 0;\n const pctFromHigh = recentHigh > 0 ? (recentHigh - currentClose) / recentHigh : 0;\n\n let momentumValue = 0;\n let momentumDetails = '';\n // Bullish momentum: price near 24-candle highs\n if (pctFromHigh < 0.01) {\n momentumValue = Math.min(0.6, pctFromLow * 5); // scales: 3% move → 0.15, 5% → 0.25, 10% → 0.50\n momentumDetails = `Price +${(pctFromLow * 100).toFixed(1)}% from 24-candle low, near highs`;\n }\n // Bearish momentum: price near 24-candle lows\n else if (pctFromLow < 0.01) {\n momentumValue = -Math.min(0.6, pctFromHigh * 5);\n momentumDetails = `Price -${(pctFromHigh * 100).toFixed(1)}% from 24-candle high, near lows`;\n }\n // Mid-range: proportional to position within range\n else if (recentHigh > recentLow) {\n const rangePosition = (currentClose - recentLow) / (recentHigh - recentLow); // 0=low, 1=high\n momentumValue = (rangePosition - 0.5) * 0.4; // -0.2 to +0.2\n momentumDetails = `Mid-range (${(rangePosition * 100).toFixed(0)}th percentile of 24-candle range)`;\n }\n\n if (Math.abs(momentumValue) > 0.02) {\n signals.push({\n name: 'momentum',\n value: momentumValue,\n confidence: Math.min(0.7, 0.3 + Math.abs(momentumValue)),\n source: 'Price Momentum',\n details: momentumDetails,\n });\n }\n }\n } else if (ohlcResult.status === 'rejected') {\n console.error(chalk.dim(` Technical analysis failed for ${tokenId}: ${ohlcResult.reason}`));\n }\n\n // Process Fear & Greed results\n if (fearGreedResult.status === 'fulfilled' && fearGreedResult.value?.length > 0) {\n const fgData = fearGreedResult.value;\n fearAndGreedValue = fgData[0]!.value;\n const values = fgData.map((d) => d.value);\n sentimentZScore = this.sentiment.computeSentimentZScore(values);\n // F&G is used as a regime-gate (extreme fear = allow BUY, extreme greed = allow SELL)\n // NOT as a scoring signal. It fires at +0.76 on 100% of observations when F&G < 25,\n // making it a constant bias rather than a directional signal. The sentimentContrarian\n // strategy (which modulates based on actual F&G value) remains active.\n // signals.push(scoreSentiment(fearAndGreedValue, sentimentZScore)); // REMOVED: constant bias\n } else if (fearGreedResult.status === 'rejected') {\n console.error(chalk.dim(` Sentiment data failed: ${fearGreedResult.reason}`));\n }\n\n // Process Hyperliquid results first to use for price substitution\n let hyperliquidData: any = undefined;\n if (hyperliquidResult.status === 'fulfilled' && hyperliquidResult.value) {\n hyperliquidData = hyperliquidResult.value;\n }\n\n // Process TVL results — mcapToTvl is now computed in Phase 2b via Messari\n if (tvlResult.status === 'fulfilled' && tvlResult.value) {\n tvl = tvlResult.value.tvl;\n }\n // scoreFundamental is called in Phase 2b after Messari data is available\n\n // Shared research data — captured in phase 2, reused in phase 3 strategies\n let nansenData: any = undefined;\n let messariData: any = undefined;\n // Nansen flow data for NarrativeVacuum + WhaleIntent strategies\n let nansenFlowData: StrategyContext['nansenFlowData'] = undefined;\n let nansenHlPerps: StrategyContext['nansenHlPerps'] = undefined;\n\n // Check x402 wallet USDC balance once per scan cycle.\n // If wallet is unfunded, skip x402 calls entirely to avoid diluting scores.\n // undefined = x402 not configured (don't exclude categories)\n // true = x402 configured + wallet funded\n // false = x402 configured but wallet empty → exclude dead categories\n const x402Enabled = this.config.useX402 && !opts?.skipX402;\n let x402Available: boolean | undefined = x402Enabled ? false : undefined;\n if (x402Enabled) {\n x402Available = await isX402WalletFunded();\n if (!x402Available) {\n console.error(chalk.yellow(` x402 wallet has insufficient USDC — skipping paid signals (smartMoney, event) for this cycle`));\n }\n }\n\n // Phase 2: Nansen smart-money data\n //\n // Fires when NANSEN_API_KEY is set (direct Pro API, ~$0.005/call) OR\n // when x402 is enabled + funded (micropayment, ~$0.06/call).\n // Two calls in parallel:\n // 1. Smart-money netflows → feeds NarrativeVacuum + WhaleIntent strategies\n // 2. HL perp trades → cross-venue confirmation for WhaleIntent\n const nansenAvailable = !!process.env.NANSEN_API_KEY || (x402Enabled && x402Available);\n if (nansenAvailable) {\n // Map tokenId to HL symbol for perp-trades query\n const hlSymbolMap: Record<string, string> = {\n bitcoin: \"BTC\", ethereum: \"ETH\", solana: \"SOL\",\n arbitrum: \"ARB\", aave: \"AAVE\", uniswap: \"UNI\",\n dogecoin: \"DOGE\", ripple: \"XRP\", hyperliquid: \"HYPE\",\n \"worldcoin-wld\": \"WLD\", bittensor: \"TAO\", zcash: \"ZEC\",\n fartcoin: \"FARTCOIN\", pepe: \"PEPE\", polkadot: \"DOT\",\n };\n const hlSymbol = hlSymbolMap[tokenId] ?? tokenId.toUpperCase();\n\n const nansenProvider = getResearchProvider(\"nansen\") as import(\"../providers/research/nansen.js\").NansenProvider;\n\n // Two Nansen calls in parallel:\n // 1. HL perp trades — cross-venue smart money positioning\n // 2. Smart money netflow — aggregate on-chain accumulation/distribution\n const [hlPerpResult, flowResult] = await Promise.allSettled([\n nansenProvider.queryHyperliquidSmartMoney(hlSymbol),\n nansenProvider.query({ type: 'smart-money', target: hlSymbol }),\n ]);\n\n // Process HL perp trades — derive a smartMoney signal from recent trade direction\n if (hlPerpResult.status === 'fulfilled') {\n const hlResult = hlPerpResult.value;\n const trades = hlResult.data.trades as Array<Record<string, unknown>> | undefined;\n if (trades && trades.length > 0) {\n // Count longs vs shorts among smart money's recent trades\n let longValueUsd = 0;\n let shortValueUsd = 0;\n for (const t of trades) {\n const val = Number(t.value_usd ?? 0);\n const side = String(t.side ?? '').toLowerCase();\n if (side === 'long') longValueUsd += val;\n else if (side === 'short') shortValueUsd += val;\n }\n const totalValue = longValueUsd + shortValueUsd;\n const longRatio = totalValue > 0 ? longValueUsd / totalValue : 0.5;\n // longRatio 0.7+ = smart money heavily long = bullish\n // longRatio 0.3- = smart money heavily short = bearish\n const smBias = (longRatio - 0.5) * 2; // -1 to +1\n\n // Push as a smartMoney signal. Scale to ±0.5 max (not ±0.8) —\n // a single Nansen snapshot shouldn't dominate the entire score.\n // At smartMoney weight 0.15 (majors profile), ±0.5 contributes\n // ±0.075 to aggregate — meaningful but well below sentiment/onchain.\n signals.push({\n name: 'smartMoney',\n value: smBias * 0.5,\n confidence: Math.min(0.7, 0.3 + (trades.length / 30) * 0.4),\n source: 'Nansen HL Smart Money',\n details: `${trades.length} trades: $${(longValueUsd / 1e6).toFixed(1)}M long / $${(shortValueUsd / 1e6).toFixed(1)}M short (${(longRatio * 100).toFixed(0)}% long)`,\n });\n console.error(chalk.dim(` Nansen HL perps: ${trades.length} trades, ${(longRatio * 100).toFixed(0)}% long bias, cost ${hlResult.costUsdc}`));\n // Capture for WhaleIntent strategy\n nansenHlPerps = { longRatio, tradeCount: trades.length, longValueUsd, shortValueUsd };\n // Derive flow data from HL perps when netflow endpoint fails (422 on token_symbol)\n if (!nansenFlowData) {\n const netFlowFromPerps = longValueUsd - shortValueUsd;\n nansenFlowData = { netFlow24hUsd: netFlowFromPerps, traderCount: trades.length };\n }\n } else {\n console.error(chalk.dim(` Nansen HL perps: no recent trades for ${hlSymbol}`));\n }\n } else {\n console.error(chalk.dim(` x402 Nansen HL perps unavailable: ${hlPerpResult.reason}`));\n }\n\n // Smart Money Netflow — aggregate accumulation/distribution across chains\n if (flowResult.status === 'fulfilled' && flowResult.value?.data) {\n const flowData = flowResult.value.data as Record<string, unknown>;\n const flows = flowData.flows as Array<Record<string, unknown>> | undefined;\n // Sum net_flow_24h_usd across all chains for this token\n let netFlow = 0;\n let traderCount = 0;\n if (flows && flows.length > 0) {\n for (const f of flows) {\n netFlow += Number(f.net_flow_24h_usd ?? 0);\n traderCount += Number(f.trader_count ?? 0);\n }\n } else {\n // Single-row response format\n netFlow = Number(flowData.net_flow_24h_usd ?? 0);\n traderCount = Number(flowData.trader_count ?? 0);\n }\n\n if (netFlow !== 0 && !isNaN(netFlow)) {\n const normalizedFlow = Math.max(-0.5, Math.min(0.5, netFlow / 2_000_000));\n signals.push({\n name: 'flowIntelligence',\n value: normalizedFlow,\n confidence: Math.min(0.7, 0.3 + Math.abs(normalizedFlow)),\n source: 'Nansen Smart Money Netflow',\n details: `Smart money 24h net flow: $${(netFlow / 1_000_000).toFixed(2)}M ${netFlow > 0 ? '(accumulating)' : '(distributing)'} (${traderCount} wallets)`,\n });\n console.error(chalk.dim(` Nansen netflow: $${(netFlow / 1_000_000).toFixed(2)}M, ${traderCount} wallets, cost ${flowResult.value.costUsdc}`));\n }\n // Capture for NarrativeVacuum + WhaleIntent strategies\n nansenFlowData = { netFlow24hUsd: netFlow, traderCount };\n }\n\n // Push event signal (no Messari — use free path)\n signals.push(scoreEvent({}));\n }\n // Free path: don't push empty fundamental/event signals that always return\n // value=0. They participate in per-category weight normalization and dilute\n // signals that actually fire. Only push when there's real data (TVL from\n // phase 1, or Nansen/Messari from x402).\n\n // Phase 2b: Fincept data (parallel, all fault-tolerant)\n const [messariResult, btcNetResult, predictionResult, socialResult, kronosResult] =\n await Promise.allSettled([\n getMessariFundamentals(tokenId),\n tokenId === 'bitcoin' ? getBtcNetworkStats() : Promise.resolve(null),\n getCryptoPredictions(),\n getSocialData(tokenId),\n // Kronos vol forecast — uses existing candles (1h cache, ~2.3s on CPU)\n candles && candles.length >= 30 ? getKronosVolForecast(tokenId, candles) : Promise.resolve(null),\n ]);\n\n const messariFundamentals = messariResult.status === 'fulfilled' ? messariResult.value ?? undefined : undefined;\n const btcNetworkData = btcNetResult.status === 'fulfilled' ? btcNetResult.value ?? undefined : undefined;\n const predictionData = predictionResult.status === 'fulfilled' && predictionResult.value?.length\n ? { markets: predictionResult.value }\n : undefined;\n const socialData = socialResult.status === 'fulfilled' ? socialResult.value ?? undefined : undefined;\n const kronosData = kronosResult.status === 'fulfilled' ? kronosResult.value ?? undefined : undefined;\n\n // Feed Messari fundamentals into scoreFundamental if available\n if (messariFundamentals && messariFundamentals.marketCap > 0) {\n const mcapToTvl = tvl && tvl > 0 ? messariFundamentals.marketCap / tvl : undefined;\n const existingFundIdx = signals.findIndex((s) => s.name === 'fundamental');\n const fundSignal = scoreFundamental({\n mcapToTvl,\n revenueGrowth: messariFundamentals.revenueGrowth7d,\n });\n if (existingFundIdx >= 0) {\n signals[existingFundIdx] = fundSignal;\n } else {\n signals.push(fundSignal);\n }\n }\n\n // Phase 3: Parallel strategy data fetching (symbol, funding rate, unlocks)\n let marketData: any = undefined;\n\n try {\n // Dead strategies removed in Apr 2026 audit (twitter, meanReversion,\n // tvlMomentum, tokenUnlock, smartMoney strategy).\n const [symbolResult, fundingRateResult, unlockResult] = await Promise.allSettled([\n // Resolve token symbol + get market data for strategies\n this.coingecko.getCoinDetails(tokenId).then(async (coinDetails) => {\n const symbol = coinDetails?.symbol?.toUpperCase();\n const marketData = await this.coingecko.getMarketData(tokenId, 7);\n return { symbol, marketData };\n }),\n\n // Fetch funding rate data (free, from Binance)\n this.fundingRate.getFundingRate(tokenId),\n\n // Fetch token unlock estimates (free, from DefiLlama FDV)\n this.tokenUnlocks.getUnlocks(tokenId),\n ]);\n\n // Process symbol/market data results\n let tokenSymbol: string | undefined;\n if (symbolResult.status === 'fulfilled' && symbolResult.value) {\n tokenSymbol = symbolResult.value.symbol;\n marketData = symbolResult.value.marketData;\n }\n\n // Process funding rate — prefer Hyperliquid (native, same venue we trade on).\n // Fall back to Binance only if HL doesn't cover this token.\n // HL publishes hourly funding; convert to 8h-equivalent so strategy\n // thresholds remain unit-consistent with Binance.\n let fundingRateData: StrategyContext['fundingRateData'] = undefined;\n if (hyperliquidData && typeof hyperliquidData.fundingRate === 'number' && Number.isFinite(hyperliquidData.fundingRate)) {\n const rate1h = hyperliquidData.fundingRate;\n const rate8h = rate1h * 8;\n const annualizedRate = rate1h * 24 * 365;\n fundingRateData = { rate8h, annualizedRate, exchange: 'hyperliquid' };\n } else if (fundingRateResult.status === 'fulfilled' && fundingRateResult.value) {\n const fr = fundingRateResult.value;\n fundingRateData = { rate8h: fr.rate8h, annualizedRate: fr.annualizedRate, exchange: fr.exchange };\n }\n\n // Process unlock results\n let unlockData: StrategyContext['unlockData'] = undefined;\n if (unlockResult.status === 'fulfilled' && unlockResult.value) {\n unlockData = unlockResult.value;\n }\n\n // Twitter data omitted — strategy disabled (see DEFAULT_STRATEGIES).\n const twitterData: StrategyContext['twitterData'] = undefined;\n\n // Hyperliquid data already processed in Phase 1\n\n const stratCtx: StrategyContext = {\n tokenId,\n candles, // reuse from phase 1\n technicals: technicalSignals,\n fearAndGreed: fearAndGreedValue !== undefined\n ? { value: fearAndGreedValue, classification: fearAndGreedValue < 25 ? 'fear' : fearAndGreedValue > 75 ? 'greed' : 'neutral' }\n : undefined,\n sentimentZScore, // reuse from phase 1\n tvlData: tvl,\n marketData,\n nansenData, // from phase 2\n messariData, // from phase 2\n dexData: undefined, // DexFlowStrategy fetches this internally\n fundingRateData, // from phase 3\n unlockData, // from phase 3\n twitterData, // from phase 3\n hyperliquidData, // from phase 3\n tokenSymbol, // from phase 3\n groupReturns: opts?.groupReturns, // cross-sectional momentum\n messariFundamentals,\n btcNetworkData,\n predictionData,\n socialData,\n kronosData,\n nansenFlowData,\n nansenHlPerps,\n };\n\n let strategySignals = await runStrategies(stratCtx, this.config.strategyConfigs);\n\n // Smooth fast/noisy signals (HL flow, smartMoney, dexFlow, fundingRate)\n // with a rolling 3-reading average. Slow signals pass through unchanged.\n // Disabled by default — enable via --smooth flag or config.smoothFastSignals.\n if (this.config.smoothFastSignals) {\n if (!this.smoother) {\n this.smoother = new SignalSmoother(\n new FileSmootherStorage(joinPath(getHomedir(), '.sherwood', 'agent', 'signal-cache.json')),\n DEFAULT_SMOOTHER_CONFIG,\n );\n }\n try {\n strategySignals = await this.smoother.smooth(tokenId, strategySignals);\n } catch (err) {\n console.error(chalk.dim(` Signal smoothing failed (using raw): ${(err as Error).message}`));\n }\n }\n\n // Merge strategy signals: only add those with meaningful confidence (>0.05)\n for (const sig of strategySignals) {\n if (sig.confidence > 0.05) {\n signals.push(sig);\n }\n }\n } catch (err) {\n console.error(chalk.dim(` Strategy modules failed: ${(err as Error).message}`));\n }\n\n // 6. Market regime detection\n let regimeAnalysis: RegimeAnalysis | undefined;\n try {\n let btcCandles: Candle[] = candles || [];\n\n // If analyzing a non-BTC token, fetch BTC candles for regime analysis.\n // Use Hyperliquid (free, no rate limits) instead of CoinGecko (429s, 400s).\n if (tokenId !== \"bitcoin\" && (!candles || candles.length < 60)) {\n const hlBtcCandles = await this.hyperliquid.getCandles(\"bitcoin\", \"4h\", 30 * 24 * 60 * 60 * 1000);\n if (hlBtcCandles && hlBtcCandles.length > 60) {\n btcCandles = hlBtcCandles;\n }\n }\n\n if (btcCandles.length > 60) {\n regimeAnalysis = await this.regimeDetector.detect(btcCandles);\n }\n } catch (err) {\n console.error(chalk.dim(` Regime detection failed: ${(err as Error).message}`));\n }\n\n // 7. BTC correlation check\n let correlationCheck: CorrelationCheck | undefined;\n try {\n correlationCheck = await this.correlationGuard.checkCorrelation(tokenId);\n } catch (err) {\n console.error(chalk.dim(` Correlation check failed: ${(err as Error).message}`));\n }\n\n // 8. Compute decision with regime adjustments, correlation suppression,\n // and regime-conditional action thresholds. Weights resolution order:\n // explicit this.config.weights > weightProfile > per-token auto-profile.\n const resolvedWeights = this.config.weights\n ?? profileForToken(tokenId, this.config.weightProfile);\n const decision = computeTradeDecision(\n signals,\n resolvedWeights,\n regimeAnalysis?.strategyAdjustments,\n correlationCheck,\n regimeAnalysis?.regime,\n x402Available,\n );\n\n const currentPrice = hyperliquidData?.markPrice\n ?? (candles && candles.length > 0 ? candles[candles.length - 1]!.close : 0);\n\n const result: TokenAnalysis = {\n token: tokenId,\n decision,\n data: { technicalSignals, fearAndGreed: fearAndGreedValue, tvl, price: currentPrice },\n regime: regimeAnalysis,\n correlation: correlationCheck,\n kronosVol4h: kronosData?.predictedVol4h,\n };\n\n // ── Velocity freshness gate (Orca-inspired) ──\n // Refuse BUY/SELL if the most recent short-term price move is flat or\n // opposed to the signal direction — prevents \"buying local tops\" and\n // \"shorting local bottoms\" when the composite score fires late.\n const gateConfig: EntryGateConfig = {\n ...DEFAULT_ENTRY_GATE_CONFIG,\n ...this.config.entryGates,\n // Legacy opt-out flag wins if set explicitly.\n ...(this.config.disableVelocityGate ? { velocityGateEnabled: false } : {}),\n };\n // Hyperliquid does not currently expose a `priceChg1h` field; resolveVelocity\n // is written to accept one in case the provider adds it later. For now we\n // always fall through to the candle-based derivation (4h cadence with\n // days=30 OHLC — tightest proxy available).\n const priceChg1h = (hyperliquidData as { priceChg1h?: number } | undefined)?.priceChg1h;\n const velocity = resolveVelocity(priceChg1h, candles);\n const velocityGated = applyVelocityGate(result, velocity, gateConfig, (msg) =>\n console.error(chalk.yellow(msg)),\n );\n\n // Regime gate — block shorts in non-bearish regimes (trending-up, ranging,\n // low-volatility). Trade log analysis: 25% short WR (-$194) vs 67% long WR\n // (+$279). Most short losses were counter-trend fades.\n const regimeGated = applyRegimeGate(velocityGated, regimeAnalysis?.regime, gateConfig, (msg) =>\n console.error(chalk.yellow(msg)),\n );\n\n // Real-alpha gate — block entries that are only supported by noisy inputs.\n const gatedResult = applyRealAlphaGate(regimeGated, gateConfig, (msg) =>\n console.error(chalk.yellow(msg)),\n );\n\n // Note: logSignal() is intentionally NOT called here. It runs in\n // analyzeAll() AFTER the judge pass, so judge verdicts can be included\n // in signal-history.jsonl. See analyzeAll() below.\n\n return gatedResult;\n }\n\n /** Update the token watchlist without recreating the agent (preserves caches). */\n updateTokens(tokens: string[]): void {\n this.config.tokens = tokens;\n }\n\n /** Register a getter for current portfolio state. Called by AgentLoop so\n * the judge can see real openCount / cashPct / daily PnL / cooldowns when\n * building its veto decisions. If not set, judge uses neutral defaults. */\n setPortfolioStateGetter(fn: () => PortfolioState | undefined): void {\n this.portfolioStateGetter = fn;\n }\n\n /** Analyze all watchlist tokens.\n *\n * When `x402TopN` is set and x402 is enabled, uses a two-pass approach:\n * 1. Score ALL tokens with free signals only (no x402 cost)\n * 2. Re-score the top N tokens by free-signal score WITH x402 paid data\n *\n * This reduces x402 cost from ~$0.16×10 = $1.60/run to ~$0.16×3 = $0.48/run\n * while concentrating paid data on the tokens most likely to fire trades.\n */\n async analyzeAll(): Promise<TokenAnalysis[]> {\n const topN = this.config.x402TopN;\n const shouldTwoPass = this.config.useX402 && topN && topN > 0 && topN < this.config.tokens.length;\n\n // ── Cross-sectional momentum: pre-compute 7-day returns for all tokens ──\n // Fetches 1d candles (8 days lookback) from Hyperliquid for each token.\n // These are lightweight requests (8 data points each) and Hyperliquid has\n // no rate limit, so the overhead is minimal (~50-100ms total, parallelized).\n const groupReturns: Record<string, number> = {};\n try {\n const returnFetches = this.config.tokens.map(async (token) => {\n const candles = await this.hyperliquid.getCandles(token, '1d', 8 * 24 * 60 * 60 * 1000);\n if (candles && candles.length >= 2) {\n const firstClose = candles[0]!.close;\n const lastClose = candles[candles.length - 1]!.close;\n if (firstClose > 0) {\n groupReturns[token] = (lastClose / firstClose) - 1;\n }\n }\n });\n await Promise.all(returnFetches);\n } catch (err) {\n console.error(chalk.dim(` Cross-sectional group returns failed: ${(err as Error).message}`));\n }\n const hasGroupReturns = Object.keys(groupReturns).length >= 3;\n\n let results: TokenAnalysis[];\n\n if (!shouldTwoPass) {\n // Original single-pass: analyze all tokens with whatever x402 config says\n results = [];\n for (const token of this.config.tokens) {\n const result = await this.analyzeToken(token, {\n groupReturns: hasGroupReturns ? groupReturns : undefined,\n });\n results.push(result);\n }\n } else {\n // Pass 1: all tokens with free signals only\n console.error(chalk.dim(` x402 top-${topN} mode: scoring all ${this.config.tokens.length} tokens with free signals first...`));\n const freeResults: TokenAnalysis[] = [];\n for (const token of this.config.tokens) {\n const result = await this.analyzeToken(token, {\n skipX402: true,\n groupReturns: hasGroupReturns ? groupReturns : undefined,\n });\n freeResults.push(result);\n }\n\n // Sort by absolute score descending — top N get the x402 enrichment\n const ranked = [...freeResults].sort(\n (a, b) => Math.abs(b.decision.score) - Math.abs(a.decision.score),\n );\n const topTokens = new Set(ranked.slice(0, topN).map((r) => r.token));\n console.error(chalk.dim(` x402 enriching top ${topN}: ${[...topTokens].join(', ')}`));\n\n // Pass 2: re-analyze top N with x402 enabled\n const enrichedMap = new Map<string, TokenAnalysis>();\n for (const token of topTokens) {\n const enriched = await this.analyzeToken(token, {\n groupReturns: hasGroupReturns ? groupReturns : undefined,\n });\n enrichedMap.set(token, enriched);\n }\n\n // Merge: use enriched results for top N, free results for the rest\n results = freeResults.map((r) => enrichedMap.get(r.token) ?? r);\n }\n\n // ── LLM Judge pass ──\n // After all tokens scored, apply the judge to borderline actionable decisions.\n // Budget-gated: only the top-N borderline candidates are judged per cycle.\n const judgeConfig: JudgeConfig = { ...DEFAULT_JUDGE_CONFIG, ...this.config.judge };\n if (judgeConfig.enabled) {\n const candidates = selectJudgeCandidates(\n results.map((r) => ({ token: r.token, score: r.decision.score, action: r.decision.action })),\n judgeConfig,\n );\n\n if (candidates.size > 0) {\n console.error(chalk.dim(` [judge] Reviewing ${candidates.size} borderline decision(s): ${[...candidates].join(', ')}`));\n }\n\n // Snapshot portfolio state once for the judge pass — all tokens in this\n // cycle see the same portfolio view. Cheaper than a per-token load and\n // avoids races with any concurrent mutation.\n const pstate = this.portfolioStateGetter?.();\n if (!pstate && candidates.size > 0 && !this.warnedNoPortfolioGetter) {\n console.warn(chalk.yellow(\n ` [judge] Warning: portfolio state getter not wired. Judge portfolio-veto branch disabled — using neutral defaults.`,\n ));\n this.warnedNoPortfolioGetter = true;\n }\n\n for (const result of results) {\n if (!candidates.has(result.token)) continue;\n\n // Build real portfolio snapshot for the judge. Falls back to neutral\n // defaults when the getter isn't wired (e.g. one-shot analyzeAll callers).\n let portfolio: JudgeContext[\"portfolio\"] = {\n openCount: 0, cashPct: 1, hasPositionThisToken: false, inStopCooldown: false, dailyPnlPct: 0,\n };\n if (pstate) {\n const totalValue = pstate.totalValue || pstate.cash || 0;\n const cooldowns = pstate.stopCooldowns ?? {};\n const cooldownAt = cooldowns[result.token];\n // Match RiskManager's STOP_COOLDOWN_MS (4h). We only need to know\n // whether a cooldown is currently active, not the exact deadline.\n const STOP_COOLDOWN_MS = 4 * 60 * 60 * 1000;\n const inStopCooldown = cooldownAt !== undefined &&\n (Date.now() - cooldownAt) < STOP_COOLDOWN_MS;\n // dailyPnlPct ≈ dailyPnl / (totalValue − dailyPnl), reconstructing\n // the start-of-day value from current total minus today's delta.\n const startOfDayValue = totalValue - pstate.dailyPnl;\n portfolio = {\n openCount: pstate.positions.length,\n cashPct: totalValue > 0 ? pstate.cash / totalValue : 1,\n hasPositionThisToken: !!pstate.positions.find((p) => p.tokenId === result.token),\n inStopCooldown,\n dailyPnlPct: startOfDayValue > 0 ? pstate.dailyPnl / startOfDayValue : 0,\n };\n }\n\n const ctx: JudgeContext = {\n tokenId: result.token,\n currentPrice: result.data.price ?? 0,\n decision: result.decision,\n technicalSignals: result.data.technicalSignals,\n fearAndGreed: result.data.fearAndGreed,\n regime: result.regime?.regime,\n btcBias: result.correlation?.btcBias,\n suppressionFactor: result.correlation?.suppressionFactor,\n portfolio,\n };\n\n const judgeResult = await judge(ctx, judgeConfig);\n result.judgeResult = judgeResult;\n\n if (judgeResult.verdict.verdict === \"veto\") {\n result.preJudge = { action: result.decision.action, score: result.decision.score };\n result.decision = { ...result.decision, action: \"HOLD\" };\n console.error(chalk.yellow(` [judge] VETO ${result.token}: ${judgeResult.verdict.reasoning}`));\n } else if (judgeResult.verdict.verdict === \"confirm\" && judgeResult.verdict.reasoning !== \"fallback\") {\n console.error(chalk.dim(` [judge] Confirmed ${result.token} (${judgeResult.cached ? \"cached\" : `${judgeResult.latencyMs}ms`})`));\n }\n }\n }\n\n // Persist analysis to ~/.sherwood/agent/signal-history.jsonl. Runs AFTER\n // the judge pass so judgeVerdict fields land in signal-history.jsonl.\n // Fire-and-forget — never blocks scoring, never crashes on disk failure.\n for (const result of results) {\n const resolvedWeights = this.config.weights\n ?? profileForToken(result.token, this.config.weightProfile);\n const price = result.data.price ?? 0;\n const judgeData: JudgeLogData | undefined = result.judgeResult\n ? {\n verdict: result.judgeResult.verdict,\n // Prefer pre-veto action/score when judge vetoed; otherwise log\n // current (post-judge) action/score — they're identical on confirm.\n preJudgeAction: result.preJudge?.action ?? result.decision.action,\n preJudgeScore: result.preJudge?.score ?? result.decision.score,\n latencyMs: result.judgeResult.latencyMs,\n cached: result.judgeResult.cached,\n }\n : undefined;\n logSignal(result, price, resolvedWeights, judgeData);\n }\n\n return results;\n }\n\n /** Analyze all tokens and generate alerts for state changes. */\n async analyzeAllWithAlerts(): Promise<{ analyses: TokenAnalysis[]; alerts: Alert[] }> {\n const analyses = await this.analyzeAll();\n const alerts = await this.alertSystem.processAnalysis(analyses);\n return { analyses, alerts };\n }\n\n /** Get recent alerts. */\n async getRecentAlerts(maxAge?: number): Promise<Alert[]> {\n return this.alertSystem.getRecentAlerts(maxAge);\n }\n\n /** Clear all alerts. */\n async clearAlerts(): Promise<void> {\n return this.alertSystem.clearAlerts();\n }\n\n /** Get urgent alerts (CRITICAL/HIGH from last 30min) and mark as sent. */\n async getUrgentAlerts(): Promise<Alert[]> {\n return this.alertSystem.getUrgentAlerts();\n }\n\n /** Format alerts for display. */\n formatAlerts(alerts: Alert[], useMarkdown: boolean = false): string {\n return this.alertSystem.formatAlerts(alerts, useMarkdown);\n }\n\n /** Format analysis results for display. */\n formatAnalysis(results: TokenAnalysis[]): string {\n const lines: string[] = [];\n\n // Header\n lines.push(\"\");\n lines.push(chalk.bold(\" Sherwood Trading Agent — Analysis Results\"));\n lines.push(chalk.dim(\" \" + \"─\".repeat(60)));\n\n // Market regime display (use the first result's regime if available)\n const regime = results[0]?.regime;\n if (regime) {\n const regimeColor = regime.regime === \"trending-up\" ? chalk.green :\n regime.regime === \"trending-down\" ? chalk.red :\n regime.regime === \"ranging\" ? chalk.yellow :\n regime.regime === \"high-volatility\" ? chalk.magenta :\n chalk.cyan;\n\n const regimeStr = regimeColor(regime.regime.toUpperCase().replace('-', ' '));\n const confidenceStr = `${Math.round(regime.confidence * 100)}%`;\n const trendStr = regime.btcTrend === \"up\" ? chalk.green(\"↑\") :\n regime.btcTrend === \"down\" ? chalk.red(\"↓\") :\n chalk.yellow(\"→\");\n\n lines.push(` Market Regime: ${regimeStr} (${confidenceStr} confidence) | BTC trend: ${trendStr} | Volatility: ${regime.volatilityLevel}`);\n lines.push(chalk.dim(\" \" + \"─\".repeat(60)));\n }\n\n // BTC correlation display (use the first non-BTC result's correlation if available)\n const correlation = results.find(r => r.token !== \"bitcoin\")?.correlation;\n if (correlation) {\n const biasColor = correlation.btcBias === \"bullish\" ? chalk.green :\n correlation.btcBias === \"bearish\" ? chalk.red :\n chalk.yellow;\n\n const biasStr = biasColor(correlation.btcBias.toUpperCase());\n const scoreStr = correlation.btcScore >= 0 ? `+${correlation.btcScore.toFixed(2)}` : correlation.btcScore.toFixed(2);\n\n let suppressionStr = \"\";\n if (correlation.shouldSuppress && correlation.btcBias === \"bearish\") {\n const suppressionPct = Math.round((1 - correlation.suppressionFactor) * 100);\n suppressionStr = ` — alt longs suppressed ${suppressionPct}%`;\n } else if (!correlation.shouldSuppress && correlation.btcBias === \"bullish\") {\n const boostPct = Math.round((correlation.suppressionFactor - 1) * 100);\n if (boostPct > 0) suppressionStr = ` — alt longs boosted ${boostPct}%`;\n }\n\n lines.push(` BTC Correlation: ${biasStr} (${scoreStr})${suppressionStr}`);\n lines.push(chalk.dim(\" \" + \"─\".repeat(60)));\n }\n\n lines.push(\"\");\n\n // Column headers\n const header = ` ${\"Token\".padEnd(14)} ${\"Score\".padEnd(8)} ${\"Action\".padEnd(14)} ${\"Conf\".padEnd(8)} Key Signals`;\n lines.push(chalk.bold(header));\n lines.push(chalk.dim(\" \" + \"─\".repeat(80)));\n\n for (const r of results) {\n const d = r.decision;\n\n // Color the action\n let actionStr: string;\n switch (d.action) {\n case \"STRONG_BUY\":\n actionStr = chalk.bgGreen.black(\" STRONG BUY \");\n break;\n case \"BUY\":\n actionStr = chalk.green(\"BUY\");\n break;\n case \"HOLD\":\n actionStr = chalk.yellow(\"HOLD\");\n break;\n case \"SELL\":\n actionStr = chalk.red(\"SELL\");\n break;\n case \"STRONG_SELL\":\n actionStr = chalk.bgRed.white(\" STRONG SELL \");\n break;\n }\n\n // Score with color\n const scoreColor = d.score > 0.3 ? chalk.green : d.score < -0.3 ? chalk.red : chalk.yellow;\n const scoreStr = scoreColor(d.score.toFixed(3).padStart(7));\n\n // Confidence\n const confStr = `${(d.confidence * 100).toFixed(0)}%`;\n\n // Key signals (first 2)\n const keySignals = d.signals\n .filter((s) => Math.abs(s.value) > 0.1)\n .slice(0, 2)\n .map((s) => {\n const v = s.value;\n const color = v > 0 ? chalk.green : v < 0 ? chalk.red : chalk.yellow;\n return color(`${s.source}: ${v > 0 ? \"+\" : \"\"}${v.toFixed(2)}`);\n })\n .join(\", \");\n\n lines.push(\n ` ${r.token.padEnd(14)} ${scoreStr} ${actionStr.padEnd(14)} ${confStr.padEnd(8)} ${keySignals}`,\n );\n }\n\n lines.push(\"\");\n lines.push(chalk.dim(` Generated at ${new Date().toISOString()}`));\n lines.push(\"\");\n\n return lines.join(\"\\n\");\n }\n}\n","/**\n * DefiLlama free API provider — no API key needed.\n * Provides TVL, protocol, DEX volume, yield, price, and stablecoin data.\n */\n\nimport type { Provider, ProviderInfo } from \"../../types.js\";\n\nexport class DefiLlamaProvider implements Provider {\n info(): ProviderInfo {\n return {\n name: \"DefiLlama\",\n type: \"research\",\n capabilities: [\n \"protocol-tvl\",\n \"protocol-list\",\n \"protocol-details\",\n \"dex-volumes\",\n \"yields\",\n \"token-prices\",\n \"stablecoins\",\n ],\n supportedChains: [],\n };\n }\n\n /** Get current TVL for a protocol (returns a number). */\n async getProtocolTvl(protocol: string): Promise<number> {\n try {\n const res = await fetch(`https://api.llama.fi/tvl/${encodeURIComponent(protocol)}`);\n if (!res.ok) throw new Error(`DefiLlama tvl error: ${res.status} ${res.statusText}`);\n const data = await res.json();\n return data as number;\n } catch (err) {\n throw new Error(`Failed to fetch TVL for ${protocol}: ${(err as Error).message}`);\n }\n }\n\n /** Get all protocols with TVL & chain info. */\n async getProtocols(): Promise<any[]> {\n try {\n const res = await fetch(\"https://api.llama.fi/protocols\");\n if (!res.ok) throw new Error(`DefiLlama protocols error: ${res.status} ${res.statusText}`);\n return (await res.json()) as any[];\n } catch (err) {\n throw new Error(`Failed to fetch protocols: ${(err as Error).message}`);\n }\n }\n\n /** Get detailed protocol info with historical TVL. */\n async getProtocolDetails(protocol: string): Promise<any> {\n try {\n const res = await fetch(`https://api.llama.fi/protocol/${encodeURIComponent(protocol)}`);\n if (!res.ok) throw new Error(`DefiLlama protocol detail error: ${res.status} ${res.statusText}`);\n return await res.json();\n } catch (err) {\n throw new Error(`Failed to fetch protocol details for ${protocol}: ${(err as Error).message}`);\n }\n }\n\n /** Get DEX volumes, optionally filtered by chain. */\n async getDexVolumes(chain?: string): Promise<any> {\n try {\n const url = chain\n ? `https://api.llama.fi/overview/dexs/${encodeURIComponent(chain)}`\n : \"https://api.llama.fi/overview/dexs\";\n const res = await fetch(url);\n if (!res.ok) throw new Error(`DefiLlama dex volumes error: ${res.status} ${res.statusText}`);\n return await res.json();\n } catch (err) {\n throw new Error(`Failed to fetch DEX volumes: ${(err as Error).message}`);\n }\n }\n\n /** Get yield/pool data from DefiLlama yields API. */\n async getYields(): Promise<any> {\n try {\n const res = await fetch(\"https://yields.llama.fi/pools\");\n if (!res.ok) throw new Error(`DefiLlama yields error: ${res.status} ${res.statusText}`);\n return await res.json();\n } catch (err) {\n throw new Error(`Failed to fetch yields: ${(err as Error).message}`);\n }\n }\n\n /**\n * Get current token prices.\n * @param coins Array of \"chain:address\" strings, e.g. [\"ethereum:0x...\"]\n */\n async getTokenPrices(coins: string[]): Promise<any> {\n try {\n const joined = coins.map(encodeURIComponent).join(\",\");\n const res = await fetch(`https://coins.llama.fi/prices/current/${joined}`);\n if (!res.ok) throw new Error(`DefiLlama prices error: ${res.status} ${res.statusText}`);\n return await res.json();\n } catch (err) {\n throw new Error(`Failed to fetch token prices: ${(err as Error).message}`);\n }\n }\n\n /** Get stablecoin data. */\n async getStablecoins(): Promise<any> {\n try {\n const res = await fetch(\"https://stablecoins.llama.fi/stablecoins\");\n if (!res.ok) throw new Error(`DefiLlama stablecoins error: ${res.status} ${res.statusText}`);\n return await res.json();\n } catch (err) {\n throw new Error(`Failed to fetch stablecoins: ${(err as Error).message}`);\n }\n }\n}\n","/**\n * CoinGecko free API provider with aggressive caching and rate-limiting.\n */\n\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { createHash } from 'node:crypto';\nimport type { Provider, ProviderInfo } from \"../../types.js\";\n\nconst BASE_URL = \"https://api.coingecko.com/api/v3\";\n\n// Shared mutex queue across all instances to prevent 429s.\n// Each request chains onto this promise so only one runs at a time with a 2.5s gap.\nlet requestQueue: Promise<void> = Promise.resolve();\nlet sharedLastCallTime = 0;\nlet MIN_INTERVAL = 2500; // 2.5s between calls (CG free tier: 30/min = 1 every 2s)\n\n// Circuit breaker: when we observe a 429, freeze all CG requests for this window.\n// Previous behavior retried 10s → 30s → 60s per call; because the queue is global\n// serial, a single rate-limit window ballooned one cycle from ~60s to ~15 min\n// (18 tokens × up to 100s per 429). Now we fail fast, let callers use cache/null,\n// and resume after the window. Break resets once a successful call lands.\nconst CIRCUIT_BREAK_MS = 5 * 60 * 1000; // 5 minutes\nlet circuitOpenUntil = 0;\n\n// Cache TTLs by endpoint (in milliseconds)\nconst CACHE_TTLS = {\n ohlc: 2 * 60 * 60 * 1000, // 2 hours - historical candles don't change\n market_chart: 60 * 60 * 1000, // 1 hour\n coin_details: 60 * 60 * 1000, // 1 hour\n simple_price: 5 * 60 * 1000, // 5 minutes\n trending: 30 * 60 * 1000, // 30 minutes\n} as const;\n\nexport class CoinGeckoProvider implements Provider {\n private cacheDir: string;\n private apiKey?: string;\n\n constructor() {\n this.cacheDir = join(homedir(), '.sherwood', 'agent', 'cache', 'coingecko');\n // Check for API key from env or config\n this.apiKey = process.env.COINGECKO_API_KEY;\n\n // Adjust throttle for demo key\n if (this.apiKey) {\n MIN_INTERVAL = 1500; // Demo key has better limits\n }\n }\n\n info(): ProviderInfo {\n return {\n name: \"CoinGecko\",\n type: \"research\",\n capabilities: [\"price\", \"market-data\", \"ohlc\", \"coin-details\", \"trending\"],\n supportedChains: [],\n };\n }\n\n /**\n * Generate cache key from endpoint and params.\n */\n private getCacheKey(endpoint: string, params: string): string {\n const hash = createHash('sha1').update(endpoint + params).digest('hex').substring(0, 12);\n return `${hash}.json`;\n }\n\n /**\n * Get cache TTL for an endpoint.\n */\n private getCacheTTL(url: string): number {\n if (url.includes('/ohlc')) return CACHE_TTLS.ohlc;\n if (url.includes('/market_chart')) return CACHE_TTLS.market_chart;\n if (url.includes('/coins/') && !url.includes('/market_chart')) return CACHE_TTLS.coin_details;\n if (url.includes('/simple/price')) return CACHE_TTLS.simple_price;\n if (url.includes('/trending')) return CACHE_TTLS.trending;\n return CACHE_TTLS.simple_price; // Default fallback\n }\n\n /**\n * Read from cache if available and fresh.\n */\n private async readCache(cacheKey: string, ttl: number): Promise<any | null> {\n try {\n const cacheFile = join(this.cacheDir, cacheKey);\n const raw = await readFile(cacheFile, 'utf-8');\n const cached = JSON.parse(raw) as { ts: number; data: any };\n\n if (Date.now() - cached.ts < ttl) {\n return cached.data;\n }\n } catch {\n // Cache miss or invalid cache\n }\n return null;\n }\n\n /**\n * Write to cache with atomic write (tmp file + rename).\n */\n private async writeCache(cacheKey: string, data: any): Promise<void> {\n try {\n await mkdir(this.cacheDir, { recursive: true });\n const cacheFile = join(this.cacheDir, cacheKey);\n const tmpFile = cacheFile + '.tmp';\n\n await writeFile(tmpFile, JSON.stringify({ ts: Date.now(), data }), 'utf-8');\n // Atomic rename (requires fs.rename which we'll use from import)\n await import('node:fs/promises').then(fs => fs.rename(tmpFile, cacheFile));\n } catch {\n // Cache write failure is non-fatal\n }\n }\n\n /**\n * Serialised request method — every CoinGecko call goes through here.\n * Checks cache first, then uses shared promise chain for API calls.\n */\n private async fetchJson(url: string): Promise<any> {\n // Extract cache key from full URL path (includes token ID)\n const urlObj = new URL(url);\n const cacheKey = this.getCacheKey(urlObj.pathname, urlObj.search);\n const ttl = this.getCacheTTL(url);\n\n // Check cache first — if hit, return immediately without touching rate limiter\n const cached = await this.readCache(cacheKey, ttl);\n if (cached) {\n return cached;\n }\n\n // Circuit breaker: if a recent 429 tripped the breaker, fail fast instead of\n // queueing up more doomed calls. A cycle scanning 18 tokens was observed to\n // take 14-18 minutes during rate-limit windows because every serialized call\n // exhausted its 10s/30s/60s retry budget. Fail-fast lets each call burn\n // ~0ms instead of ~100s and returns control to the caller immediately.\n if (Date.now() < circuitOpenUntil) {\n throw new Error(`CoinGecko circuit breaker open — rate-limited, retrying after ${new Date(circuitOpenUntil).toISOString()}`);\n }\n\n // Cache miss — make API call with rate limiting\n const job = requestQueue.then(async () => {\n // Re-check breaker inside the serialized job: callers that queued behind\n // the 429-triggering call should also fail fast rather than replay 10-60s\n // backoffs one-by-one.\n if (Date.now() < circuitOpenUntil) {\n throw new Error(`CoinGecko circuit breaker open — rate-limited, retrying after ${new Date(circuitOpenUntil).toISOString()}`);\n }\n\n const now = Date.now();\n const elapsed = now - sharedLastCallTime;\n if (elapsed < MIN_INTERVAL) {\n await new Promise((resolve) => setTimeout(resolve, MIN_INTERVAL - elapsed));\n }\n sharedLastCallTime = Date.now();\n\n // Add API key header if available\n const headers: Record<string, string> = {};\n if (this.apiKey) {\n headers['x-cg-demo-api-key'] = this.apiKey;\n }\n\n const res = await fetch(url, { headers });\n if (res.status === 429) {\n // Rate limited — trip the circuit breaker and fail fast. Do NOT retry\n // inline: the 10s/30s/60s per-call backoff made cycles balloon to\n // 15+ minutes when CG was cranky. Caller gets a clean failure, can\n // fall back to cached/null, and subsequent CG calls in this cycle\n // skip the API entirely until the window closes.\n circuitOpenUntil = Date.now() + CIRCUIT_BREAK_MS;\n throw new Error(`CoinGecko 429 — circuit breaker opened for ${CIRCUIT_BREAK_MS / 1000}s — ${url}`);\n }\n if (!res.ok) throw new Error(`CoinGecko error: ${res.status} ${res.statusText} — ${url}`);\n\n const data = await res.json();\n // Success closes the breaker (we got through, so the rate window passed).\n circuitOpenUntil = 0;\n await this.writeCache(cacheKey, data);\n return data;\n });\n\n // Chain the next request after this one settles (success or failure)\n requestQueue = job.then(() => {}, () => {});\n return job;\n }\n\n /**\n * Get simple prices for multiple tokens.\n * Returns price, 24h vol, 24h change, and market cap per token.\n */\n async getPrice(\n ids: string[],\n vsCurrencies: string[] = [\"usd\"],\n ): Promise<Record<string, any>> {\n const params = new URLSearchParams({\n ids: ids.join(\",\"),\n vs_currencies: vsCurrencies.join(\",\"),\n include_24hr_vol: \"true\",\n include_24hr_change: \"true\",\n include_market_cap: \"true\",\n });\n return this.fetchJson(`${BASE_URL}/simple/price?${params}`);\n }\n\n /**\n * Get market chart data (prices, market_caps, total_volumes) over time.\n * Note: only fetches for a single id at a time.\n */\n async getMarketData(\n id: string,\n days: number = 30,\n ): Promise<{ prices: number[][]; market_caps: number[][]; total_volumes: number[][] }> {\n const params = new URLSearchParams({\n vs_currency: \"usd\",\n days: String(days),\n });\n return this.fetchJson(`${BASE_URL}/coins/${encodeURIComponent(id)}/market_chart?${params}`);\n }\n\n /**\n * Get OHLC candle data.\n * days: 1/7/14/30/90/180/365/max\n * Returns array of [timestamp, open, high, low, close].\n */\n async getOHLC(\n id: string,\n days: number = 30,\n ): Promise<number[][]> {\n const params = new URLSearchParams({\n vs_currency: \"usd\",\n days: String(days),\n });\n return this.fetchJson(`${BASE_URL}/coins/${encodeURIComponent(id)}/ohlc?${params}`);\n }\n\n /** Get detailed coin information. */\n async getCoinDetails(id: string): Promise<any> {\n const params = new URLSearchParams({\n localization: \"false\",\n tickers: \"false\",\n community_data: \"true\",\n developer_data: \"true\",\n });\n return this.fetchJson(`${BASE_URL}/coins/${encodeURIComponent(id)}?${params}`);\n }\n\n /** Get trending coins. */\n async getTrending(): Promise<any> {\n return this.fetchJson(`${BASE_URL}/search/trending`);\n }\n}\n","/**\n * Dedicated Fear & Greed Index provider with history tracking and caching.\n */\n\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\n\nexport interface FearGreedEntry {\n value: number;\n classification: string;\n timestamp: string;\n}\n\ninterface FearGreedApiResponse {\n name: string;\n data: Array<{\n value: string;\n value_classification: string;\n timestamp: string;\n }>;\n}\n\nconst API_URL = 'https://api.alternative.me/fng/';\n\nexport class FearGreedProvider {\n private cacheDir: string;\n private cacheFile: string;\n private cacheTTL = 30 * 60 * 1000; // 30 minutes\n\n constructor() {\n this.cacheDir = join(homedir(), '.sherwood', 'agent', 'cache');\n this.cacheFile = join(this.cacheDir, 'feargreed.json');\n }\n\n /** Get current Fear & Greed data. */\n async getCurrent(): Promise<FearGreedEntry> {\n const data = await this.fetchWithCache(1);\n if (!data.length) throw new Error('No Fear & Greed data available');\n return data[0]!;\n }\n\n /** Get historical data for the last N days. */\n async getHistory(days: number = 30): Promise<FearGreedEntry[]> {\n return this.fetchWithCache(days);\n }\n\n /**\n * Check if F&G has been in extreme zone for N consecutive days.\n * @param threshold - Value boundary (e.g. 25 for fear, 75 for greed)\n * @param days - Minimum consecutive days in extreme zone\n */\n async isExtreme(\n threshold: number = 25,\n days: number = 3,\n ): Promise<{ extreme: boolean; direction: 'fear' | 'greed' | null; consecutive: number }> {\n const history = await this.getHistory(Math.max(days + 5, 30));\n\n if (history.length === 0) {\n return { extreme: false, direction: null, consecutive: 0 };\n }\n\n // Check consecutive days of extreme fear (below threshold)\n let fearStreak = 0;\n for (const entry of history) {\n if (entry.value <= threshold) {\n fearStreak++;\n } else {\n break;\n }\n }\n\n // Check consecutive days of extreme greed (above 100 - threshold)\n const greedThreshold = 100 - threshold;\n let greedStreak = 0;\n for (const entry of history) {\n if (entry.value >= greedThreshold) {\n greedStreak++;\n } else {\n break;\n }\n }\n\n if (fearStreak >= days) {\n return { extreme: true, direction: 'fear', consecutive: fearStreak };\n }\n if (greedStreak >= days) {\n return { extreme: true, direction: 'greed', consecutive: greedStreak };\n }\n\n return {\n extreme: false,\n direction: null,\n consecutive: Math.max(fearStreak, greedStreak),\n };\n }\n\n /** Fetch data with local file cache. */\n private async fetchWithCache(limit: number): Promise<FearGreedEntry[]> {\n // Try reading cache\n try {\n const raw = await readFile(this.cacheFile, 'utf-8');\n const cached = JSON.parse(raw) as { ts: number; data: FearGreedEntry[] };\n if (Date.now() - cached.ts < this.cacheTTL && cached.data.length >= limit) {\n return cached.data.slice(0, limit);\n }\n } catch {\n // No cache or invalid — fetch fresh\n }\n\n const data = await this.fetchFromApi(Math.max(limit, 30));\n\n // Write cache\n try {\n await mkdir(this.cacheDir, { recursive: true });\n await writeFile(this.cacheFile, JSON.stringify({ ts: Date.now(), data }), 'utf-8');\n } catch {\n // Cache write failure is non-fatal\n }\n\n return data.slice(0, limit);\n }\n\n /** Fetch from the alternative.me API. */\n private async fetchFromApi(limit: number): Promise<FearGreedEntry[]> {\n const url = `${API_URL}?limit=${limit}`;\n const res = await fetch(url);\n if (!res.ok) {\n throw new Error(`Fear & Greed API error: ${res.status} ${res.statusText}`);\n }\n const json = (await res.json()) as FearGreedApiResponse;\n return json.data.map((d) => ({\n value: Number(d.value),\n classification: d.value_classification,\n timestamp: d.timestamp,\n }));\n }\n}\n","/**\n * Sentiment data provider — delegates to FearGreedProvider for API access,\n * adds z-score utilities and Provider interface compliance.\n */\n\nimport type { Provider, ProviderInfo } from \"../../types.js\";\nimport { FearGreedProvider, type FearGreedEntry } from \"./feargreed.js\";\n\n/** @deprecated Use FearGreedEntry from feargreed.ts instead. */\nexport type FearAndGreedData = FearGreedEntry;\n\nconst fearGreed = new FearGreedProvider();\n\nexport class SentimentProvider implements Provider {\n info(): ProviderInfo {\n return {\n name: \"Sentiment\",\n type: \"research\",\n capabilities: [\"fear-and-greed\", \"sentiment-zscore\"],\n supportedChains: [],\n };\n }\n\n /** Fetch last 30 days of Fear & Greed index data (delegates to FearGreedProvider). */\n async getFearAndGreed(): Promise<FearGreedEntry[]> {\n return fearGreed.getHistory(30);\n }\n\n /** Get just the latest Fear & Greed value (delegates to FearGreedProvider). */\n async getFearAndGreedCurrent(): Promise<FearGreedEntry> {\n return fearGreed.getCurrent();\n }\n\n /**\n * Compute z-score of the latest value compared to the array.\n * z = (latest - mean) / stddev\n */\n computeSentimentZScore(values: number[]): number {\n if (values.length < 2) return 0;\n const latest = values[0]!;\n const mean = values.reduce((a, b) => a + b, 0) / values.length;\n const variance = values.reduce((sum, v) => sum + (v - mean) ** 2, 0) / values.length;\n const stddev = Math.sqrt(variance);\n if (stddev === 0) return 0;\n return (latest - mean) / stddev;\n }\n}\n","/**\n * Breakout + On-Chain Confirmation Strategy\n * Detects price breakouts confirmed by volume and optionally by smart money flow.\n */\n\nimport type { Signal } from '../scoring.js';\nimport type { Strategy, StrategyContext, Candle } from './types.js';\nimport { clamp } from '../utils.js';\n\nfunction detect20DayBreakout(candles: Candle[]): { direction: 'up' | 'down' | 'none'; volumeConfirmed: boolean } {\n if (candles.length < 21) return { direction: 'none', volumeConfirmed: false };\n\n const lookback = candles.slice(-21, -1); // previous 20 candles (excluding latest)\n const latest = candles[candles.length - 1]!;\n\n const high20 = Math.max(...lookback.map(c => c.high));\n const low20 = Math.min(...lookback.map(c => c.low));\n\n // Volume confirmation: latest volume > 2x 20-period average\n const avgVolume = lookback.reduce((sum, c) => sum + c.volume, 0) / lookback.length;\n const volumeConfirmed = avgVolume > 0 && latest.volume > avgVolume * 2;\n\n if (latest.close > high20) {\n return { direction: 'up', volumeConfirmed };\n } else if (latest.close < low20) {\n return { direction: 'down', volumeConfirmed };\n }\n\n return { direction: 'none', volumeConfirmed: false };\n}\n\nfunction checkEmaAlignment(technicals: { ema: { ema8: number; ema21: number; ema50: number } }): 'bullish' | 'bearish' | 'neutral' {\n const { ema8, ema21, ema50 } = technicals.ema;\n if (isNaN(ema8) || isNaN(ema21) || isNaN(ema50)) return 'neutral';\n\n if (ema8 > ema21 && ema21 > ema50) return 'bullish';\n if (ema8 < ema21 && ema21 < ema50) return 'bearish';\n return 'neutral';\n}\n\nexport class BreakoutOnChainStrategy implements Strategy {\n name = 'breakoutOnChain';\n description = 'Detects 20-day breakouts with volume confirmation and optional smart-money support';\n requiredData = ['candles', 'technicals'];\n\n async analyze(ctx: StrategyContext): Promise<Signal> {\n if (!ctx.candles || ctx.candles.length < 21) {\n return {\n name: this.name,\n value: 0.0,\n confidence: 0.1,\n source: 'Breakout + On-Chain',\n details: 'Insufficient candle data for breakout detection (need 21+)',\n };\n }\n\n const details: string[] = [];\n let value = 0;\n let confidence = 0.4;\n\n // Detect breakout\n const breakout = detect20DayBreakout(ctx.candles);\n\n if (breakout.direction === 'none') {\n details.push('No 20-day breakout detected (ranging)');\n\n // Still check EMA alignment for a mild signal\n if (ctx.technicals) {\n const ema = checkEmaAlignment(ctx.technicals);\n if (ema === 'bullish') {\n value = 0.1;\n details.push('EMA alignment bullish (8>21>50)');\n } else if (ema === 'bearish') {\n value = -0.1;\n details.push('EMA alignment bearish (8<21<50)');\n }\n }\n\n return {\n name: this.name,\n value,\n confidence: 0.3,\n source: 'Breakout + On-Chain',\n details: details.join('; '),\n };\n }\n\n // Breakout detected\n if (breakout.direction === 'up') {\n value = breakout.volumeConfirmed ? 0.8 : 0.5;\n confidence = breakout.volumeConfirmed ? 0.7 : 0.5;\n details.push(`Bullish 20-day breakout${breakout.volumeConfirmed ? ' with volume confirmation (>2x avg)' : ' (low volume — caution)'}`);\n } else {\n value = breakout.volumeConfirmed ? -0.8 : -0.5;\n confidence = breakout.volumeConfirmed ? 0.7 : 0.5;\n details.push(`Bearish 20-day breakdown${breakout.volumeConfirmed ? ' with volume confirmation (>2x avg)' : ' (low volume — caution)'}`);\n }\n\n // EMA alignment bonus\n if (ctx.technicals) {\n const ema = checkEmaAlignment(ctx.technicals);\n if ((ema === 'bullish' && value > 0) || (ema === 'bearish' && value < 0)) {\n const bonus = value > 0 ? 0.2 : -0.2;\n value = clamp(value + bonus);\n confidence += 0.1;\n details.push(`EMA alignment confirms direction (+${Math.abs(bonus).toFixed(1)})`);\n } else if ((ema === 'bearish' && value > 0) || (ema === 'bullish' && value < 0)) {\n details.push('EMA alignment contradicts breakout — mixed signal');\n confidence -= 0.1;\n }\n }\n\n // Nansen smart money confirmation (optional)\n if (ctx.nansenData) {\n const data = ctx.nansenData as Record<string, unknown>;\n const flows = data.flows as Array<Record<string, unknown>> | undefined;\n if (flows && flows.length > 0) {\n const netflow = flows.reduce(\n (sum, f) => sum + (Number(f.netflow ?? f.net_flow ?? 0)),\n 0,\n );\n // Negative netflow = buying, positive = selling\n const smartMoneyBuying = netflow < 0;\n const smartMoneySelling = netflow > 0;\n\n if ((smartMoneyBuying && value > 0) || (smartMoneySelling && value < 0)) {\n const bonus = value > 0 ? 0.2 : -0.2;\n value = clamp(value + bonus);\n confidence += 0.1;\n details.push(`Smart money confirms breakout direction (+${Math.abs(bonus).toFixed(1)})`);\n } else if ((smartMoneySelling && value > 0) || (smartMoneyBuying && value < 0)) {\n details.push('Smart money diverges from breakout — caution');\n confidence -= 0.05;\n }\n }\n }\n\n return {\n name: this.name,\n value: clamp(value),\n confidence: Math.min(Math.max(confidence, 0.1), 1.0),\n source: 'Breakout + On-Chain',\n details: details.join('; '),\n };\n }\n}\n","/**\n * Funding Rate Harvester Strategy\n * Looks at perpetual futures funding rates for arbitrage opportunities.\n * Currently a placeholder — requires external funding rate API integration.\n */\n\nimport type { Signal } from '../scoring.js';\nimport type { Strategy, StrategyContext } from './types.js';\nimport { clamp } from '../utils.js';\n\n/**\n * Calculate annualized yield from an 8-hour funding rate.\n * Funding is paid 3x per day (every 8 hours).\n * @param rate8h - The 8-hour funding rate as a decimal (e.g., 0.0005 = 0.05%)\n * @returns Annualized yield as a decimal (e.g., 0.5475 = 54.75%)\n */\nexport function annualizedYieldFromFundingRate(rate8h: number): number {\n // 3 funding periods per day × 365 days = 1095 periods per year\n return rate8h * 3 * 365;\n}\n\n/**\n * Calculate expected return from funding rate arbitrage over a period.\n * @param rate8h - 8-hour funding rate as decimal\n * @param days - Holding period in days\n * @returns Expected return as decimal\n */\nexport function expectedReturnFromFunding(rate8h: number, days: number): number {\n return rate8h * 3 * days;\n}\n\nexport class FundingRateStrategy implements Strategy {\n name = 'fundingRate';\n description = 'Identifies funding rate arbitrage opportunities in perp markets (requires external data source)';\n requiredData = ['fundingRateData'];\n\n async analyze(ctx: StrategyContext): Promise<Signal> {\n // Check if funding rate data is available in the context\n // For now, this is a placeholder — funding rate data isn't provided by current providers\n const fundingData = ctx.fundingRateData;\n\n if (!fundingData) {\n return {\n name: this.name,\n value: 0.0,\n confidence: 0.0,\n source: 'Funding Rate Harvester',\n details: 'No funding rate data available — requires external data source (Binance, Bybit, etc.)',\n };\n }\n\n const rate = fundingData.rate8h;\n const annualized = annualizedYieldFromFundingRate(rate);\n const ratePct = (rate * 100).toFixed(4);\n const annualizedPct = (annualized * 100).toFixed(1);\n const details: string[] = [];\n let value = 0;\n let confidence = 0.5;\n\n // Thresholds calibrated to BTC/ETH majors (typical 8h funding is ±0.01%).\n // Tightened from the previous altcoin-era defaults that only fired at\n // ±0.02%/±0.05%, which almost never happen on majors in chop.\n if (rate > 0.0002) {\n // High positive funding: longs pay shorts → market overleveraged long → bearish contrarian\n value = -0.5;\n confidence = 0.6;\n details.push(\n `High positive funding ${ratePct}% (annualized ${annualizedPct}%): overleveraged longs — bearish contrarian on ${fundingData.exchange}`,\n );\n } else if (rate > 0.00005) {\n // Mild positive funding: slightly overleveraged long → mildly bearish\n value = -0.3;\n confidence = 0.4;\n details.push(\n `Mild positive funding ${ratePct}% (annualized ${annualizedPct}%): longs paying shorts — mildly bearish on ${fundingData.exchange}`,\n );\n } else if (rate < -0.0002) {\n // High negative funding: shorts pay longs → market overleveraged short → bullish contrarian\n value = 0.5;\n confidence = 0.6;\n details.push(\n `High negative funding ${ratePct}% (annualized ${annualizedPct}%): overleveraged shorts — bullish contrarian on ${fundingData.exchange}`,\n );\n } else if (rate < -0.00005) {\n // Mild negative funding: slightly overleveraged short → mildly bullish\n value = 0.3;\n confidence = 0.4;\n details.push(\n `Mild negative funding ${ratePct}% (annualized ${annualizedPct}%): shorts paying longs — mildly bullish on ${fundingData.exchange}`,\n );\n } else {\n // Funding within ±0.005% (8h): truly neutral\n value = 0.0;\n confidence = 0.3;\n details.push(\n `Funding rate ${ratePct}% near neutral: no directional signal`,\n );\n }\n\n return {\n name: this.name,\n value: clamp(value),\n confidence,\n source: 'Funding Rate Harvester',\n details: details.join('; '),\n };\n }\n}\n","/**\n * DEXScreener free API provider — real-time DEX data across all chains.\n * Base URL: https://api.dexscreener.com/latest\n * No API key needed. Rate limit: 300 req/min.\n */\n\nconst BASE_URL = 'https://api.dexscreener.com/latest';\n\nexport interface DexPair {\n chainId: string;\n dexId: string;\n pairAddress: string;\n baseToken: { address: string; name: string; symbol: string };\n quoteToken: { address: string; name: string; symbol: string };\n priceUsd: string;\n priceChange: { h1: number; h6: number; h24: number };\n volume: { h1: number; h6: number; h24: number };\n liquidity: { usd: number };\n fdv: number;\n txns: {\n h1: { buys: number; sells: number };\n h24: { buys: number; sells: number };\n };\n}\n\ninterface DexSearchResponse {\n pairs: DexPair[] | null;\n}\n\ninterface DexPairsResponse {\n pairs: DexPair[] | null;\n pair?: DexPair;\n}\n\n// Module-level throttle shared across all instances to enforce rate limit\nlet sharedLastCallTime = 0;\nconst SHARED_MIN_INTERVAL = 200; // 200ms = 300 req/min\n\nexport class DexScreenerProvider {\n private async throttle(): Promise<void> {\n const now = Date.now();\n const elapsed = now - sharedLastCallTime;\n if (elapsed < SHARED_MIN_INTERVAL) {\n await new Promise((resolve) => setTimeout(resolve, SHARED_MIN_INTERVAL - elapsed));\n }\n sharedLastCallTime = Date.now();\n }\n\n private async fetchJson<T>(url: string): Promise<T> {\n await this.throttle();\n const res = await fetch(url);\n if (!res.ok) {\n throw new Error(`DEXScreener API error: ${res.status} ${res.statusText} — ${url}`);\n }\n return res.json() as Promise<T>;\n }\n\n /** Search for token pairs by query string (token name, symbol, or address). */\n async searchPairs(query: string): Promise<DexPair[]> {\n const encoded = encodeURIComponent(query);\n const data = await this.fetchJson<DexSearchResponse>(\n `${BASE_URL}/dex/search/?q=${encoded}`,\n );\n return data.pairs ?? [];\n }\n\n /** Get pair data by chain and pair address. */\n async getPair(chain: string, pairAddress: string): Promise<DexPair> {\n const data = await this.fetchJson<DexPairsResponse>(\n `${BASE_URL}/dex/pairs/${encodeURIComponent(chain)}/${encodeURIComponent(pairAddress)}`,\n );\n if (data.pair) return data.pair;\n if (data.pairs && data.pairs.length > 0) return data.pairs[0]!;\n throw new Error(`No pair found: ${chain}/${pairAddress}`);\n }\n\n /** Get all DEX pairs for a token address (across all chains). */\n async getTokenPairs(tokenAddress: string): Promise<DexPair[]> {\n const data = await this.fetchJson<DexPairsResponse>(\n `${BASE_URL}/dex/tokens/${encodeURIComponent(tokenAddress)}`,\n );\n return data.pairs ?? [];\n }\n\n /** Get trending/top pairs by searching for common tokens. */\n async getTrending(): Promise<DexPair[]> {\n // DEXScreener doesn't have a dedicated trending endpoint in the free API,\n // so we search for well-known tokens and sort by volume\n const results = await this.searchPairs('WETH');\n return results\n .filter((p) => p.volume?.h24 > 0)\n .sort((a, b) => (b.volume?.h24 ?? 0) - (a.volume?.h24 ?? 0))\n .slice(0, 20);\n }\n}\n","/**\n * Hyperliquid Flow Strategy\n * Combines exchange-native signals for maximum alpha:\n * - Funding rate arbitrage\n * - OI + price divergence\n * - Order book imbalance\n * - Whale trade flow\n */\n\nimport type { Signal } from '../scoring.js';\nimport type { Strategy, StrategyContext } from './types.js';\nimport { clamp } from '../utils.js';\n\nexport class HyperliquidFlowStrategy implements Strategy {\n name = 'hyperliquidFlow';\n description = 'Exchange-native flow analysis using Hyperliquid funding, OI, order book, and whale trades';\n requiredData = ['hyperliquidData'];\n\n async analyze(ctx: StrategyContext): Promise<Signal> {\n const hlData = ctx.hyperliquidData;\n\n if (!hlData) {\n return {\n name: this.name,\n value: 0.0,\n confidence: 0.0,\n source: 'Hyperliquid Flow',\n details: 'No Hyperliquid data available',\n };\n }\n\n const signals = [\n this.analyzeFundingRate(hlData.fundingRate),\n this.analyzeOIPriceDivergence(hlData.oiChangePct, hlData.markPrice, hlData.prevDayPrice),\n this.analyzeOrderBookImbalance(hlData.orderBookImbalance),\n this.analyzeWhaleFlow(hlData.largeTradesBias),\n ];\n\n // Combine signals with weights\n const weights = [0.25, 0.30, 0.25, 0.20]; // funding, OI+price, orderbook, whales\n let totalValue = 0;\n let totalWeight = 0;\n const details: string[] = [];\n\n for (let i = 0; i < signals.length; i++) {\n const signal = signals[i]!;\n const weight = weights[i]!;\n totalValue += signal.value * weight;\n totalWeight += weight;\n if (signal.details) details.push(signal.details);\n }\n\n const value = totalValue / totalWeight;\n\n // Calculate confidence\n let confidence = 0.6; // Base confidence (exchange-native data)\n if (hlData.volume24h > 100_000_000) confidence += 0.1; // High liquidity\n\n // Boost confidence if multiple signals agree\n const positiveSignals = signals.filter(s => s.value > 0.2).length;\n const negativeSignals = signals.filter(s => s.value < -0.2).length;\n if (positiveSignals >= 3 || negativeSignals >= 3) confidence += 0.1;\n\n return {\n name: this.name,\n value: clamp(value),\n confidence: Math.min(1.0, confidence),\n source: 'Hyperliquid Flow',\n details: details.join(' | '),\n };\n }\n\n /** Analyze funding rate for mean-reversion opportunities. */\n private analyzeFundingRate(rate: number): { value: number; details: string } {\n const ratePct = (rate * 100).toFixed(4);\n\n if (rate > 0.001) {\n // >0.1% funding: strong bearish (overleveraged longs)\n return {\n value: -0.8,\n details: `High positive funding ${ratePct}%: overleveraged longs → bearish`,\n };\n } else if (rate > 0.0003) {\n // >0.03% funding: bearish\n return {\n value: -0.6,\n details: `Elevated positive funding ${ratePct}%: longs paying → bearish`,\n };\n } else if (rate < -0.001) {\n // <-0.1% funding: strong bullish (overleveraged shorts)\n return {\n value: 0.8,\n details: `High negative funding ${ratePct}%: overleveraged shorts → bullish`,\n };\n } else if (rate < -0.0003) {\n // <-0.03% funding: bullish\n return {\n value: 0.6,\n details: `Elevated negative funding ${ratePct}%: shorts paying → bullish`,\n };\n } else {\n // Neutral funding\n return {\n value: 0.0,\n details: `Neutral funding ${ratePct}%: no directional signal`,\n };\n }\n }\n\n /** Analyze OI + price divergence patterns. */\n private analyzeOIPriceDivergence(oiChangePct: number, markPrice: number, prevDayPrice: number): { value: number; details: string } {\n const priceChange = ((markPrice - prevDayPrice) / prevDayPrice) * 100;\n const oiRising = oiChangePct > 1; // >1% OI increase\n const oiFalling = oiChangePct < -1; // >1% OI decrease\n const priceRising = priceChange > 1; // >1% price increase\n const priceFalling = priceChange < -1; // >1% price decrease\n\n if (oiRising && priceRising) {\n return {\n value: 0.6,\n details: `OI+${oiChangePct.toFixed(1)}% Price+${priceChange.toFixed(1)}%: strong trend → bullish`,\n };\n } else if (oiRising && priceFalling) {\n return {\n value: 0.3,\n details: `OI+${oiChangePct.toFixed(1)}% Price${priceChange.toFixed(1)}%: shorts building → potential squeeze`,\n };\n } else if (oiFalling && priceRising) {\n return {\n value: -0.3,\n details: `OI${oiChangePct.toFixed(1)}% Price+${priceChange.toFixed(1)}%: weak rally → potential trap`,\n };\n } else if (oiFalling && priceFalling) {\n return {\n value: 0.2,\n details: `OI${oiChangePct.toFixed(1)}% Price${priceChange.toFixed(1)}%: capitulation → potential bottom`,\n };\n } else {\n return {\n value: 0.0,\n details: `OI${oiChangePct.toFixed(1)}% Price${priceChange.toFixed(1)}%: no clear divergence`,\n };\n }\n }\n\n /** Analyze order book imbalance. */\n private analyzeOrderBookImbalance(imbalance: number): { value: number; details: string } {\n const imbalancePct = (imbalance * 100).toFixed(1);\n\n if (imbalance > 0.5) {\n return {\n value: 0.8,\n details: `Very heavy bids (${imbalancePct}%): strong demand → bullish`,\n };\n } else if (imbalance > 0.3) {\n return {\n value: 0.5,\n details: `Heavy bids (${imbalancePct}%): demand pressure → bullish`,\n };\n } else if (imbalance < -0.5) {\n return {\n value: -0.8,\n details: `Very heavy asks (${imbalancePct}%): strong supply → bearish`,\n };\n } else if (imbalance < -0.3) {\n return {\n value: -0.5,\n details: `Heavy asks (${imbalancePct}%): supply pressure → bearish`,\n };\n } else {\n return {\n value: 0.0,\n details: `Balanced orderbook (${imbalancePct}%): no directional bias`,\n };\n }\n }\n\n /** Analyze whale trade flow direction. */\n private analyzeWhaleFlow(bias: number): { value: number; details: string } {\n const biasPct = (bias * 100).toFixed(1);\n\n if (bias > 0.3) {\n return {\n value: 0.4,\n details: `Whales buying (${biasPct}%): smart money accumulation → bullish`,\n };\n } else if (bias < -0.3) {\n return {\n value: -0.4,\n details: `Whales selling (${biasPct}%): smart money distribution → bearish`,\n };\n } else {\n return {\n value: bias, // Scale linearly for smaller values\n details: `Whale flow (${biasPct}%): moderate directional bias`,\n };\n }\n }\n}","/**\n * Multi-Timeframe Confluence Strategy\n * Analyzes the same token across multiple timeframes for signal confluence.\n * Strong signals occur when multiple timeframes agree on direction.\n */\n\nimport type { Signal } from '../scoring.js';\nimport type { Strategy, StrategyContext, Candle } from './types.js';\nimport { calculateEMA, calculateRSI } from '../technical.js';\nimport { clamp } from '../utils.js';\n\ninterface TimeframeAnalysis {\n timeframe: string;\n trend: number; // +1 bullish, 0 neutral, -1 bearish\n momentum: number; // +1 bullish, 0 neutral, -1 bearish\n structure: number; // +1 bullish, 0 neutral, -1 bearish\n score: number; // combined score\n}\n\nexport class MultiTimeframeStrategy implements Strategy {\n name = 'multiTimeframe';\n description = 'Multi-timeframe confluence analysis using daily, weekly, and short-term data';\n requiredData = ['candles'];\n\n async analyze(ctx: StrategyContext): Promise<Signal> {\n if (!ctx.candles || ctx.candles.length < 30) {\n return {\n name: this.name,\n value: 0.0,\n confidence: 0.1,\n source: 'Multi-Timeframe',\n details: 'Insufficient candle data for multi-timeframe analysis (need 30+ daily candles)',\n };\n }\n\n const details: string[] = [];\n let value = 0;\n let confidence = 0.4;\n\n try {\n // Prepare timeframe data\n const dailyCandles = ctx.candles;\n const weeklyCandles = this.aggregateWeeklyCandles(dailyCandles);\n const shortTermCandles = dailyCandles.slice(-10); // Last 10 days as short-term proxy\n\n // Analyze each timeframe with appropriate periods\n const shortTermAnalysis = this.analyzeTimeframe(shortTermCandles, 'Short-term', 5);\n const dailyAnalysis = this.analyzeTimeframe(dailyCandles, 'Daily', 20);\n const weeklyAnalysis = this.analyzeTimeframe(weeklyCandles, 'Weekly', 5); // Reduced from 10\n\n const analyses = [shortTermAnalysis, dailyAnalysis, weeklyAnalysis];\n\n // Calculate confluence score\n const bullishCount = analyses.filter(a => a.score > 0).length;\n const bearishCount = analyses.filter(a => a.score < 0).length;\n const neutralCount = analyses.filter(a => a.score === 0).length;\n\n // Confluence scoring - adjusted for more realistic signals\n if (bullishCount === 3) {\n value = 0.8;\n confidence += 0.2;\n details.push('All timeframes bullish (strong confluence)');\n } else if (bearishCount === 3) {\n value = -0.8;\n confidence += 0.2;\n details.push('All timeframes bearish (strong confluence)');\n } else if (bullishCount === 2) {\n value = 0.4;\n confidence += 0.1;\n details.push('2 of 3 timeframes bullish');\n } else if (bearishCount === 2) {\n value = -0.4;\n confidence += 0.1;\n details.push('2 of 3 timeframes bearish');\n } else if (bullishCount > bearishCount) {\n value = 0.2;\n details.push('Majority bullish (weak confluence)');\n } else if (bearishCount > bullishCount) {\n value = -0.2;\n details.push('Majority bearish (weak confluence)');\n } else {\n value = 0.0;\n details.push('Mixed timeframe signals');\n }\n\n // Add timeframe breakdown details\n for (const analysis of analyses) {\n const score = analysis.score > 0 ? 'bullish' : analysis.score < 0 ? 'bearish' : 'neutral';\n details.push(`${analysis.timeframe}: ${score} (trend:${analysis.trend} momentum:${analysis.momentum} structure:${analysis.structure})`);\n }\n\n // Data quality factor\n const dataQualityFactor = Math.min(dailyCandles.length / 50, 1.0); // Full confidence at 50+ days\n confidence *= dataQualityFactor;\n\n if (dataQualityFactor < 1.0) {\n details.push(`Data quality: ${(dataQualityFactor * 100).toFixed(0)}% (${dailyCandles.length} days)`);\n }\n\n } catch (error) {\n return {\n name: this.name,\n value: 0.0,\n confidence: 0.1,\n source: 'Multi-Timeframe',\n details: `Analysis error: ${(error as Error).message}`,\n };\n }\n\n return {\n name: this.name,\n value: clamp(value),\n confidence: Math.min(Math.max(confidence, 0.1), 1.0),\n source: 'Multi-Timeframe',\n details: details.join('; '),\n };\n }\n\n private aggregateWeeklyCandles(dailyCandles: Candle[]): Candle[] {\n if (dailyCandles.length < 7) return [];\n\n const weeklyCandles: Candle[] = [];\n const sortedCandles = [...dailyCandles].sort((a, b) => a.timestamp - b.timestamp);\n\n // Group by ISO week\n const weekGroups = new Map<string, Candle[]>();\n\n for (const candle of sortedCandles) {\n const date = new Date(candle.timestamp);\n const year = date.getFullYear();\n const week = this.getISOWeek(date);\n const weekKey = `${year}-W${week.toString().padStart(2, '0')}`;\n\n if (!weekGroups.has(weekKey)) {\n weekGroups.set(weekKey, []);\n }\n weekGroups.get(weekKey)!.push(candle);\n }\n\n // Aggregate each week\n for (const [weekKey, candles] of weekGroups) {\n if (candles.length === 0) continue;\n\n const open = candles[0]!.open;\n const close = candles[candles.length - 1]!.close;\n const high = Math.max(...candles.map(c => c.high));\n const low = Math.min(...candles.map(c => c.low));\n const volume = candles.reduce((sum, c) => sum + c.volume, 0);\n const timestamp = candles[0]!.timestamp;\n\n weeklyCandles.push({ timestamp, open, high, low, close, volume });\n }\n\n return weeklyCandles.sort((a, b) => a.timestamp - b.timestamp);\n }\n\n private getISOWeek(date: Date): number {\n const tempDate = new Date(date.valueOf());\n const dayNumber = (date.getDay() + 6) % 7;\n tempDate.setDate(tempDate.getDate() - dayNumber + 3);\n const firstThursday = tempDate.valueOf();\n tempDate.setMonth(0, 1);\n if (tempDate.getDay() !== 4) {\n tempDate.setMonth(0, 1 + ((4 - tempDate.getDay()) + 7) % 7);\n }\n return 1 + Math.ceil((firstThursday - tempDate.valueOf()) / 604800000);\n }\n\n private analyzeTimeframe(candles: Candle[], label: string, emaPeriod: number): TimeframeAnalysis {\n if (candles.length < Math.max(emaPeriod * 0.7, 10)) { // More lenient requirement\n return {\n timeframe: label,\n trend: 0,\n momentum: 0,\n structure: 0,\n score: 0,\n };\n }\n\n // 1. Trend: price vs EMA\n const closes = candles.map(c => c.close);\n const emaValues = calculateEMA(closes, emaPeriod);\n const currentPrice = closes[closes.length - 1]!;\n const currentEMA = emaValues[emaValues.length - 1];\n\n let trend = 0;\n if (!isNaN(currentEMA!)) {\n trend = currentPrice > currentEMA! ? 1 : currentPrice < currentEMA! ? -1 : 0;\n }\n\n // 2. Momentum: RSI zones\n const rsiValues = calculateRSI(candles, 14);\n const currentRSI = rsiValues[rsiValues.length - 1];\n\n let momentum = 0;\n if (!isNaN(currentRSI!)) {\n if (currentRSI! > 60) momentum = 1;\n else if (currentRSI! < 40) momentum = -1;\n else momentum = 0;\n }\n\n // 3. Structure: last 3 candles pattern\n let structure = 0;\n if (candles.length >= 3) {\n const last3 = candles.slice(-3);\n const highs = last3.map(c => c.high);\n const lows = last3.map(c => c.low);\n\n const higherHighs = highs[1] > highs[0] && highs[2] > highs[1];\n const higherLows = lows[1] > lows[0] && lows[2] > lows[1];\n const lowerHighs = highs[1] < highs[0] && highs[2] < highs[1];\n const lowerLows = lows[1] < lows[0] && lows[2] < lows[1];\n\n if (higherHighs && higherLows) structure = 1;\n else if (lowerHighs && lowerLows) structure = -1;\n else structure = 0;\n }\n\n // Combine into score\n const score = trend + momentum + structure;\n\n return {\n timeframe: label,\n trend,\n momentum,\n structure,\n score: Math.sign(score) // Convert to -1, 0, or 1\n };\n }\n}","/**\n * Cross-Sectional Momentum Strategy\n *\n * Based on Kakushadze & Serur \"151 Trading Strategies\" §10.3, adapted for\n * crypto perpetuals. Ranks tokens by their 7-day return RELATIVE to the\n * group mean, expressed as a z-score. In flat markets where absolute signals\n * are muted, the token outperforming (or underperforming) the group still\n * produces a directional signal.\n *\n * Uses momentum (not contrarian) weighting — crypto trends persist:\n * z_i = (R_i - R_m) / σ_R\n * signal = clamp(z_i * 0.3, -1, 1)\n */\n\nimport type { Signal } from '../scoring.js';\nimport type { Strategy, StrategyContext } from './types.js';\nimport { clamp } from '../utils.js';\n\n/** Minimum number of tokens in the group to produce a meaningful z-score. */\nconst MIN_GROUP_SIZE = 3;\n\n/** Scaling factor: z=1 → signal value 0.30. */\nconst Z_SCALE = 0.3;\n\n/** Below this stdev the group is effectively flat — no meaningful dispersion. */\nconst MIN_STDEV = 1e-10;\n\nexport class CrossSectionalMomentumStrategy implements Strategy {\n name = 'crossSectionalMomentum';\n description = 'Ranks tokens by relative 7-day performance vs group (§10.3 Kakushadze)';\n requiredData = ['groupReturns'];\n\n async analyze(ctx: StrategyContext): Promise<Signal> {\n const returns = ctx.groupReturns;\n if (!returns || Object.keys(returns).length < MIN_GROUP_SIZE) {\n return {\n name: this.name,\n value: 0,\n confidence: 0.1,\n source: 'Cross-Sectional',\n details: 'Insufficient group data',\n };\n }\n\n const myReturn = returns[ctx.tokenId];\n if (myReturn === undefined) {\n return {\n name: this.name,\n value: 0,\n confidence: 0.1,\n source: 'Cross-Sectional',\n details: 'Token not in group',\n };\n }\n\n const vals = Object.values(returns);\n const mean = vals.reduce((s, v) => s + v, 0) / vals.length;\n const variance = vals.reduce((s, v) => s + (v - mean) ** 2, 0) / vals.length;\n const stdev = Math.sqrt(variance);\n\n if (stdev < MIN_STDEV) {\n return {\n name: this.name,\n value: 0,\n confidence: 0.1,\n source: 'Cross-Sectional',\n details: 'Zero group dispersion',\n };\n }\n\n const zScore = (myReturn - mean) / stdev;\n const value = clamp(zScore * Z_SCALE);\n const confidence = clamp(0.3 + Math.abs(zScore) * 0.2, 0.1, 0.9);\n\n return {\n name: this.name,\n value,\n confidence,\n source: 'Cross-Sectional Momentum',\n details: `7d return ${(myReturn * 100).toFixed(1)}% vs group mean ${(mean * 100).toFixed(1)}% (z=${zScore.toFixed(2)})`,\n };\n }\n}\n","/**\n * TradingView MCP data provider — spawns a local TradingView MCP server\n * (JSON-RPC over stdio) and exposes coin analysis + multi-timeframe alignment.\n *\n * The MCP server is spawned once on first call and kept alive for the process\n * lifetime. If it dies, it is respawned on the next call.\n *\n * Results are cached for 5 minutes per (symbol, tool, timeframe) key.\n */\n\nimport { spawn, type ChildProcess } from 'node:child_process';\nimport { createInterface, type Interface as ReadlineInterface } from 'node:readline';\n\n// ---------------------------------------------------------------------------\n// Symbol mapping — CoinGecko token IDs → TradingView BINANCE symbols\n// ---------------------------------------------------------------------------\n\nconst TV_SYMBOLS: Record<string, string> = {\n bitcoin: 'BTCUSDT',\n ethereum: 'ETHUSDT',\n solana: 'SOLUSDT',\n arbitrum: 'ARBUSDT',\n chainlink: 'LINKUSDT',\n aave: 'AAVEUSDT',\n uniswap: 'UNIUSDT',\n dogecoin: 'DOGEUSDT',\n avalanche: 'AVAXUSDT',\n 'avalanche-2': 'AVAXUSDT',\n near: 'NEARUSDT',\n sui: 'SUIUSDT',\n aptos: 'APTUSDT',\n injective: 'INJUSDT',\n pendle: 'PENDLEUSDT',\n pepe: 'PEPEUSDT',\n polygon: 'MATICUSDT',\n optimism: 'OPUSDT',\n litecoin: 'LTCUSDT',\n cosmos: 'ATOMUSDT',\n filecoin: 'FILUSDT',\n maker: 'MKRUSDT',\n cardano: 'ADAUSDT',\n polkadot: 'DOTUSDT',\n render: 'RENDERUSDT',\n jupiter: 'JUPUSDT',\n hyperliquid: 'HYPEUSDT',\n ethena: 'ENAUSDT',\n zcash: 'ZECUSDT',\n ripple: 'XRPUSDT',\n bittensor: 'TAOUSDT',\n fartcoin: 'FARTCOINUSDT',\n binancecoin: 'BNBUSDT',\n blur: 'BLURUSDT',\n 'worldcoin-wld': 'WLDUSDT',\n 'pudgy-penguins': 'PENGUUSDT',\n 'fetch-ai': 'FETUSDT',\n};\n\nconst DEFAULT_EXCHANGE = 'BINANCE';\nconst MCP_BIN = '/home/ana/.local/tradingview-mcp/bin/tradingview-mcp';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface TVAnalysis {\n symbol: string;\n timeframe: string;\n /** Overall recommendation: -1 (Strong Sell) to +1 (Strong Buy). */\n recommendAll: number;\n /** Buy/sell signal string from market_sentiment. */\n buySellSignal: string;\n /** Number of buy indicators. */\n buyCount: number;\n /** Number of sell indicators. */\n sellCount: number;\n /** Number of neutral indicators. */\n neutralCount: number;\n /** Total indicator count. */\n totalIndicators: number;\n /** Raw response payload for debugging. */\n raw: any;\n}\n\nexport interface TVAlignment {\n symbol: string;\n /** Per-timeframe recommendation (-1 to +1). */\n timeframes: Record<string, number>;\n /** Number of timeframes agreeing on direction. */\n agreeing: number;\n /** Total timeframes analysed. */\n total: number;\n raw: any;\n}\n\n// ---------------------------------------------------------------------------\n// Cache\n// ---------------------------------------------------------------------------\n\ninterface CacheEntry<T> {\n ts: number;\n data: T;\n}\n\nconst CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes\nconst cache = new Map<string, CacheEntry<any>>();\n\nfunction cacheGet<T>(key: string): T | null {\n const entry = cache.get(key);\n if (!entry) return null;\n if (Date.now() - entry.ts > CACHE_TTL_MS) {\n cache.delete(key);\n return null;\n }\n return entry.data as T;\n}\n\nfunction cacheSet<T>(key: string, data: T): void {\n cache.set(key, { ts: Date.now(), data });\n}\n\n// ---------------------------------------------------------------------------\n// MCP process management (singleton)\n// ---------------------------------------------------------------------------\n\nlet mcpProcess: ChildProcess | null = null;\nlet mcpReadline: ReadlineInterface | null = null;\nlet mcpReady = false;\nlet nextId = 1;\n\n/** Pending JSON-RPC responses keyed by request id. */\nconst pending = new Map<number, {\n resolve: (value: any) => void;\n reject: (reason: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n}>();\n\nconst RPC_TIMEOUT_MS = 15_000;\n\nfunction ensureProcess(): ChildProcess {\n if (mcpProcess && !mcpProcess.killed && mcpProcess.exitCode === null) {\n return mcpProcess;\n }\n\n // Reset state on respawn\n mcpReady = false;\n for (const [, p] of pending) {\n clearTimeout(p.timer);\n p.reject(new Error('MCP process died — respawning'));\n }\n pending.clear();\n\n mcpProcess = spawn(MCP_BIN, [], {\n stdio: ['pipe', 'pipe', 'pipe'],\n env: { ...process.env },\n });\n\n mcpReadline = createInterface({ input: mcpProcess.stdout! });\n mcpReadline.on('line', (line: string) => {\n try {\n const msg = JSON.parse(line);\n const id = msg.id;\n if (id !== undefined && pending.has(id)) {\n const p = pending.get(id)!;\n clearTimeout(p.timer);\n pending.delete(id);\n if (msg.error) {\n p.reject(new Error(`MCP error ${msg.error.code}: ${msg.error.message}`));\n } else {\n p.resolve(msg.result);\n }\n }\n } catch {\n // Non-JSON line or notification — ignore\n }\n });\n\n mcpProcess.on('exit', () => {\n mcpProcess = null;\n mcpReady = false;\n });\n\n mcpProcess.stderr?.on('data', () => {\n // Swallow stderr to prevent backpressure\n });\n\n return mcpProcess;\n}\n\nfunction rpcSend(method: string, params: any): Promise<any> {\n const proc = ensureProcess();\n const id = nextId++;\n const msg = JSON.stringify({ jsonrpc: '2.0', id, method, params }) + '\\n';\n\n return new Promise<any>((resolve, reject) => {\n const timer = setTimeout(() => {\n pending.delete(id);\n reject(new Error(`MCP RPC timeout for ${method} (${RPC_TIMEOUT_MS}ms)`));\n }, RPC_TIMEOUT_MS);\n pending.set(id, { resolve, reject, timer });\n\n proc.stdin!.write(msg, (err) => {\n if (err) {\n clearTimeout(timer);\n pending.delete(id);\n reject(err);\n }\n });\n });\n}\n\nasync function ensureInitialized(): Promise<void> {\n if (mcpReady) return;\n\n ensureProcess();\n\n // Send initialize\n await rpcSend('initialize', {\n protocolVersion: '2024-11-05',\n capabilities: {},\n clientInfo: { name: 'sherwood', version: '1.0' },\n });\n\n // Send initialized notification (no id, fire-and-forget)\n const proc = ensureProcess();\n proc.stdin!.write(\n JSON.stringify({ jsonrpc: '2.0', method: 'notifications/initialized', params: {} }) + '\\n',\n );\n\n mcpReady = true;\n}\n\nasync function callTool(name: string, args: Record<string, any>): Promise<any> {\n await ensureInitialized();\n const result = await rpcSend('tools/call', { name, arguments: args });\n // MCP tool results come as { content: [{ type: \"text\", text: \"...\" }] }\n if (result?.content?.[0]?.text) {\n try {\n return JSON.parse(result.content[0].text);\n } catch {\n return result.content[0].text;\n }\n }\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Map a buy/sell signal string to a numeric value (-1 to +1).\n */\nfunction signalToValue(signal: string): number {\n const s = (signal || '').toLowerCase().trim();\n if (s === 'strong buy') return 1.0;\n if (s === 'buy') return 0.5;\n if (s === 'neutral') return 0.0;\n if (s === 'sell') return -0.5;\n if (s === 'strong sell') return -1.0;\n return 0.0;\n}\n\n/**\n * Get coin analysis from TradingView MCP.\n * Returns null if the token is unmapped or the MCP call fails.\n */\nexport async function getCoinAnalysis(\n tokenId: string,\n timeframe: string = '4h',\n): Promise<TVAnalysis | null> {\n const symbol = TV_SYMBOLS[tokenId];\n if (!symbol) return null;\n\n const cacheKey = `tv:coin:${symbol}:${timeframe}`;\n const cached = cacheGet<TVAnalysis>(cacheKey);\n if (cached) return cached;\n\n try {\n const raw = await callTool('coin_analysis', {\n symbol,\n exchange: DEFAULT_EXCHANGE,\n timeframe,\n });\n\n // Parse the response — structure varies, extract what we need\n const recommendAll = typeof raw?.recommend_all === 'number'\n ? raw.recommend_all\n : typeof raw?.summary?.RECOMMENDATION === 'string'\n ? signalToValue(raw.summary.RECOMMENDATION)\n : 0;\n\n const buySellSignal: string =\n raw?.market_sentiment?.buy_sell_signal\n ?? raw?.summary?.RECOMMENDATION\n ?? 'Neutral';\n\n const buyCount = raw?.summary?.BUY ?? raw?.buy_count ?? 0;\n const sellCount = raw?.summary?.SELL ?? raw?.sell_count ?? 0;\n const neutralCount = raw?.summary?.NEUTRAL ?? raw?.neutral_count ?? 0;\n\n const result: TVAnalysis = {\n symbol,\n timeframe,\n recommendAll: Math.max(-1, Math.min(1, recommendAll)),\n buySellSignal,\n buyCount,\n sellCount,\n neutralCount,\n totalIndicators: buyCount + sellCount + neutralCount,\n raw,\n };\n\n cacheSet(cacheKey, result);\n return result;\n } catch (err) {\n console.error(`TradingView MCP coin_analysis failed for ${symbol}: ${(err as Error).message}`);\n return null;\n }\n}\n\n/**\n * Get multi-timeframe alignment from TradingView MCP.\n * Returns null if the token is unmapped or the MCP call fails.\n */\nexport async function getMultiTimeframeAlignment(\n tokenId: string,\n): Promise<TVAlignment | null> {\n const symbol = TV_SYMBOLS[tokenId];\n if (!symbol) return null;\n\n const cacheKey = `tv:mtf:${symbol}`;\n const cached = cacheGet<TVAlignment>(cacheKey);\n if (cached) return cached;\n\n try {\n const raw = await callTool('multi_timeframe_analysis', {\n symbol,\n exchange: DEFAULT_EXCHANGE,\n });\n\n // Parse per-timeframe recommendations\n const timeframes: Record<string, number> = {};\n const tfKeys = ['15m', '1h', '4h', 'daily', 'weekly'];\n\n for (const tf of tfKeys) {\n const tfData = raw?.[tf] ?? raw?.timeframes?.[tf];\n if (tfData) {\n const rec =\n typeof tfData.recommend_all === 'number'\n ? tfData.recommend_all\n : typeof tfData.RECOMMENDATION === 'string'\n ? signalToValue(tfData.RECOMMENDATION)\n : typeof tfData.recommendation === 'string'\n ? signalToValue(tfData.recommendation)\n : null;\n if (rec !== null) {\n timeframes[tf] = Math.max(-1, Math.min(1, rec));\n }\n }\n }\n\n const values = Object.values(timeframes);\n const majoritySign = values.reduce((s, v) => s + Math.sign(v), 0);\n const direction = majoritySign > 0 ? 1 : majoritySign < 0 ? -1 : 0;\n const agreeing = values.filter((v) => Math.sign(v) === direction).length;\n\n const result: TVAlignment = {\n symbol,\n timeframes,\n agreeing,\n total: values.length,\n raw,\n };\n\n cacheSet(cacheKey, result);\n return result;\n } catch (err) {\n console.error(`TradingView MCP multi_timeframe failed for ${symbol}: ${(err as Error).message}`);\n return null;\n }\n}\n\n/**\n * Check if the TradingView MCP binary exists.\n */\nexport function isTradingViewAvailable(): boolean {\n try {\n require('node:fs').accessSync(MCP_BIN, require('node:fs').constants.X_OK);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Gracefully shut down the MCP process (call on agent exit).\n */\nexport function shutdownTradingView(): void {\n if (mcpProcess && !mcpProcess.killed) {\n mcpProcess.kill('SIGTERM');\n mcpProcess = null;\n mcpReady = false;\n }\n}\n","/**\n * TradingView Signal Strategy\n *\n * Consumes coin analysis from the local TradingView MCP server and converts\n * it into a directional signal (-1 to +1). Falls back to a zero-confidence\n * neutral signal when the MCP server is unavailable or the token is unmapped.\n */\n\nimport type { Signal } from '../scoring.js';\nimport type { Strategy, StrategyContext } from './types.js';\nimport { clamp } from '../utils.js';\nimport { getCoinAnalysis, type TVAnalysis } from '../../providers/data/tradingview.js';\n\nexport class TradingViewSignalStrategy implements Strategy {\n name = 'tradingviewSignal';\n description = 'TradingView technical indicators via local MCP server (4h timeframe)';\n requiredData = [] as string[]; // No StrategyContext fields needed — uses own provider\n\n async analyze(ctx: StrategyContext): Promise<Signal> {\n const analysis = await getCoinAnalysis(ctx.tokenId, '4h');\n\n if (!analysis) {\n return {\n name: this.name,\n value: 0.0,\n confidence: 0.0,\n source: 'TradingView MCP',\n details: 'TV MCP unavailable or token unmapped',\n };\n }\n\n return this.buildSignal(analysis);\n }\n\n private buildSignal(tv: TVAnalysis): Signal {\n // Primary value: Recommend.All (-1 to +1) from TradingView summary\n let value = tv.recommendAll;\n\n // Fallback: derive from buy_sell_signal string if recommendAll is zero\n if (value === 0 && tv.buySellSignal.toLowerCase() !== 'neutral') {\n value = signalStringToValue(tv.buySellSignal);\n }\n\n // Confidence: proportion of indicators that agree with the direction\n let confidence = 0.5; // baseline\n if (tv.totalIndicators > 0) {\n const agreeingCount =\n value > 0\n ? tv.buyCount\n : value < 0\n ? tv.sellCount\n : tv.neutralCount;\n confidence = agreeingCount / tv.totalIndicators;\n }\n\n // Boost confidence when signal is extreme (Strong Buy / Strong Sell)\n const absValue = Math.abs(value);\n if (absValue >= 0.8) {\n confidence = Math.min(confidence + 0.1, 1.0);\n }\n\n const details = [\n `TV ${tv.timeframe}: ${tv.buySellSignal}`,\n `Recommend.All=${tv.recommendAll.toFixed(3)}`,\n `buy=${tv.buyCount} sell=${tv.sellCount} neutral=${tv.neutralCount}`,\n ].join('; ');\n\n return {\n name: this.name,\n value: clamp(value),\n confidence: Math.max(0.1, Math.min(confidence, 1.0)),\n source: 'TradingView MCP',\n details,\n };\n }\n}\n\n/**\n * Map a TV buy/sell signal label to a numeric value.\n */\nfunction signalStringToValue(signal: string): number {\n const s = signal.toLowerCase().trim();\n if (s === 'strong buy') return 1.0;\n if (s === 'buy') return 0.5;\n if (s === 'neutral') return 0.0;\n if (s === 'sell') return -0.5;\n if (s === 'strong sell') return -1.0;\n return 0.0;\n}\n","/**\n * BTC Network Health Strategy\n *\n * Uses Blockchain.com network data (via Fincept) to gauge Bitcoin network\n * health: transaction count, miner revenue, and mempool demand.\n * Only fires for bitcoin — returns zero-confidence for other tokens.\n */\n\nimport type { Signal } from '../scoring.js';\nimport type { Strategy, StrategyContext } from './types.js';\nimport { clamp } from '../utils.js';\n\nexport class BtcNetworkHealthStrategy implements Strategy {\n name = 'btcNetworkHealth';\n description = 'Analyzes BTC network health — tx count, miner revenue, mempool demand';\n requiredData = ['btcNetworkData'];\n\n async analyze(ctx: StrategyContext): Promise<Signal> {\n if (ctx.tokenId !== 'bitcoin') {\n return {\n name: this.name,\n value: 0,\n confidence: 0,\n source: 'BTC Network Health',\n details: 'Not bitcoin — skipped',\n };\n }\n\n if (!ctx.btcNetworkData) {\n return {\n name: this.name,\n value: 0,\n confidence: 0.1,\n source: 'BTC Network Health',\n details: 'No BTC network data available',\n };\n }\n\n const { transactionCount, minerRevenueBtc, mempoolSize } = ctx.btcNetworkData;\n const details: string[] = [];\n let value = 0;\n\n // Transaction count signals\n if (transactionCount > 300_000) {\n value += 0.2;\n details.push(`active network (${(transactionCount / 1000).toFixed(0)}k txs)`);\n } else if (transactionCount < 200_000) {\n value -= 0.1;\n details.push(`low activity (${(transactionCount / 1000).toFixed(0)}k txs)`);\n }\n\n // Miner revenue signals\n if (minerRevenueBtc > 1000) {\n value += 0.15;\n details.push(`healthy miners (${minerRevenueBtc.toFixed(0)} BTC revenue)`);\n }\n\n // Mempool demand signals\n if (mempoolSize > 100_000) {\n value += 0.1;\n details.push(`high demand (${(mempoolSize / 1000).toFixed(0)}k mempool)`);\n }\n\n const confidence = details.length > 0 ? 0.5 : 0.2;\n\n return {\n name: this.name,\n value: clamp(value),\n confidence,\n source: 'BTC Network Health',\n details: details.length > 0 ? details.join('; ') : 'Network metrics within normal range',\n };\n }\n}\n","/**\n * Prediction Market Strategy\n *\n * Uses Polymarket/Manifold prediction market probabilities to derive\n * directional signals. High-conviction markets (>75% or <25%) for\n * bullish/bearish crypto events contribute to the score.\n */\n\nimport type { Signal } from \"../scoring.js\";\nimport type { Strategy, StrategyContext } from \"./types.js\";\nimport { clamp } from \"../utils.js\";\n\nconst BULLISH_KEYWORDS = [\"approve\", \"etf\", \"cut\", \"ease\", \"adopt\", \"pass\", \"bull\"];\nconst BEARISH_KEYWORDS = [\"ban\", \"restrict\", \"crash\", \"reject\", \"hike\", \"bear\", \"default\"];\n\nfunction classifyQuestion(question: string): \"bullish\" | \"bearish\" | \"neutral\" {\n const q = question.toLowerCase();\n if (BULLISH_KEYWORDS.some((kw) => q.includes(kw))) return \"bullish\";\n if (BEARISH_KEYWORDS.some((kw) => q.includes(kw))) return \"bearish\";\n return \"neutral\";\n}\n\nexport class PredictionMarketStrategy implements Strategy {\n name = \"predictionMarket\";\n description =\n \"Derives directional signals from high-conviction prediction market probabilities\";\n requiredData = [\"predictionData\"];\n\n async analyze(ctx: StrategyContext): Promise<Signal> {\n const predictionData = ctx.predictionData;\n\n if (!predictionData || !predictionData.markets || predictionData.markets.length === 0) {\n return {\n name: this.name,\n value: 0.0,\n confidence: 0.0,\n source: \"Prediction Markets\",\n details: \"No prediction market data available\",\n };\n }\n\n let bullishScore = 0;\n let bearishScore = 0;\n const details: string[] = [];\n\n for (const market of predictionData.markets) {\n const classification = classifyQuestion(market.question);\n if (classification === \"neutral\") continue;\n\n const prob = market.probability;\n\n // Only process high-conviction markets\n if (prob > 0.75) {\n // Scale: 0 at 75%, 1 at 100%, then × 0.15 per market (max contribution)\n const strength = (prob - 0.75) * 4 * 0.15;\n if (classification === \"bullish\") {\n bullishScore += strength;\n details.push(`Bullish event likely (${(prob * 100).toFixed(0)}%): ${market.question}`);\n } else {\n bearishScore += strength;\n details.push(`Bearish event likely (${(prob * 100).toFixed(0)}%): ${market.question}`);\n }\n } else if (prob < 0.25) {\n // Event is unlikely — mild contrarian signal\n if (classification === \"bullish\") {\n bearishScore += 0.05;\n details.push(`Bullish event unlikely (${(prob * 100).toFixed(0)}%): ${market.question}`);\n } else {\n bullishScore += 0.05;\n details.push(`Bearish event unlikely (${(prob * 100).toFixed(0)}%): ${market.question}`);\n }\n }\n }\n\n const rawValue = bullishScore - bearishScore;\n const value = clamp(rawValue, -0.5, 0.5);\n\n return {\n name: this.name,\n value,\n confidence: details.length > 0 ? 0.5 : 0.1,\n source: \"Prediction Markets\",\n details: details.length > 0 ? details.join(\"; \") : \"No high-conviction markets found\",\n };\n }\n}\n","/**\n * Contrarian Social Volume Strategy\n * Trades against crowd attention — elevated news coverage is contrarian bearish.\n */\n\nimport type { Signal } from '../scoring.js';\nimport type { Strategy, StrategyContext } from './types.js';\nimport { clamp } from '../utils.js';\n\nexport class SocialVolumeStrategy implements Strategy {\n name = 'socialVolume';\n description = 'Contrarian social volume: fades extreme news attention, neutral when coverage is low';\n requiredData = ['socialData'];\n\n async analyze(ctx: StrategyContext): Promise<Signal> {\n if (!ctx.socialData) {\n return {\n name: this.name,\n value: 0.0,\n confidence: 0.1,\n source: 'Social Volume',\n details: 'No social data available',\n };\n }\n\n const { newsCount24h } = ctx.socialData;\n let value = 0;\n let confidence = 0.3;\n let detail: string;\n\n if (newsCount24h > 10) {\n // Extreme attention — contrarian bearish\n value = -0.2;\n confidence = 0.5;\n detail = `Extreme news attention (${newsCount24h} articles): contrarian bearish`;\n } else if (newsCount24h > 5) {\n // Elevated attention — mild caution\n value = -0.1;\n confidence = 0.4;\n detail = `Elevated news attention (${newsCount24h} articles): mild caution`;\n } else if (newsCount24h >= 1) {\n // Normal coverage\n value = 0;\n confidence = 0.3;\n detail = `Normal news coverage (${newsCount24h} articles)`;\n } else {\n // Under the radar\n value = 0;\n confidence = 0.3;\n detail = 'Under the radar: no recent news coverage';\n }\n\n return {\n name: this.name,\n value: clamp(value),\n confidence,\n source: 'Social Volume',\n details: detail,\n };\n }\n}\n","/**\n * Kronos Volatility Forecast Strategy — uses ML-predicted future volatility\n * to generate signals and inform risk management.\n *\n * How it works:\n * 1. Kronos generates N Monte Carlo price paths from historical OHLCV\n * 2. Path spread = predicted volatility (wider spread = more uncertain future)\n * 3. Directional bias = mean path direction (if most paths go up → bullish)\n *\n * Signal logic:\n * - directionalBias > 0.3: bullish signal (+0.2 to +0.4 scaled)\n * - directionalBias < -0.3: bearish signal (-0.2 to -0.4 scaled)\n * - High predicted vol (pathSpread > 8%): increase confidence on directional\n * signals (high vol = big moves, worth trading), but clamp signal magnitude\n * (don't overbet on volatile predictions)\n * - Low predicted vol (pathSpread < 3%): reduce confidence (narrow paths =\n * range-bound, less opportunity)\n *\n * The volatility forecast is ALSO consumed by the executor (via StrategyContext)\n * for dynamic stop-loss width, separate from this signal.\n */\n\nimport type { Signal } from '../scoring.js';\nimport type { Strategy, StrategyContext } from './types.js';\nimport { clamp } from '../utils.js';\n\nexport class KronosVolForecastStrategy implements Strategy {\n name = 'kronosVolForecast';\n description = 'Kronos ML volatility forecast (directional bias + vol regime)';\n requiredData = ['kronosData'];\n\n async analyze(ctx: StrategyContext): Promise<Signal> {\n const k = ctx.kronosData;\n if (!k) {\n return {\n name: this.name,\n value: 0,\n confidence: 0,\n source: this.description,\n details: 'No Kronos forecast available',\n };\n }\n\n let value = 0;\n const details: string[] = [];\n let confidence = 0.3;\n\n // Directional bias from mean predicted path\n const bias = k.directionalBias;\n if (Math.abs(bias) > 0.3) {\n // Strong directional signal: scale from 0 at 0.3 to ±0.4 at ±1.0\n const strength = (Math.abs(bias) - 0.3) / 0.7; // 0 to 1\n value = Math.sign(bias) * (0.2 + strength * 0.2); // ±0.2 to ±0.4\n const dir = bias > 0 ? 'bullish' : 'bearish';\n details.push(`Kronos ${dir} bias ${(bias * 100).toFixed(0)}% → ${value >= 0 ? '+' : ''}${value.toFixed(2)}`);\n confidence = 0.4 + strength * 0.2; // 0.4 to 0.6\n } else if (Math.abs(bias) > 0.1) {\n // Mild directional lean — informational, weak signal\n value = bias * 0.15; // ±0.015 to ±0.045\n details.push(`Kronos mild ${bias > 0 ? 'bullish' : 'bearish'} lean ${(bias * 100).toFixed(0)}%`);\n } else {\n details.push(`Kronos neutral (bias ${(bias * 100).toFixed(0)}%)`);\n }\n\n // Vol regime context\n const spread = k.pathSpreadPct;\n if (spread > 8) {\n // High predicted vol: directional signals are MORE meaningful\n // (big moves expected — worth taking positions)\n if (Math.abs(value) > 0.05) {\n confidence = Math.min(confidence + 0.15, 0.75);\n }\n details.push(`High vol forecast (${spread.toFixed(1)}% path spread)`);\n } else if (spread < 3) {\n // Low predicted vol: range-bound expected, reduce conviction\n confidence *= 0.6;\n details.push(`Low vol forecast (${spread.toFixed(1)}% path spread)`);\n } else {\n details.push(`Normal vol (${spread.toFixed(1)}% path spread)`);\n }\n\n return {\n name: this.name,\n value: clamp(value),\n confidence: Math.min(confidence, 1.0),\n source: this.description,\n details: details.join('; '),\n };\n }\n}\n","/**\n * Narrative-Vacuum Whale Signal — detects when whale flow spikes but\n * social/news volume is quiet (\"the market is positioning before the\n * narrative catches up\").\n *\n * Signal: WhaleFlowZ × (1 - SocialVolumeZ)\n * - High whale flow + low social chatter = smart money moving silently → follow\n * - High whale flow + high social chatter = crowd already knows → no edge\n * - Low whale flow + any chatter = nothing happening\n *\n * Data sources:\n * - Nansen smart-money netflow (whale positioning)\n * - SocialData/CryptoCompare news count (narrative proxy)\n *\n * Fires as an \"onchain\" category signal.\n */\n\nimport type { Signal } from '../scoring.js';\nimport type { Strategy, StrategyContext } from './types.js';\nimport { clamp } from '../utils.js';\n\nexport class NarrativeVacuumStrategy implements Strategy {\n name = 'narrativeVacuum';\n description = 'Whale flow divergence from social narrative (Nansen + social data)';\n requiredData = ['nansenFlowData', 'socialData'];\n\n async analyze(ctx: StrategyContext): Promise<Signal> {\n const flow = ctx.nansenFlowData;\n const social = ctx.socialData;\n\n if (!flow) {\n return {\n name: this.name,\n value: 0,\n confidence: 0,\n source: this.description,\n details: 'No Nansen flow data',\n };\n }\n\n const details: string[] = [];\n let value = 0;\n let confidence = 0.3;\n\n // ── Whale flow strength ──\n // netFlow24hUsd: positive = smart money accumulating, negative = distributing\n const netFlow = flow.netFlow24hUsd;\n const traderCount = flow.traderCount;\n\n // Normalize flow: $1M+ is a strong signal\n const flowStrength = Math.min(1, Math.abs(netFlow) / 2_000_000); // 0-1 scale\n const flowDirection = Math.sign(netFlow); // +1 accumulating, -1 distributing\n\n if (flowStrength < 0.1) {\n // Minimal whale activity — no signal\n return {\n name: this.name,\n value: 0,\n confidence: 0.1,\n source: this.description,\n details: `Low whale flow ($${(netFlow / 1000).toFixed(0)}K, ${traderCount} traders) — no edge`,\n };\n }\n\n // ── Social narrative intensity ──\n // newsCount24h: 0 = silence, 1-3 = quiet, 5+ = moderate, 10+ = loud\n const newsCount = social?.newsCount24h ?? 0;\n\n // Social intensity score: 0 = total silence, 1 = very loud\n const socialIntensity = Math.min(1, newsCount / 10);\n\n // ── Narrative vacuum = high flow × low chatter ──\n // vacuum: 1 = whale flow with zero chatter (strongest signal)\n // vacuum: 0 = whale flow with heavy chatter (crowd already knows)\n const vacuum = 1 - socialIntensity;\n const vacuumScore = flowStrength * vacuum;\n\n if (vacuumScore > 0.3) {\n // Strong narrative vacuum — follow the whales\n value = flowDirection * (0.2 + vacuumScore * 0.3); // ±0.2 to ±0.5\n confidence = 0.5 + vacuumScore * 0.25; // 0.5 to 0.75\n\n const dir = netFlow > 0 ? 'accumulating' : 'distributing';\n details.push(\n `Whale ${dir} $${(Math.abs(netFlow) / 1_000_000).toFixed(1)}M (${traderCount} traders) ` +\n `with only ${newsCount} news articles — narrative vacuum (score ${vacuumScore.toFixed(2)})`\n );\n } else if (flowStrength > 0.3 && socialIntensity > 0.5) {\n // Whale flow exists but crowd already talking — no vacuum edge\n details.push(\n `Whale flow $${(Math.abs(netFlow) / 1_000_000).toFixed(1)}M but ${newsCount} articles — ` +\n `narrative already priced in`\n );\n confidence = 0.2;\n } else {\n details.push(\n `Moderate flow $${(Math.abs(netFlow) / 1000).toFixed(0)}K, ${newsCount} articles — ` +\n `insufficient vacuum`\n );\n }\n\n return {\n name: this.name,\n value: clamp(value),\n confidence: Math.min(confidence, 1.0),\n source: this.description,\n details: details.join('; '),\n };\n }\n}\n","/**\n * Whale Intent Classifier — distinguishes operational whale transfers\n * (ETF AP flows, exchange rebalancing) from directional positioning.\n *\n * Key insight from X research: the edge is NOT \"big transfer happened\"\n * but WHAT TYPE of transfer it is. ETF authorized participants move\n * BTC/ETH regularly for operational reasons (creations/redemptions) —\n * these are noise. Discretionary smart-money moves at unusual times,\n * sizes, or patterns are the real signal.\n *\n * Intent classification heuristics:\n * - High trader count + consistent direction = consensus positioning (strong)\n * - Few traders + large size = single whale (moderate, could be operational)\n * - Flow aligned with HL perp positioning = cross-venue confirmation (very strong)\n * - Flow opposing HL perp positioning = divergence (caution)\n *\n * Data sources:\n * - Nansen smart-money netflow (aggregate)\n * - Nansen HL perp trades (cross-venue confirmation)\n * - Hyperliquid funding rate (market sentiment proxy)\n *\n * Fires as a \"smartMoney\" category signal.\n */\n\nimport type { Signal } from '../scoring.js';\nimport type { Strategy, StrategyContext } from './types.js';\nimport { clamp } from '../utils.js';\n\nexport class WhaleIntentStrategy implements Strategy {\n name = 'whaleIntent';\n description = 'Whale transfer intent classifier (operational vs directional, Nansen)';\n requiredData = ['nansenFlowData'];\n\n async analyze(ctx: StrategyContext): Promise<Signal> {\n const flow = ctx.nansenFlowData;\n if (!flow) {\n return {\n name: this.name,\n value: 0,\n confidence: 0,\n source: this.description,\n details: 'No Nansen flow data',\n };\n }\n\n const details: string[] = [];\n let value = 0;\n let confidence = 0.2;\n\n const netFlow = flow.netFlow24hUsd;\n const traderCount = flow.traderCount;\n const flowDirection = Math.sign(netFlow); // +1 buy, -1 sell\n\n // ── Intent classification ──\n\n // 1. Trader count analysis: many traders = consensus, few = single whale/operational\n const isConsensus = traderCount >= 5;\n const isSingleWhale = traderCount <= 2 && Math.abs(netFlow) > 500_000;\n\n // 2. Flow magnitude\n const flowMagnitude = Math.abs(netFlow);\n const isSignificant = flowMagnitude > 200_000; // >$200K\n const isLarge = flowMagnitude > 1_000_000; // >$1M\n\n if (!isSignificant) {\n return {\n name: this.name,\n value: 0,\n confidence: 0.1,\n source: this.description,\n details: `Flow too small ($${(flowMagnitude / 1000).toFixed(0)}K) for intent classification`,\n };\n }\n\n // 3. Cross-venue confirmation: does HL perp positioning agree?\n const hlPerps = ctx.nansenHlPerps;\n let crossVenueAgreement = false;\n let crossVenueDivergence = false;\n\n if (hlPerps && hlPerps.longRatio !== undefined) {\n const hlDirection = hlPerps.longRatio > 0.6 ? 1 : hlPerps.longRatio < 0.4 ? -1 : 0;\n if (hlDirection !== 0 && hlDirection === flowDirection) {\n crossVenueAgreement = true;\n details.push(`HL perps confirm: ${(hlPerps.longRatio * 100).toFixed(0)}% long`);\n } else if (hlDirection !== 0 && hlDirection === -flowDirection) {\n crossVenueDivergence = true;\n details.push(`HL perps DIVERGE: ${(hlPerps.longRatio * 100).toFixed(0)}% long vs on-chain ${flowDirection > 0 ? 'accumulation' : 'distribution'}`);\n }\n }\n\n // 4. Funding rate context: extreme funding = crowded trade\n const fundingRate = ctx.fundingRateData?.rate8h ?? 0;\n const isFundingExtreme = Math.abs(fundingRate) > 0.0005; // >0.05% per 8h\n\n // ── Score computation ──\n\n if (isConsensus && isLarge) {\n // Multiple smart wallets, large flow = strong directional intent\n value = flowDirection * 0.4;\n confidence = 0.6;\n const dir = netFlow > 0 ? 'accumulating' : 'distributing';\n details.push(`Consensus ${dir}: ${traderCount} smart wallets, $${(flowMagnitude / 1e6).toFixed(1)}M`);\n\n if (crossVenueAgreement) {\n value = flowDirection * 0.5;\n confidence = 0.75;\n details.push('Cross-venue confirmed');\n }\n } else if (isConsensus) {\n // Multiple wallets, moderate flow\n value = flowDirection * 0.25;\n confidence = 0.45;\n details.push(`Smart money consensus: ${traderCount} wallets, $${(flowMagnitude / 1000).toFixed(0)}K`);\n } else if (isSingleWhale) {\n // Single large transfer — could be operational (ETF AP, exchange rebalance)\n // Lower confidence unless cross-venue confirms\n if (crossVenueAgreement) {\n value = flowDirection * 0.3;\n confidence = 0.5;\n details.push(`Single whale $${(flowMagnitude / 1e6).toFixed(1)}M — cross-venue confirmed, likely directional`);\n } else {\n value = flowDirection * 0.15;\n confidence = 0.3;\n details.push(`Single whale $${(flowMagnitude / 1e6).toFixed(1)}M — may be operational (ETF/AP/rebalance)`);\n }\n }\n\n // Funding extreme warning: if funding is extreme in the same direction as flow,\n // the trade may be crowded (whale joining a crowded side = lower edge)\n if (isFundingExtreme && Math.sign(fundingRate) === flowDirection) {\n confidence *= 0.7; // reduce confidence\n details.push(`Crowded funding (${(fundingRate * 100).toFixed(3)}%) — whale joining crowded side`);\n }\n\n // Divergence penalty\n if (crossVenueDivergence) {\n confidence *= 0.5;\n details.push('WARNING: on-chain vs perps divergence — conflicting signals');\n }\n\n return {\n name: this.name,\n value: clamp(value),\n confidence: Math.min(confidence, 1.0),\n source: this.description,\n details: details.join('; '),\n };\n }\n}\n","/**\n * Strategy registry — runs all enabled strategies and collects signals.\n */\n\nimport type { Signal } from '../scoring.js';\nimport type { Strategy, StrategyContext, StrategyConfig } from './types.js';\nimport { SentimentContrarianStrategy } from './sentiment-contrarian.js';\nimport { BreakoutOnChainStrategy } from './breakout-onchain.js';\nimport { FundingRateStrategy } from './funding-rate.js';\nimport { DexFlowStrategy } from './dex-flow.js';\nimport { HyperliquidFlowStrategy } from './hyperliquid-flow.js';\nimport { MultiTimeframeStrategy } from './multi-timeframe.js';\nimport { CrossSectionalMomentumStrategy } from './cross-sectional-momentum.js';\nimport { TradingViewSignalStrategy } from './tradingview-signal.js';\nimport { BtcNetworkHealthStrategy } from './btc-network-health.js';\nimport { PredictionMarketStrategy } from './prediction-market.js';\nimport { SocialVolumeStrategy } from './social-volume.js';\nimport { KronosVolForecastStrategy } from './kronos-vol-forecast.js';\nimport { NarrativeVacuumStrategy } from './narrative-vacuum.js';\nimport { WhaleIntentStrategy } from './whale-intent.js';\n\nexport type { Strategy, StrategyContext, StrategyConfig };\nexport { SentimentContrarianStrategy, BreakoutOnChainStrategy, FundingRateStrategy };\nexport { DexFlowStrategy, HyperliquidFlowStrategy };\nexport { MultiTimeframeStrategy, CrossSectionalMomentumStrategy };\nexport { TradingViewSignalStrategy };\nexport { BtcNetworkHealthStrategy };\nexport { PredictionMarketStrategy, SocialVolumeStrategy };\nexport { KronosVolForecastStrategy };\nexport { NarrativeVacuumStrategy, WhaleIntentStrategy };\n\nexport const DEFAULT_STRATEGIES: Strategy[] = [\n // SentimentContrarianStrategy removed from default list — fires only at F&G\n // extremes (<25 or >75), returns zero 85% of the time, wasting 20% of sentiment\n // weight budget. Re-enable via config when F&G regime is extreme.\n new BreakoutOnChainStrategy(), // technical — breakout + volume\n new FundingRateStrategy(), // onchain — HL funding rates\n // DexFlowStrategy disabled — our tokens (BTC, SOL, HYPE, FARTCOIN) trade on\n // CEXes, not EVM DEXes. DEXScreener returns 0 txns/hr for most tokens.\n new HyperliquidFlowStrategy(), // onchain — HL flow/OI/orderbook\n new MultiTimeframeStrategy(), // technical — multi-TF EMA alignment\n new CrossSectionalMomentumStrategy(), // technical — relative strength ranking\n new TradingViewSignalStrategy(), // technical — TradingView MCP indicators\n new BtcNetworkHealthStrategy(), // technical — BTC network health\n new PredictionMarketStrategy(), // event — prediction market catalysts\n new SocialVolumeStrategy(), // sentiment — social volume contrarian\n new KronosVolForecastStrategy(), // technical — ML volatility forecast\n new NarrativeVacuumStrategy(), // onchain — whale flow vs narrative vacuum\n new WhaleIntentStrategy(), // smartMoney — whale intent classifier\n];\n\n/**\n * Run all enabled strategies and collect signals.\n * Each strategy runs independently — one failure doesn't break others.\n */\nexport async function runStrategies(\n ctx: StrategyContext,\n configs?: Record<string, StrategyConfig>,\n): Promise<Signal[]> {\n const signals: Signal[] = [];\n\n const tasks = DEFAULT_STRATEGIES.map(async (strategy) => {\n // Check if strategy is explicitly disabled\n const config = configs?.[strategy.name];\n if (config && !config.enabled) return null;\n\n try {\n const signal = await strategy.analyze(ctx);\n\n // Apply weight override if configured\n if (config?.weight !== undefined) {\n // Weight override is handled at the scoring layer, but we tag it\n (signal as any)._weightOverride = config.weight;\n }\n\n return signal;\n } catch (err) {\n // Strategy failed — return a zero-confidence signal so scoring isn't affected\n console.error(`Strategy ${strategy.name} failed: ${(err as Error).message}`);\n return {\n name: strategy.name,\n value: 0.0,\n confidence: 0.0,\n source: strategy.description,\n details: `Error: ${(err as Error).message}`,\n } satisfies Signal;\n }\n });\n\n const results = await Promise.allSettled(tasks);\n\n for (const result of results) {\n if (result.status === 'fulfilled' && result.value) {\n signals.push(result.value);\n }\n }\n\n return signals;\n}\n","/**\n * Funding rate provider — fetches perpetual futures funding rates from Binance.\n * Free public API, no authentication required.\n * Rate limit: 2400 req/min (generous).\n */\n\nconst BINANCE_BASE = 'https://fapi.binance.com';\n\n// Map CoinGecko token IDs to Binance perp symbols\nconst TOKEN_TO_SYMBOL: Record<string, string> = {\n bitcoin: 'BTCUSDT',\n ethereum: 'ETHUSDT',\n solana: 'SOLUSDT',\n avalanche: 'AVAXUSDT',\n cardano: 'ADAUSDT',\n polkadot: 'DOTUSDT',\n near: 'NEARUSDT',\n cosmos: 'ATOMUSDT',\n sui: 'SUIUSDT',\n aptos: 'APTUSDT',\n uniswap: 'UNIUSDT',\n aave: 'AAVEUSDT',\n maker: 'MKRUSDT',\n chainlink: 'LINKUSDT',\n arbitrum: 'ARBUSDT',\n optimism: 'OPUSDT',\n polygon: 'MATICUSDT',\n dogecoin: 'DOGEUSDT',\n litecoin: 'LTCUSDT',\n filecoin: 'FILUSDT',\n render: 'RENDERUSDT',\n injective: 'INJUSDT',\n jupiter: 'JUPUSDT',\n pendle: 'PENDLEUSDT',\n pepe: 'PEPEUSDT',\n};\n\nexport interface FundingRateData {\n rate8h: number; // Current 8h funding rate (e.g., 0.0001 = 0.01%)\n annualizedRate: number; // Annualized funding rate\n exchange: string;\n symbol: string;\n timestamp: number;\n}\n\nexport class FundingRateProvider {\n /** Get the current funding rate for a token. Returns null if token has no perp market. */\n async getFundingRate(tokenId: string): Promise<FundingRateData | null> {\n const symbol = TOKEN_TO_SYMBOL[tokenId];\n if (!symbol) return null;\n\n try {\n const res = await fetch(`${BINANCE_BASE}/fapi/v1/fundingRate?symbol=${symbol}&limit=1`);\n if (!res.ok) return null;\n\n const data = await res.json() as Array<{ fundingRate: string; fundingTime: number }>;\n if (!data || data.length === 0) return null;\n\n const rate8h = parseFloat(data[0]!.fundingRate);\n if (!Number.isFinite(rate8h)) return null;\n // Annualize: 3 funding periods per day × 365 days\n const annualizedRate = rate8h * 3 * 365;\n\n return {\n rate8h,\n annualizedRate,\n exchange: 'binance',\n symbol,\n timestamp: data[0]!.fundingTime,\n };\n } catch {\n return null;\n }\n }\n}\n","/**\n * Token unlock provider — fetches upcoming vesting/unlock events.\n * Uses DefiLlama's free unlocks API (no authentication required).\n * Fallback: CoinGecko status_updates for governance events.\n */\n\nconst DEFILLAMA_UNLOCKS = 'https://api.llama.fi/protocol';\n\n// Map CoinGecko IDs to DefiLlama protocol slugs\nconst TOKEN_TO_PROTOCOL: Record<string, string> = {\n uniswap: 'uniswap',\n aave: 'aave',\n maker: 'makerdao',\n compound: 'compound-finance',\n curve: 'curve-dex',\n lido: 'lido',\n arbitrum: 'arbitrum',\n optimism: 'optimism',\n polygon: 'polygon',\n sushi: 'sushi',\n jupiter: 'jupiter',\n pendle: 'pendle',\n injective: 'injective-protocol',\n render: 'render-network',\n};\n\nexport interface UnlockEvent {\n /** Percentage of circulating supply being unlocked */\n percentOfSupply: number;\n /** Days until the unlock */\n daysUntil: number;\n /** Description of the unlock */\n description: string;\n}\n\nexport interface TokenUnlockData {\n upcomingUnlocks: UnlockEvent[];\n totalUpcomingPercent: number;\n}\n\nexport class TokenUnlocksProvider {\n /** Get upcoming unlock events for a token. Returns null if no data available. */\n async getUnlocks(tokenId: string): Promise<TokenUnlockData | null> {\n const slug = TOKEN_TO_PROTOCOL[tokenId];\n if (!slug) return null;\n\n try {\n const res = await fetch(`${DEFILLAMA_UNLOCKS}/${slug}`);\n if (!res.ok) return null;\n\n const data = await res.json() as any;\n\n // DefiLlama protocol endpoint includes mcap and token info\n // We estimate unlock pressure from token allocation data\n const mcap = data?.mcap;\n const fdv = data?.fdvData;\n\n if (!mcap || !fdv || fdv <= 0) return null;\n\n // Ratio of circulating to fully-diluted gives us how much is still locked\n const circulatingRatio = mcap / fdv;\n const lockedPercent = (1 - circulatingRatio) * 100;\n\n if (lockedPercent < 1) {\n // Fully circulating — no unlock pressure\n return { upcomingUnlocks: [], totalUpcomingPercent: 0 };\n }\n\n // Estimate: assume locked tokens vest linearly over 2 years\n // This is an approximation — real schedules vary\n const monthlyUnlockPercent = lockedPercent / 24;\n const weeklyUnlockPercent = monthlyUnlockPercent / 4;\n\n const unlocks: UnlockEvent[] = [];\n\n if (weeklyUnlockPercent > 0.5) {\n unlocks.push({\n percentOfSupply: weeklyUnlockPercent,\n daysUntil: 7,\n description: `Estimated ~${weeklyUnlockPercent.toFixed(1)}% weekly vesting (${lockedPercent.toFixed(0)}% still locked)`,\n });\n }\n\n if (monthlyUnlockPercent > 1) {\n unlocks.push({\n percentOfSupply: monthlyUnlockPercent,\n daysUntil: 30,\n description: `Estimated ~${monthlyUnlockPercent.toFixed(1)}% monthly vesting`,\n });\n }\n\n return {\n upcomingUnlocks: unlocks,\n totalUpcomingPercent: weeklyUnlockPercent,\n };\n } catch {\n return null;\n }\n }\n}\n","/**\n * Twitter Sentiment Provider — fetches token-specific sentiment data from Twitter API v2.\n * Uses OAuth 1.0a for user-context authentication (higher rate limits than app-only).\n * Rate limit: 10 requests/min, 100 tweets/request (Recent Search free tier).\n */\n\nimport { createHmac, randomBytes } from 'node:crypto';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\n\nexport interface TwitterSentimentData {\n mentionVolume: number; // Total tweets in last hour vs 24h hourly average\n sentimentScore: number; // Simple keyword-based sentiment (-1 to +1)\n engagementWeightedSentiment: number; // Sentiment weighted by engagement\n volumeSpike: number; // Ratio of last-hour volume to 24h hourly average\n tweetCount: number; // Total tweets analyzed\n llmSentiment?: number; // LLM-analyzed sentiment (-1 to +1)\n llmConfidence?: number; // average LLM confidence\n llmBullishPercent?: number; // % of tweets classified bullish\n llmBearishPercent?: number; // % classified bearish\n}\n\ninterface TwitterTweet {\n id: string;\n text: string;\n created_at: string;\n public_metrics: {\n like_count: number;\n retweet_count: number;\n reply_count: number;\n };\n author_id: string;\n}\n\ninterface TwitterApiResponse {\n data: TwitterTweet[];\n meta: {\n result_count: number;\n next_token?: string;\n };\n}\n\ninterface OpenAISentimentResponse {\n sentiment: 'BULLISH' | 'BEARISH' | 'NEUTRAL';\n confidence: number;\n}\n\ninterface TweetWithEngagement {\n text: string;\n engagement: number;\n}\n\nconst TWITTER_BASE = 'https://api.twitter.com/2';\n\n// Map CoinGecko token IDs to Twitter search queries\nconst TOKEN_TO_SEARCH: Record<string, string> = {\n bitcoin: '$BTC OR #bitcoin',\n ethereum: '$ETH OR #ethereum',\n solana: '$SOL OR #solana',\n arbitrum: '$ARB OR #arbitrum',\n uniswap: '$UNI OR #uniswap',\n aave: '$AAVE OR #aave',\n chainlink: '$LINK OR #chainlink',\n cardano: '$ADA OR #cardano',\n polkadot: '$DOT OR #polkadot',\n avalanche: '$AVAX OR #avalanche',\n near: '$NEAR OR #near',\n cosmos: '$ATOM OR #cosmos',\n sui: '$SUI OR #sui',\n aptos: '$APT OR #aptos',\n maker: '$MKR OR #maker',\n optimism: '$OP OR #optimism',\n polygon: '$MATIC OR #polygon',\n dogecoin: '$DOGE OR #dogecoin',\n litecoin: '$LTC OR #litecoin',\n filecoin: '$FIL OR #filecoin',\n render: '$RENDER OR #render',\n injective: '$INJ OR #injective',\n jupiter: '$JUP OR #jupiter',\n pendle: '$PENDLE OR #pendle',\n pepe: '$PEPE OR #pepe',\n};\n\n// Sentiment keywords\nconst BULLISH_WORDS = [\n 'bullish', 'moon', 'pump', 'buy', 'long', 'breakout', 'ath', 'send it', 'lfg', 'wagmi', 'undervalued',\n 'hodl', 'diamond hands', 'to the moon', 'rocket', 'bull run', 'green', 'gainz', 'surge', 'rally'\n];\n\nconst BEARISH_WORDS = [\n 'bearish', 'dump', 'sell', 'short', 'crash', 'dead', 'rekt', 'ngmi', 'overvalued', 'scam',\n 'paper hands', 'bear market', 'red', 'dip', 'correction', 'bubble', 'rugpull', 'bloodbath'\n];\n\nexport class TwitterSentimentProvider {\n private cacheDir: string;\n private cacheTTL = 5 * 60 * 1000; // 5 minutes\n\n constructor() {\n this.cacheDir = join(homedir(), '.sherwood', 'agent', 'cache');\n }\n\n /** Get Twitter sentiment data for a token. Returns null if no data or API failure. */\n async getSentiment(tokenId: string): Promise<TwitterSentimentData | null> {\n const query = TOKEN_TO_SEARCH[tokenId];\n if (!query) {\n // For unknown tokens, try to use the token symbol if available\n return null;\n }\n\n // Check cache first\n const cached = await this.readCache(tokenId);\n if (cached) return cached;\n\n try {\n // Fetch recent tweets (last 24 hours for volume analysis)\n const tweets = await this.fetchTweets(query, new Date(Date.now() - 24 * 60 * 60 * 1000));\n if (!tweets || tweets.length === 0) return null;\n\n // Calculate metrics\n const data = await this.analyzeTweets(tweets);\n\n // Cache results\n await this.writeCache(tokenId, data);\n\n return data;\n } catch (err) {\n console.error(`Twitter API error for ${tokenId}: ${(err as Error).message}`);\n return null;\n }\n }\n\n /** Fetch tweets using Twitter API v2 Recent Search with OAuth 1.0a. */\n private async fetchTweets(query: string, startTime: Date): Promise<TwitterTweet[] | null> {\n const apiKey = process.env.TWITTER_API_KEY;\n const apiSecret = process.env.TWITTER_API_SECRET;\n const accessToken = process.env.TWITTER_ACCESS_TOKEN;\n const accessTokenSecret = process.env.TWITTER_ACCESS_TOKEN_SECRET;\n\n if (!apiKey || !apiSecret || !accessToken || !accessTokenSecret) {\n throw new Error('Twitter API credentials not found in environment variables');\n }\n\n const url = new URL(`${TWITTER_BASE}/tweets/search/recent`);\n url.searchParams.set('query', query);\n url.searchParams.set('start_time', startTime.toISOString());\n url.searchParams.set('max_results', '100');\n url.searchParams.set('tweet.fields', 'created_at,public_metrics,author_id');\n\n // Generate OAuth 1.0a signature\n const oauthParams: Record<string, string> = {\n oauth_consumer_key: apiKey,\n oauth_token: accessToken,\n oauth_signature_method: 'HMAC-SHA1',\n oauth_timestamp: Math.floor(Date.now() / 1000).toString(),\n oauth_nonce: randomBytes(16).toString('hex'),\n oauth_version: '1.0',\n };\n\n // Create signature base string\n const params = { ...oauthParams, ...Object.fromEntries(url.searchParams) };\n const sortedParams = Object.keys(params)\n .sort()\n .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)\n .join('&');\n\n const signatureBaseString = `GET&${encodeURIComponent(url.origin + url.pathname)}&${encodeURIComponent(sortedParams)}`;\n\n // Create signing key\n const signingKey = `${encodeURIComponent(apiSecret)}&${encodeURIComponent(accessTokenSecret)}`;\n\n // Generate signature\n const signature = createHmac('sha1', signingKey).update(signatureBaseString).digest('base64');\n\n // Create Authorization header\n const authHeader = 'OAuth ' + Object.entries({ ...oauthParams, oauth_signature: signature })\n .map(([key, value]) => `${encodeURIComponent(key)}=\"${encodeURIComponent(value)}\"`)\n .join(', ');\n\n const response = await fetch(url.toString(), {\n headers: {\n 'Authorization': authHeader,\n 'User-Agent': 'Sherwood-Agent/1.0'\n }\n });\n\n if (!response.ok) {\n if (response.status === 429) {\n // Rate limited\n return null;\n }\n throw new Error(`Twitter API error: ${response.status} ${response.statusText}`);\n }\n\n const data = await response.json() as TwitterApiResponse;\n return data.data || [];\n }\n\n /** Analyze sentiment using OpenAI GPT-4o-mini. */\n private async analyzeSentimentWithLLM(tweets: TwitterTweet[]): Promise<{\n llmSentiment: number;\n llmConfidence: number;\n llmBullishPercent: number;\n llmBearishPercent: number;\n } | null> {\n const openaiKey = process.env.OPENAI_API_KEY;\n if (!openaiKey) {\n return null;\n }\n\n try {\n // Prepare tweets with engagement data\n const tweetsWithEngagement: TweetWithEngagement[] = tweets.map(tweet => ({\n text: tweet.text,\n engagement: tweet.public_metrics.like_count +\n tweet.public_metrics.retweet_count +\n tweet.public_metrics.reply_count,\n }));\n\n // Keep LLM pass bounded so full scanner doesn't timeout under 10-token auto mode.\n // Use top-engagement tweets first because they carry the most signal.\n const llmTweets = [...tweetsWithEngagement]\n .sort((a, b) => b.engagement - a.engagement)\n .slice(0, 40);\n if (llmTweets.length === 0) {\n return null;\n }\n\n // Batch tweets into groups of 20\n const batchSize = 20;\n const batches: TweetWithEngagement[][] = [];\n for (let i = 0; i < llmTweets.length; i += batchSize) {\n batches.push(llmTweets.slice(i, i + batchSize));\n }\n\n const allResults: (OpenAISentimentResponse & { engagement: number })[] = [];\n let consecutiveBatchFailures = 0;\n const MAX_CONSECUTIVE_BATCH_FAILURES = 2;\n\n // Process each batch\n for (const batch of batches) {\n // Sanitize tweet text to prevent prompt injection:\n // 1. Strip dangerous chars first (bidi overrides, zero-width, control chars)\n // 2. Flatten newlines\n // 3. Escape quotes/backslashes\n // 4. Truncate\n const sanitizeTweet = (text: string): string => {\n return text\n .replace(/[\\x00-\\x1f\\x7f]/g, '') // strip control chars\n .replace(/[\\u200B-\\u200D\\u202A-\\u202E\\u2066-\\u2069\\u00AD\\uFEFF]/g, '') // strip bidi overrides + zero-width chars\n .replace(/\\n/g, ' ') // flatten newlines\n .replace(/[\"\\\\]/g, (c) => `\\\\${c}`) // escape quotes/backslashes\n .slice(0, 280); // truncate last (after stripping, not before)\n };\n\n const tweetTexts = batch.map((tweet, idx) => `${idx + 1}. \"${sanitizeTweet(tweet.text)}\"`).join('\\n');\n\n const systemPrompt = `You are a crypto market sentiment analyzer. For each tweet, classify sentiment as BULLISH, BEARISH, or NEUTRAL with confidence 0-100. Consider sarcasm, irony, and CT slang.\nReturn ONLY valid JSON with this exact shape:\n{\"results\":[{\"sentiment\":\"BULLISH|BEARISH|NEUTRAL\",\"confidence\":0-100}]}`;\n\n const userPrompt = `Analyze these ${batch.length} tweets:\\n${tweetTexts}`;\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 10_000);\n const response = await fetch('https://api.openai.com/v1/chat/completions', {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${openaiKey}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n model: 'gpt-4o-mini',\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt },\n ],\n response_format: { type: 'json_object' },\n temperature: 0,\n }),\n signal: controller.signal,\n });\n clearTimeout(timeout);\n\n if (!response.ok) {\n console.warn(`OpenAI API error: ${response.status} ${response.statusText}`);\n consecutiveBatchFailures++;\n if (consecutiveBatchFailures >= MAX_CONSECUTIVE_BATCH_FAILURES) return null;\n continue;\n }\n\n const data = await response.json();\n const content = data.choices?.[0]?.message?.content;\n if (!content) {\n console.warn('OpenAI API returned no content');\n consecutiveBatchFailures++;\n if (consecutiveBatchFailures >= MAX_CONSECUTIVE_BATCH_FAILURES) return null;\n continue;\n }\n\n // Parse JSON response — strip markdown code blocks if present\n let batchResults: OpenAISentimentResponse[];\n try {\n let jsonStr = content.trim();\n // OpenAI sometimes wraps JSON in ```json ... ``` markdown blocks\n if (jsonStr.startsWith('```')) {\n jsonStr = jsonStr.replace(/^```(?:json)?\\s*\\n?/, '').replace(/\\n?```\\s*$/, '');\n }\n const parsed = JSON.parse(jsonStr) as unknown;\n let parsedResults: unknown[] | null = null;\n\n if (Array.isArray(parsed)) {\n parsedResults = parsed;\n } else if (parsed && typeof parsed === 'object') {\n const obj = parsed as Record<string, unknown>;\n if (Array.isArray(obj.results)) {\n parsedResults = obj.results;\n } else if (Array.isArray(obj.tweets)) {\n parsedResults = obj.tweets;\n } else if (Array.isArray(obj.sentiments)) {\n parsedResults = obj.sentiments;\n }\n }\n\n if (!parsedResults) {\n const arrMatch = jsonStr.match(/\\[[\\s\\S]*\\]/);\n if (arrMatch) {\n const recovered = JSON.parse(arrMatch[0]) as unknown;\n if (Array.isArray(recovered)) {\n parsedResults = recovered;\n }\n }\n }\n\n if (!parsedResults) {\n throw new Error('No parseable sentiment array');\n }\n\n batchResults = parsedResults.map((item) => {\n const row = (item && typeof item === 'object') ? item as Record<string, unknown> : {};\n const sentimentRaw = String(row.sentiment ?? row.label ?? row.classification ?? 'NEUTRAL').toUpperCase();\n const confidenceRaw = Number(row.confidence ?? row.score ?? 50);\n return {\n sentiment: sentimentRaw as OpenAISentimentResponse['sentiment'],\n confidence: confidenceRaw,\n };\n });\n\n if (batchResults.length > batch.length) {\n batchResults = batchResults.slice(0, batch.length);\n } else if (batchResults.length < batch.length) {\n while (batchResults.length < batch.length) {\n batchResults.push({ sentiment: 'NEUTRAL', confidence: 50 });\n }\n }\n\n // Validate each element to prevent NaN propagation from malformed responses\n const VALID_SENTIMENTS = new Set(['BULLISH', 'BEARISH', 'NEUTRAL']);\n for (const r of batchResults) {\n if (!VALID_SENTIMENTS.has(r.sentiment)) r.sentiment = 'NEUTRAL';\n if (typeof r.confidence !== 'number' || !Number.isFinite(r.confidence)) r.confidence = 50;\n r.confidence = Math.max(0, Math.min(100, r.confidence));\n }\n consecutiveBatchFailures = 0;\n } catch (parseErr) {\n console.warn(`Failed to parse OpenAI response: ${parseErr}`);\n consecutiveBatchFailures++;\n if (consecutiveBatchFailures >= MAX_CONSECUTIVE_BATCH_FAILURES) return null;\n // Continue using neutral placeholders for this batch instead of failing whole token scan.\n batchResults = batch.map(() => ({ sentiment: 'NEUTRAL', confidence: 50 }));\n }\n\n // Combine with engagement data\n for (let i = 0; i < batchResults.length; i++) {\n allResults.push({\n ...batchResults[i],\n engagement: batch[i].engagement,\n });\n }\n }\n\n // Calculate weighted sentiment metrics\n const bullishTweets = allResults.filter(r => r.sentiment === 'BULLISH');\n const bearishTweets = allResults.filter(r => r.sentiment === 'BEARISH');\n\n const totalTweets = allResults.length;\n const llmBullishPercent = (bullishTweets.length / totalTweets) * 100;\n const llmBearishPercent = (bearishTweets.length / totalTweets) * 100;\n\n // Calculate engagement-weighted sentiment score\n let totalWeightedScore = 0;\n let totalWeight = 0;\n let totalConfidence = 0;\n\n for (const result of allResults) {\n let sentimentMultiplier = 0;\n if (result.sentiment === 'BULLISH') {\n sentimentMultiplier = 1;\n } else if (result.sentiment === 'BEARISH') {\n sentimentMultiplier = -1;\n }\n\n const weight = (result.confidence / 100) * (1 + Math.log10(result.engagement + 1));\n totalWeightedScore += sentimentMultiplier * weight;\n totalWeight += weight;\n totalConfidence += result.confidence;\n }\n\n const llmSentiment = totalWeight > 0 ? Math.max(-1, Math.min(1, totalWeightedScore / totalWeight)) : 0;\n const llmConfidence = totalTweets > 0 ? totalConfidence / totalTweets : 0;\n\n return {\n llmSentiment,\n llmConfidence,\n llmBullishPercent,\n llmBearishPercent,\n };\n\n } catch (error) {\n console.warn(`LLM sentiment analysis failed: ${error}`);\n return null;\n }\n }\n\n /** Analyze tweets to calculate sentiment metrics. */\n private async analyzeTweets(tweets: TwitterTweet[]): Promise<TwitterSentimentData> {\n const now = Date.now();\n const oneHourAgo = now - 60 * 60 * 1000;\n\n // Separate recent (last hour) vs all tweets\n const recentTweets = tweets.filter(t => new Date(t.created_at).getTime() > oneHourAgo);\n const allTweets = tweets;\n\n // Calculate mention volume (recent vs average)\n const recentVolume = recentTweets.length;\n const avgHourlyVolume = allTweets.length / 24; // 24 hours of data\n const volumeSpike = avgHourlyVolume > 0 ? recentVolume / avgHourlyVolume : 1;\n\n // Analyze sentiment for all tweets (keyword-based)\n let bullishCount = 0;\n let bearishCount = 0;\n let totalEngagement = 0;\n let engagementWeightedBullish = 0;\n let engagementWeightedBearish = 0;\n\n for (const tweet of allTweets) {\n const text = tweet.text.toLowerCase();\n const engagement = tweet.public_metrics.like_count +\n tweet.public_metrics.retweet_count +\n tweet.public_metrics.reply_count;\n\n let tweetBullish = 0;\n let tweetBearish = 0;\n\n // Count sentiment words\n for (const word of BULLISH_WORDS) {\n if (text.includes(word)) tweetBullish++;\n }\n for (const word of BEARISH_WORDS) {\n if (text.includes(word)) tweetBearish++;\n }\n\n if (tweetBullish > tweetBearish) {\n bullishCount++;\n engagementWeightedBullish += engagement;\n } else if (tweetBearish > tweetBullish) {\n bearishCount++;\n engagementWeightedBearish += engagement;\n }\n\n totalEngagement += engagement;\n }\n\n // Calculate keyword-based sentiment scores\n const totalAnalyzed = bullishCount + bearishCount;\n const sentimentScore = totalAnalyzed > 0\n ? Math.max(-1, Math.min(1, (bullishCount - bearishCount) / totalAnalyzed))\n : 0;\n\n const engagementWeightedSentiment = totalEngagement > 0\n ? Math.max(-1, Math.min(1, (engagementWeightedBullish - engagementWeightedBearish) / totalEngagement))\n : sentimentScore;\n\n // Try LLM sentiment analysis\n const llmResult = await this.analyzeSentimentWithLLM(allTweets);\n\n const baseData: TwitterSentimentData = {\n mentionVolume: recentVolume,\n sentimentScore,\n engagementWeightedSentiment,\n volumeSpike,\n tweetCount: allTweets.length,\n };\n\n // Add LLM results if available\n if (llmResult) {\n return {\n ...baseData,\n llmSentiment: llmResult.llmSentiment,\n llmConfidence: llmResult.llmConfidence,\n llmBullishPercent: llmResult.llmBullishPercent,\n llmBearishPercent: llmResult.llmBearishPercent,\n };\n }\n\n return baseData;\n }\n\n /** Read cached sentiment data. */\n private async readCache(tokenId: string): Promise<TwitterSentimentData | null> {\n try {\n const cacheFile = join(this.cacheDir, `twitter-${tokenId}.json`);\n const raw = await readFile(cacheFile, 'utf-8');\n const cached = JSON.parse(raw) as { ts: number; data: TwitterSentimentData };\n\n if (Date.now() - cached.ts < this.cacheTTL) {\n return cached.data;\n }\n } catch {\n // No cache or invalid cache\n }\n return null;\n }\n\n /** Write sentiment data to cache. */\n private async writeCache(tokenId: string, data: TwitterSentimentData): Promise<void> {\n try {\n await mkdir(this.cacheDir, { recursive: true });\n const cacheFile = join(this.cacheDir, `twitter-${tokenId}.json`);\n await writeFile(cacheFile, JSON.stringify({ ts: Date.now(), data }), 'utf-8');\n } catch {\n // Cache write failure is non-fatal\n }\n }\n\n /** Get sentiment data with fallback to token symbol for unknown tokens. */\n async getSentimentWithSymbol(tokenId: string, tokenSymbol?: string): Promise<TwitterSentimentData | null> {\n // Try with known token mapping first\n let result = await this.getSentiment(tokenId);\n\n // If no result and we have a symbol, try searching with symbol\n if (!result && tokenSymbol) {\n const symbolQuery = `$${tokenSymbol.toUpperCase()} OR #${tokenSymbol.toLowerCase()}`;\n try {\n const tweets = await this.fetchTweets(symbolQuery, new Date(Date.now() - 24 * 60 * 60 * 1000));\n if (tweets && tweets.length > 0) {\n result = await this.analyzeTweets(tweets);\n await this.writeCache(tokenId, result);\n }\n } catch {\n // Symbol-based search failed\n }\n }\n\n return result;\n }\n}\n","/**\n * Generic Python subprocess bridge for Fincept scripts.\n *\n * Calls vendored Python scripts in cli/scripts/fincept/ as one-shot\n * subprocesses, parses their JSON stdout, and returns typed results.\n */\n\nimport { execFile } from \"node:child_process\";\nimport { join, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { existsSync } from \"node:fs\";\n\nconst __dirname = fileURLToPath(new URL(\".\", import.meta.url));\n\n/**\n * Resolve the fincept scripts directory. Works both in source mode\n * (cli/src/providers/fincept/ → ../../.. → cli/scripts/fincept) and\n * bundled mode (cli/dist/ → .. → cli/scripts/fincept).\n */\nfunction resolveScriptsDir(): string {\n // Try source layout first: cli/src/providers/fincept/ → cli/scripts/fincept\n const fromSource = join(__dirname, \"..\", \"..\", \"..\", \"scripts\", \"fincept\");\n if (existsSync(fromSource)) return fromSource;\n // Bundled layout: cli/dist/ → cli/scripts/fincept\n const fromDist = join(__dirname, \"..\", \"scripts\", \"fincept\");\n if (existsSync(fromDist)) return fromDist;\n // Fallback: relative to cwd (typically repo root or cli/)\n const fromCwd = join(process.cwd(), \"scripts\", \"fincept\");\n if (existsSync(fromCwd)) return fromCwd;\n const fromCwdCli = join(process.cwd(), \"cli\", \"scripts\", \"fincept\");\n if (existsSync(fromCwdCli)) return fromCwdCli;\n // Last resort\n return fromSource;\n}\n\n/** Absolute path to the vendored fincept scripts directory. */\nexport const FINCEPT_SCRIPTS_DIR = resolveScriptsDir();\n\nexport interface BridgeResult<T = unknown> {\n ok: boolean;\n data?: T;\n error?: string;\n latencyMs: number;\n}\n\n// ---------------------------------------------------------------------------\n// In-memory cache\n// ---------------------------------------------------------------------------\n\ninterface CacheEntry {\n ts: number;\n data: unknown;\n}\n\nconst cache = new Map<string, CacheEntry>();\n\n/** Clear the in-memory bridge cache (useful in tests). */\nexport function clearFinceptCache(): void {\n cache.clear();\n}\n\n// ---------------------------------------------------------------------------\n// Core bridge function\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\nconst MAX_BUFFER = 10 * 1024 * 1024; // 10 MB\n\n/**\n * Run a Fincept Python script and return its parsed JSON output.\n *\n * @param script - Filename of the script inside FINCEPT_SCRIPTS_DIR (e.g. \"blockchain_com_data.py\")\n * @param args - CLI arguments to pass after the script path\n * @param timeoutMs - Subprocess timeout in milliseconds (default 30 s)\n * @param cacheTtlMs - How long to cache results in memory (0 = no cache)\n */\nexport async function callFincept<T = unknown>(\n script: string,\n args: string[] = [],\n timeoutMs: number = DEFAULT_TIMEOUT_MS,\n cacheTtlMs: number = 0,\n): Promise<BridgeResult<T>> {\n const cacheKey = `${script}:${args.join(\":\")}`;\n\n // Check cache\n if (cacheTtlMs > 0) {\n const entry = cache.get(cacheKey);\n if (entry && Date.now() - entry.ts < cacheTtlMs) {\n return { ok: true, data: entry.data as T, latencyMs: 0 };\n }\n }\n\n // Validate script name — prevent path traversal\n if (script.includes('/') || script.includes('\\\\') || !script.endsWith('.py')) {\n return { ok: false, error: `Invalid script name: ${script}`, latencyMs: 0 };\n }\n const scriptPath = join(FINCEPT_SCRIPTS_DIR, script);\n const t0 = Date.now();\n\n return new Promise<BridgeResult<T>>((resolve) => {\n execFile(\n \"python3\",\n [scriptPath, ...args],\n {\n timeout: timeoutMs,\n maxBuffer: MAX_BUFFER,\n env: process.env,\n },\n (error, stdout, _stderr) => {\n const latencyMs = Date.now() - t0;\n\n // Subprocess was killed (timeout)\n if (error && \"killed\" in error && error.killed) {\n resolve({\n ok: false,\n error: `Script \"${script}\" timed out after ${timeoutMs}ms`,\n latencyMs,\n });\n return;\n }\n\n // Non-zero exit or other spawn error\n if (error) {\n resolve({\n ok: false,\n error: `Script \"${script}\" failed: ${error.message}`,\n latencyMs,\n });\n return;\n }\n\n // Empty stdout\n const raw = stdout.trim();\n if (!raw) {\n resolve({\n ok: false,\n error: `Script \"${script}\" returned empty output`,\n latencyMs,\n });\n return;\n }\n\n // Parse JSON\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n resolve({\n ok: false,\n error: `Script \"${script}\" returned invalid JSON: ${raw.slice(0, 200)}`,\n latencyMs,\n });\n return;\n }\n\n // Check for error field in response\n if (\n parsed !== null &&\n typeof parsed === \"object\" &&\n \"error\" in parsed &&\n typeof (parsed as Record<string, unknown>).error === \"string\"\n ) {\n resolve({\n ok: false,\n error: (parsed as Record<string, string>).error,\n latencyMs,\n });\n return;\n }\n\n // Success — cache if requested\n if (cacheTtlMs > 0) {\n cache.set(cacheKey, { ts: Date.now(), data: parsed });\n }\n\n resolve({ ok: true, data: parsed as T, latencyMs });\n },\n );\n });\n}\n","/**\n * Messari fundamentals wrapper via Fincept bridge.\n *\n * Provides supply metrics, revenue, and developer activity for tokens.\n */\n\nimport { callFincept } from \"./bridge.js\";\n\nconst CACHE_TTL = 60 * 60 * 1_000; // 1 hour\n\nexport interface MessariFundamentals {\n marketCap: number;\n supply: {\n circulating: number;\n max: number;\n percentCirculating: number;\n };\n revenueUsd24h: number;\n revenueGrowth7d: number;\n developerActivity: number;\n}\n\n/**\n * Map CoinGecko IDs to Messari slugs where they differ.\n * Most IDs are identical — only overrides listed here.\n */\nconst CG_TO_MESSARI: Record<string, string> = {\n \"avalanche-2\": \"avalanche\",\n \"worldcoin-wld\": \"worldcoin\",\n \"pudgy-penguins\": \"pudgy-penguins\",\n \"fetch-ai\": \"fetch-ai\",\n};\n\n/** Messari API response shape (subset we care about). */\ninterface MessariResponse {\n data: {\n id: string;\n metrics: {\n market_data: {\n price_usd: number;\n market_cap: { current_marketcap_usd: number };\n };\n supply: {\n circulating: number;\n max: number;\n y_2050: number;\n };\n blockchain_stats_24_hours: {\n revenue_usd: number;\n };\n developer_activity: {\n commits_last_3_months: number;\n };\n };\n };\n}\n\n/**\n * Fetch Messari fundamentals for a token via the Fincept bridge.\n *\n * @param tokenId - CoinGecko token ID (e.g. \"ethereum\", \"avalanche-2\")\n * @returns Parsed fundamentals or null if unavailable\n */\nexport async function getMessariFundamentals(\n tokenId: string,\n): Promise<MessariFundamentals | null> {\n const slug = CG_TO_MESSARI[tokenId] ?? tokenId;\n\n const result = await callFincept<MessariResponse>(\n \"messari_data.py\",\n [\"metrics\", slug],\n 30_000,\n CACHE_TTL,\n );\n\n if (!result.ok || !result.data?.data?.metrics) {\n return null;\n }\n\n const m = result.data.data.metrics;\n const circulating = m.supply?.circulating ?? 0;\n const max = m.supply?.max ?? 0;\n\n return {\n marketCap: m.market_data?.market_cap?.current_marketcap_usd ?? 0,\n supply: {\n circulating,\n max,\n percentCirculating: max > 0 ? circulating / max : 0,\n },\n revenueUsd24h: m.blockchain_stats_24_hours?.revenue_usd ?? 0,\n revenueGrowth7d: 0, // Messari doesn't expose this directly\n developerActivity: m.developer_activity?.commits_last_3_months ?? 0,\n };\n}\n","/**\n * Blockchain.com BTC network stats wrapper.\n *\n * Fetches hash rate, difficulty, mempool size, miner revenue, market price,\n * and transaction count via the Fincept Python bridge.\n */\n\nimport { callFincept } from \"./bridge.js\";\n\nconst CACHE_TTL = 30 * 60 * 1000; // 30 minutes\n\nexport interface BtcNetworkStats {\n hashRate: number;\n difficulty: number;\n mempoolSize: number;\n minerRevenueBtc: number;\n marketPriceUsd: number;\n transactionCount: number;\n}\n\ninterface RawStats {\n hash_rate: number;\n difficulty: number;\n mempool_size: number;\n miners_revenue_btc: number;\n market_price_usd: number;\n n_tx: number;\n}\n\n/**\n * Fetch BTC network statistics from Blockchain.com via the Fincept bridge.\n * Returns null if the script fails or returns invalid data.\n */\nexport async function getBtcNetworkStats(): Promise<BtcNetworkStats | null> {\n const result = await callFincept<RawStats>(\n \"blockchain_com_data.py\",\n [\"stats\"],\n 30_000,\n CACHE_TTL,\n );\n\n if (!result.ok || !result.data) {\n return null;\n }\n\n const raw = result.data;\n return {\n hashRate: raw.hash_rate ?? 0,\n difficulty: raw.difficulty ?? 0,\n mempoolSize: raw.mempool_size ?? 0,\n minerRevenueBtc: raw.miners_revenue_btc ?? 0,\n marketPriceUsd: raw.market_price_usd ?? 0,\n transactionCount: raw.n_tx ?? 0,\n };\n}\n","/**\n * Polymarket/Manifold prediction market wrapper.\n *\n * Fetches crypto-relevant prediction markets via the Fincept Python bridge\n * and exposes them as typed PredictionMarket objects.\n */\n\nimport { callFincept } from \"./bridge.js\";\n\nconst CACHE_TTL = 5 * 60 * 1000; // 5 minutes\n\nexport interface PredictionMarket {\n question: string;\n probability: number;\n volume: number;\n}\n\nconst CRYPTO_KEYWORDS = [\n \"bitcoin\",\n \"btc\",\n \"ethereum\",\n \"eth\",\n \"crypto\",\n \"sec\",\n \"etf\",\n \"fed\",\n \"rate\",\n \"inflation\",\n \"regulation\",\n \"stablecoin\",\n \"defi\",\n];\n\ninterface RawMarket {\n question: string;\n outcomePrices: string[];\n probability: number;\n volume: number;\n active: boolean;\n closed: boolean;\n}\n\n/**\n * Fetch crypto-relevant prediction markets from Polymarket/Manifold.\n * Returns up to 10 active markets whose question matches a crypto keyword.\n */\nexport async function getCryptoPredictions(): Promise<PredictionMarket[]> {\n try {\n const result = await callFincept<RawMarket[]>(\n \"polymarket.py\",\n [\"get_markets\", \"50\"],\n 30_000,\n CACHE_TTL,\n );\n\n if (!result.ok || !result.data) {\n return [];\n }\n\n const markets = result.data\n .filter((m) => {\n if (!m.active || m.closed) return false;\n const q = m.question.toLowerCase();\n return CRYPTO_KEYWORDS.some((kw) => q.includes(kw));\n })\n .map((m): PredictionMarket => {\n const probability =\n m.outcomePrices && m.outcomePrices.length > 0\n ? parseFloat(m.outcomePrices[0])\n : m.probability;\n return {\n question: m.question,\n probability: isNaN(probability) ? m.probability : probability,\n volume: m.volume,\n };\n })\n .slice(0, 10);\n\n return markets;\n } catch {\n return [];\n }\n}\n","/**\n * CryptoCompare social / news wrapper via Fincept bridge.\n */\n\nimport { callFincept } from './bridge.js';\n\nconst NEWS_CACHE_TTL = 10 * 60 * 1_000; // 10 minutes\n\n/** Map CoinGecko token IDs → CryptoCompare symbols. */\nconst CC_SYMBOL_MAP: Record<string, string> = {\n bitcoin: 'BTC',\n ethereum: 'ETH',\n solana: 'SOL',\n aave: 'AAVE',\n uniswap: 'UNI',\n chainlink: 'LINK',\n ripple: 'XRP',\n dogecoin: 'DOGE',\n polkadot: 'DOT',\n avalanche: 'AVAX',\n arbitrum: 'ARB',\n hyperliquid: 'HYPE',\n zcash: 'ZEC',\n fartcoin: 'FARTCOIN',\n pepe: 'PEPE',\n cardano: 'ADA',\n ethena: 'ENA',\n 'worldcoin-wld': 'WLD',\n bittensor: 'TAO',\n sui: 'SUI',\n near: 'NEAR',\n aptos: 'APT',\n};\n\nexport interface SocialData {\n socialVolume24h: number;\n socialVolumeSpike: number;\n newsCount24h: number;\n topNewsSentiment: number;\n}\n\n/**\n * Fetch CryptoCompare news and derive social-data metrics for a token.\n * Returns null when news data is unavailable.\n */\nexport async function getSocialData(tokenId: string): Promise<SocialData | null> {\n const symbol = CC_SYMBOL_MAP[tokenId] ?? tokenId.toUpperCase();\n\n try {\n const newsResult = await callFincept(\n 'cryptocompare_data.py',\n ['news'],\n 30_000,\n NEWS_CACHE_TTL,\n );\n\n const rawData = newsResult.ok ? (newsResult.data as Record<string, unknown>) : undefined;\n // Script returns { articles: [...] } (lowercase), raw API returns { Data: [...] } (uppercase)\n const articles: Array<{ title?: string; categories?: string; tags?: string }> =\n (rawData?.articles as Array<{ title?: string; categories?: string; tags?: string }>)\n ?? (rawData?.Data as Array<{ title?: string; categories?: string; tags?: string }>)\n ?? [];\n\n const lowerSymbol = symbol.toLowerCase();\n const lowerTokenId = tokenId.toLowerCase();\n\n const matchingArticles = articles.filter((a) => {\n const haystack = [a.title, a.categories, a.tags]\n .filter(Boolean)\n .join(' ')\n .toLowerCase();\n return haystack.includes(lowerSymbol) || haystack.includes(lowerTokenId);\n });\n\n return {\n socialVolume24h: matchingArticles.length,\n socialVolumeSpike: 1.0, // baseline — no historical comparison available\n newsCount24h: matchingArticles.length,\n topNewsSentiment: 0, // CryptoCompare doesn't have sentiment scores\n };\n } catch {\n return null;\n }\n}\n","/**\n * CryptoCompare candle wrapper — fallback candle source replacing CoinGecko OHLC.\n * Hyperliquid remains the primary candle source.\n */\n\nimport { callFincept } from \"./bridge.js\";\nimport type { Candle } from \"../../agent/technical.js\";\n\nconst OHLCV_CACHE_TTL = 2 * 60 * 60 * 1000; // 2 hours\n\n/** CoinGecko token ID → CryptoCompare symbol */\nconst CC_SYMBOL: Record<string, string> = {\n bitcoin: \"BTC\",\n ethereum: \"ETH\",\n solana: \"SOL\",\n aave: \"AAVE\",\n uniswap: \"UNI\",\n chainlink: \"LINK\",\n ripple: \"XRP\",\n dogecoin: \"DOGE\",\n polkadot: \"DOT\",\n avalanche: \"AVAX\",\n arbitrum: \"ARB\",\n hyperliquid: \"HYPE\",\n zcash: \"ZEC\",\n fartcoin: \"FARTCOIN\",\n pepe: \"PEPE\",\n cardano: \"ADA\",\n ethena: \"ENA\",\n \"worldcoin-wld\": \"WLD\",\n bittensor: \"TAO\",\n sui: \"SUI\",\n near: \"NEAR\",\n aptos: \"APT\",\n \"pudgy-penguins\": \"PENGU\",\n blur: \"BLUR\",\n \"fetch-ai\": \"FET\",\n};\n\ninterface CCBar {\n time: number;\n open: number;\n high: number;\n low: number;\n close: number;\n volumefrom: number;\n volumeto: number;\n}\n\ninterface CCResponse {\n ohlcv: CCBar[];\n}\n\n/**\n * Fetch hourly candles from CryptoCompare via Fincept bridge and downsample to 4h.\n * Returns null if the token is unmapped or data is insufficient.\n */\nexport async function getCryptoCompareCandles(\n tokenId: string,\n limit: number = 180,\n): Promise<Candle[] | null> {\n const symbol = CC_SYMBOL[tokenId];\n if (!symbol) return null;\n\n const result = await callFincept<CCResponse>(\n \"cryptocompare_data.py\",\n [\"hourly\", symbol, \"USD\", String(limit * 4)],\n 30_000,\n OHLCV_CACHE_TTL,\n );\n\n if (!result.ok || !result.data) return null;\n\n const bars = result.data.ohlcv;\n if (!bars || bars.length < 10) return null;\n\n // Downsample hourly bars to 4h candles\n const candles: Candle[] = [];\n for (let i = 0; i + 3 < bars.length; i += 4) {\n const chunk = bars.slice(i, i + 4);\n candles.push({\n timestamp: chunk[0]!.time * 1000,\n open: chunk[0]!.open,\n high: Math.max(...chunk.map((b) => b.high)),\n low: Math.min(...chunk.map((b) => b.low)),\n close: chunk[chunk.length - 1]!.close,\n volume: chunk.reduce((sum, b) => sum + b.volumeto, 0),\n });\n }\n\n return candles.length > 0 ? candles : null;\n}\n","/**\n * Kronos volatility forecaster via Python subprocess.\n *\n * Runs the Kronos-mini foundation model (4.1M params) on CPU to predict\n * N future price paths from OHLCV candles, then computes volatility metrics\n * from the path spread.\n *\n * Latency: ~2.3s for 5 paths on CPU (tested on 2-core VPS).\n * Memory: ~350MB peak (model + PyTorch).\n * Cache: 1 hour (vol forecasts don't need per-cycle updates).\n */\n\nimport { execFile } from \"node:child_process\";\nimport { join } from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { FINCEPT_SCRIPTS_DIR } from \"./bridge.js\";\nimport type { Candle } from \"../../agent/technical.js\";\n\n/** Kronos needs PyTorch — use the dedicated venv if it exists,\n * otherwise fall back to system python3. */\nconst KRONOS_VENV_PYTHON = join(process.env.HOME ?? \"/home/ana\", \".sherwood\", \"kronos-venv\", \"bin\", \"python3\");\nconst KRONOS_PYTHON = existsSync(KRONOS_VENV_PYTHON) ? KRONOS_VENV_PYTHON : \"python3\";\n\nconst CACHE_TTL = 60 * 60 * 1000; // 1 hour\nconst TIMEOUT_MS = 30_000;\n\nexport interface KronosVolForecast {\n /** Annualized volatility from Monte Carlo path spread. */\n predictedVolatility: number;\n /** Per-candle (4h) volatility as a fraction. */\n predictedVol4h: number;\n /** Directional bias from mean path: -1 (bearish) to +1 (bullish). */\n directionalBias: number;\n /** % spread between worst and best path at prediction horizon. */\n pathSpreadPct: number;\n /** Number of candles predicted. */\n predictionHorizon: number;\n /** Number of Monte Carlo paths generated. */\n sampleCount: number;\n /** Last input close price. */\n lastClose: number;\n /** Mean predicted close at horizon. */\n meanPredictedClose: number;\n /** Inference time in milliseconds. */\n inferenceTimeMs: number;\n}\n\ninterface CacheEntry {\n ts: number;\n data: KronosVolForecast;\n}\n\nconst cache = new Map<string, CacheEntry>();\n\n/**\n * Run Kronos volatility forecast on OHLCV candles.\n *\n * @param tokenId - Token identifier (used for caching)\n * @param candles - Historical OHLCV candles (need at least 30, ideally 200)\n * @param samples - Number of Monte Carlo paths (default 5)\n * @param predLen - Number of future candles to predict (default 24)\n * @returns Volatility forecast or null on failure\n */\nexport async function getKronosVolForecast(\n tokenId: string,\n candles: Candle[],\n samples: number = 5,\n predLen: number = 24,\n): Promise<KronosVolForecast | null> {\n // Check cache\n const cacheKey = `kronos:${tokenId}`;\n const cached = cache.get(cacheKey);\n if (cached && Date.now() - cached.ts < CACHE_TTL) {\n return cached.data;\n }\n\n if (!candles || candles.length < 30) return null;\n\n // Prepare input JSON\n const input = JSON.stringify({\n candles: candles.map((c) => ({\n timestamp: c.timestamp,\n open: c.open,\n high: c.high,\n low: c.low,\n close: c.close,\n volume: c.volume,\n })),\n });\n\n const scriptPath = join(FINCEPT_SCRIPTS_DIR, \"kronos_predict.py\");\n\n return new Promise<KronosVolForecast | null>((resolve) => {\n const proc = execFile(\n KRONOS_PYTHON,\n [scriptPath, \"--samples\", String(samples), \"--pred-len\", String(predLen)],\n {\n timeout: TIMEOUT_MS,\n maxBuffer: 5 * 1024 * 1024,\n env: { ...process.env },\n },\n (error, stdout, stderr) => {\n if (error) {\n console.error(` [kronos] Inference failed: ${error.message}`);\n resolve(null);\n return;\n }\n\n try {\n const result = JSON.parse(stdout.trim());\n if (result.error) {\n console.error(` [kronos] ${result.error}`);\n resolve(null);\n return;\n }\n\n const forecast = result as KronosVolForecast;\n cache.set(cacheKey, { ts: Date.now(), data: forecast });\n resolve(forecast);\n } catch {\n console.error(` [kronos] Invalid JSON output`);\n resolve(null);\n }\n },\n );\n\n // Write candle data to stdin\n if (proc.stdin) {\n proc.stdin.write(input);\n proc.stdin.end();\n }\n });\n}\n","/**\n * DefiLlama data wrapper.\n *\n * Fetches protocol TVL and yield pool data via the Fincept Python bridge\n * (defillama_data.py).\n */\n\nimport { callFincept } from \"./bridge.js\";\n\nconst TVL_CACHE_TTL = 30 * 60 * 1000; // 30 minutes\n\ninterface DefiLlamaTvlResponse {\n tvl?: number;\n name?: string;\n [key: string]: unknown;\n}\n\ninterface DefiLlamaYieldsResponse {\n data?: Array<Record<string, unknown>>;\n}\n\nexport interface YieldPool {\n pool: string;\n chain: string;\n project: string;\n symbol: string;\n tvlUsd: number;\n apy: number;\n}\n\n/**\n * Fetch protocol TVL from DefiLlama.\n *\n * @param tokenId - CoinGecko-style token ID (e.g. \"aave\", \"uniswap\")\n * @returns TVL in USD, or null on failure\n */\nexport async function getProtocolTvl(\n tokenId: string,\n): Promise<number | null> {\n const res = await callFincept<DefiLlamaTvlResponse>(\n \"defillama_data.py\",\n [\"protocol\", tokenId],\n 30_000,\n TVL_CACHE_TTL,\n );\n\n if (!res.ok || !res.data) return null;\n return res.data.tvl ?? null;\n}\n\n/**\n * Fetch top yield pools from DefiLlama.\n *\n * @param limit - Maximum number of pools to return (default 20)\n * @returns Array of yield pool entries, or null on failure\n */\nexport async function getYieldPools(\n limit: number = 20,\n): Promise<YieldPool[] | null> {\n const res = await callFincept<DefiLlamaYieldsResponse>(\n \"defillama_data.py\",\n [\"yields\"],\n 30_000,\n 60 * 60 * 1000, // 1 hour cache\n );\n\n if (!res.ok || !res.data?.data) return null;\n\n return res.data.data\n .slice(0, limit)\n .map((entry) => ({\n pool: String(entry.pool ?? \"\"),\n chain: String(entry.chain ?? \"\"),\n project: String(entry.project ?? \"\"),\n symbol: String(entry.symbol ?? \"\"),\n tvlUsd: Number(entry.tvlUsd ?? 0),\n apy: Number(entry.apy ?? 0),\n }));\n}\n","/**\n * DexScreener data wrapper.\n *\n * Searches DEX pairs and fetches boosted tokens via the Fincept Python\n * bridge (dexscreener_data.py).\n */\n\nimport { callFincept } from \"./bridge.js\";\n\nconst CACHE_TTL = 5 * 60 * 1000; // 5 minutes\n\nexport interface FinceptDexPair {\n chainId: string;\n baseToken: { address: string; name: string; symbol: string };\n quoteToken: { address: string; name: string; symbol: string };\n priceUsd: string;\n volume: { h24: number };\n liquidity: { usd: number };\n priceChange: { h24: number };\n txns: { h24: { buys: number; sells: number } };\n}\n\ninterface DexSearchResponse {\n pairs?: FinceptDexPair[];\n}\n\ninterface TokenBoost {\n tokenAddress: string;\n chainId: string;\n amount: number;\n}\n\ninterface DexBoostedResponse {\n data?: Array<Record<string, unknown>>;\n}\n\n/**\n * Search DexScreener for trading pairs matching a query.\n *\n * @param query - Search string (token name, symbol, or address)\n * @returns Array of matching pairs, or empty array on failure\n */\nexport async function searchDexPairs(\n query: string,\n): Promise<FinceptDexPair[]> {\n const res = await callFincept<DexSearchResponse>(\n \"dexscreener_data.py\",\n [\"search\", query],\n 30_000,\n CACHE_TTL,\n );\n\n if (!res.ok || !res.data?.pairs) return [];\n return res.data.pairs;\n}\n\n/**\n * Fetch currently boosted tokens from DexScreener.\n *\n * @returns Array of boosted token entries, or empty array on failure\n */\nexport async function getTokenBoosts(): Promise<TokenBoost[]> {\n const res = await callFincept<DexBoostedResponse>(\n \"dexscreener_data.py\",\n [\"boosted\"],\n 30_000,\n CACHE_TTL,\n );\n\n if (!res.ok || !res.data?.data) return [];\n\n return res.data.data.map((entry) => ({\n tokenAddress: String(entry.tokenAddress ?? \"\"),\n chainId: String(entry.chainId ?? \"\"),\n amount: Number(entry.amount ?? 0),\n }));\n}\n","/**\n * Signal logger — appends every token analysis to a JSONL file for tracking.\n * Fire-and-forget: never blocks analysis, never crashes on failure.\n */\n\nimport { appendFile, stat, rename, mkdir } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport type { TokenAnalysis } from './index.js';\nimport type { ScoringWeights } from './scoring.js';\nimport type { JudgeVerdict } from './judge.js';\n\nconst SIGNAL_DIR = join(homedir(), '.sherwood', 'agent');\nconst SIGNAL_FILE = join(SIGNAL_DIR, 'signal-history.jsonl');\nconst MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB\n\nexport interface SignalLogEntry {\n timestamp: string;\n tokenId: string;\n tokenSymbol: string;\n price: number;\n decision: 'STRONG_BUY' | 'BUY' | 'HOLD' | 'SELL' | 'STRONG_SELL';\n score: number;\n confidence: number;\n signals: { name: string; value: number; confidence: number }[];\n regime: string;\n btcCorrelation: number;\n weights: { smartMoney: number; technical: number; sentiment: number; onchain: number; fundamental: number; event: number };\n // LLM judge fields (v1 — additive, ignored by older consumers)\n judgeVerdict?: \"confirm\" | \"veto\";\n judgeReasoning?: string;\n judgeConfidence?: number;\n preJudgeAction?: string;\n preJudgeScore?: number;\n judgeLatencyMs?: number;\n judgeCached?: boolean;\n}\n\nexport interface JudgeLogData {\n verdict: JudgeVerdict;\n preJudgeAction: string;\n preJudgeScore: number;\n latencyMs: number;\n cached: boolean;\n}\n\n/**\n * Log a signal entry after token analysis completes.\n * Fire-and-forget — call without await. Errors are swallowed with a warning.\n */\nexport function logSignal(\n analysis: TokenAnalysis,\n price: number,\n weights: ScoringWeights,\n judgeData?: JudgeLogData,\n): void {\n // Fire-and-forget — do not await\n _writeSignal(analysis, price, weights, judgeData).catch((err) => {\n console.error(`[signal-logger] Warning: failed to log signal: ${(err as Error).message}`);\n });\n}\n\nasync function _writeSignal(\n analysis: TokenAnalysis,\n price: number,\n weights: ScoringWeights,\n judgeData?: JudgeLogData,\n): Promise<void> {\n await mkdir(SIGNAL_DIR, { recursive: true });\n\n // Auto-rotate if file exceeds 10MB\n try {\n const stats = await stat(SIGNAL_FILE);\n if (stats.size > MAX_FILE_SIZE) {\n const dateStr = new Date().toISOString().slice(0, 10);\n const rotatedName = join(SIGNAL_DIR, `signal-history-${dateStr}.jsonl`);\n await rename(SIGNAL_FILE, rotatedName);\n }\n } catch {\n // File doesn't exist yet, that's fine\n }\n\n const entry: SignalLogEntry = {\n timestamp: new Date().toISOString(),\n tokenId: analysis.token,\n tokenSymbol: analysis.token.toUpperCase().slice(0, 6),\n price,\n decision: analysis.decision.action,\n score: analysis.decision.score,\n confidence: analysis.decision.confidence,\n signals: analysis.decision.signals.map((s) => ({\n name: s.name,\n value: s.value,\n confidence: s.confidence,\n })),\n regime: analysis.regime?.regime ?? 'unknown',\n btcCorrelation: analysis.correlation?.btcScore ?? 0,\n weights: {\n smartMoney: weights.smartMoney,\n technical: weights.technical,\n sentiment: weights.sentiment,\n onchain: weights.onchain,\n fundamental: weights.fundamental,\n event: weights.event,\n },\n // Judge fields (additive — omitted when judge is off)\n ...(judgeData ? {\n judgeVerdict: judgeData.verdict.verdict,\n judgeReasoning: judgeData.verdict.reasoning,\n judgeConfidence: judgeData.verdict.confidence,\n preJudgeAction: judgeData.preJudgeAction,\n preJudgeScore: judgeData.preJudgeScore,\n judgeLatencyMs: judgeData.latencyMs,\n judgeCached: judgeData.cached,\n } : {}),\n };\n\n const line = JSON.stringify(entry) + '\\n';\n await appendFile(SIGNAL_FILE, line, 'utf-8');\n}\n\n/** Get the path to the signal history file. */\nexport function getSignalFilePath(): string {\n return SIGNAL_FILE;\n}\n\n/** Get the signal directory path. */\nexport function getSignalDir(): string {\n return SIGNAL_DIR;\n}\n","/**\n * LLM Judge — confirm or veto borderline trade decisions using Claude.\n *\n * V1 scope: confirm + veto only. No score overrides, no direction flips.\n * Fallback is always pass-through — judge can never block a cycle.\n */\n\nimport { createHash } from \"node:crypto\";\nimport { readFile, writeFile, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport chalk from \"chalk\";\nimport { chatCompletion } from \"../lib/venice.js\";\nimport { getVeniceApiKey } from \"../lib/config.js\";\nimport type { TradeDecision } from \"./scoring.js\";\nimport type { TechnicalSignals } from \"./technical.js\";\nimport type { PortfolioState } from \"./risk.js\";\n\n// ── Types ──\n\nexport interface JudgeVerdict {\n verdict: \"confirm\" | \"veto\";\n reasoning: string;\n risks: string[];\n confidence: number;\n}\n\nexport interface JudgeConfig {\n enabled: boolean;\n model: string;\n topN: number;\n scoreBand: [number, number];\n timeoutMs: number;\n cacheTtlMs: number;\n}\n\nexport const DEFAULT_JUDGE_CONFIG: JudgeConfig = {\n enabled: false,\n model: \"llama-3.3-70b\",\n topN: 3,\n scoreBand: [0.10, 0.50],\n timeoutMs: 8_000,\n cacheTtlMs: 900_000, // 15 min\n};\n\n/** Context provided to the judge for a single token decision. */\nexport interface JudgeContext {\n tokenId: string;\n currentPrice: number;\n decision: TradeDecision;\n technicalSignals?: TechnicalSignals;\n fearAndGreed?: number;\n sentimentZScore?: number;\n fundingRate?: number;\n regime?: string;\n btcRegime?: string;\n btcBias?: string;\n suppressionFactor?: number;\n portfolio: {\n openCount: number;\n cashPct: number;\n hasPositionThisToken: boolean;\n inStopCooldown: boolean;\n dailyPnlPct: number;\n };\n recentCloses?: number[];\n}\n\n// ── Fallback ──\n\nconst FALLBACK_VERDICT: JudgeVerdict = {\n verdict: \"confirm\",\n reasoning: \"fallback\",\n risks: [],\n confidence: 0,\n};\n\n// ── Cache ──\n\nconst CACHE_DIR = join(homedir(), \".sherwood\", \"agent\", \"cache\");\n\nfunction cacheKey(ctx: JudgeContext): string {\n const priceBucket = Math.round(ctx.currentPrice / (ctx.currentPrice * 0.005));\n const signalTuples = ctx.decision.signals\n .map((s) => `${s.name}:${Math.sign(s.value)}`)\n .sort()\n .join(\",\");\n const raw = `${ctx.tokenId}:${priceBucket}:${signalTuples}:${ctx.regime ?? \"unknown\"}`;\n return createHash(\"sha256\").update(raw).digest(\"hex\").slice(0, 16);\n}\n\nasync function readCache(key: string, ttlMs: number): Promise<JudgeVerdict | undefined> {\n try {\n const path = join(CACHE_DIR, `judge-${key}.json`);\n const data = JSON.parse(await readFile(path, \"utf-8\"));\n if (Date.now() - (data.cachedAt ?? 0) < ttlMs) {\n return data.verdict as JudgeVerdict;\n }\n } catch {\n // Cache miss or read error — fine\n }\n return undefined;\n}\n\nasync function writeCache(key: string, verdict: JudgeVerdict): Promise<void> {\n try {\n await mkdir(CACHE_DIR, { recursive: true });\n const path = join(CACHE_DIR, `judge-${key}.json`);\n await writeFile(path, JSON.stringify({ cachedAt: Date.now(), verdict }), \"utf-8\");\n } catch {\n // Write failure is non-fatal\n }\n}\n\n// ── Prompt ──\n\nconst SYSTEM_PROMPT = `You are a crypto trade-decision reviewer. Your job is to confirm or veto a proposed trade entry.\n\nReturn ONLY valid JSON matching this schema:\n{\"verdict\":\"confirm\"|\"veto\",\"reasoning\":\"<max 240 chars>\",\"risks\":[\"<max 80 chars each, 0-4 items>\"],\"confidence\":<0-1>}\n\nVETO conditions (veto if ANY apply):\n- Regime contradicts direction (e.g., trending-down regime for a long entry)\n- Token is in stop cooldown (recently stopped out)\n- Daily portfolio loss exceeds -2% and this is a new entry\n- Insufficient data quality (too many signals missing/zero)\n- BTC bearish bias with high suppression for a long entry on an alt\n\nCONFIRM otherwise. Default to confirm — only veto with clear justification.\nNo prose outside the JSON. No markdown fences.`;\n\nfunction buildUserPrompt(ctx: JudgeContext): string {\n const d = ctx.decision;\n const topSignals = [...d.signals]\n .sort((a, b) => Math.abs(b.value) - Math.abs(a.value))\n .slice(0, 3)\n .map((s) => `${s.name}=${s.value.toFixed(3)}(c=${s.confidence.toFixed(2)})`)\n .join(\", \");\n\n const lines: string[] = [\n `Token: ${ctx.tokenId} | Price: $${ctx.currentPrice.toPrecision(4)}`,\n `Proposed: ${d.action} | Score: ${d.score.toFixed(3)} | Confidence: ${d.confidence.toFixed(2)}`,\n `Top signals: ${topSignals}`,\n `Regime: ${ctx.regime ?? \"unknown\"} | BTC regime: ${ctx.btcRegime ?? \"unknown\"}`,\n `BTC bias: ${ctx.btcBias ?? \"neutral\"} | Suppression: ${(ctx.suppressionFactor ?? 1).toFixed(2)}`,\n ];\n\n if (ctx.technicalSignals) {\n const t = ctx.technicalSignals;\n lines.push(`RSI: ${isNaN(t.rsi) ? \"N/A\" : t.rsi.toFixed(1)} | MACD hist: ${isNaN(t.macd.histogram) ? \"N/A\" : t.macd.histogram.toFixed(4)} | ATR: ${isNaN(t.atr) ? \"N/A\" : t.atr.toFixed(4)}`);\n }\n\n if (ctx.fearAndGreed !== undefined) {\n lines.push(`F&G: ${ctx.fearAndGreed}${ctx.sentimentZScore !== undefined ? ` (z=${ctx.sentimentZScore.toFixed(2)})` : \"\"}`);\n }\n\n if (ctx.fundingRate !== undefined) {\n lines.push(`Funding rate (8h): ${(ctx.fundingRate * 100).toFixed(4)}%`);\n }\n\n const p = ctx.portfolio;\n lines.push(`Portfolio: ${p.openCount} open | ${(p.cashPct * 100).toFixed(1)}% cash | dailyPnL: ${(p.dailyPnlPct * 100).toFixed(2)}%`);\n lines.push(`This token: ${p.hasPositionThisToken ? \"HAS POSITION\" : \"no position\"} | Stop cooldown: ${p.inStopCooldown ? \"YES\" : \"no\"}`);\n\n if (ctx.recentCloses?.length) {\n lines.push(`Recent closes (10d): ${ctx.recentCloses.map((c) => c.toPrecision(4)).join(\", \")}`);\n }\n\n return lines.join(\"\\n\");\n}\n\n// ── Main entry point ──\n\n/**\n * Judge a trade decision. Returns confirm/veto verdict.\n * NEVER throws — all errors return fallback confirm.\n */\nexport async function judge(\n ctx: JudgeContext,\n config: JudgeConfig = DEFAULT_JUDGE_CONFIG,\n): Promise<{ verdict: JudgeVerdict; cached: boolean; latencyMs: number }> {\n const start = Date.now();\n\n try {\n // Gate: judge disabled or no Venice API key\n if (!config.enabled || !getVeniceApiKey()) {\n return { verdict: FALLBACK_VERDICT, cached: false, latencyMs: 0 };\n }\n\n // Gate: only judge non-HOLD actions in the score band\n const absScore = Math.abs(ctx.decision.score);\n if (ctx.decision.action === \"HOLD\" || absScore < config.scoreBand[0] || absScore > config.scoreBand[1]) {\n return { verdict: FALLBACK_VERDICT, cached: false, latencyMs: 0 };\n }\n\n // Check cache\n const key = cacheKey(ctx);\n const cached = await readCache(key, config.cacheTtlMs);\n if (cached) {\n return { verdict: cached, cached: true, latencyMs: Date.now() - start };\n }\n\n // Call Venice (OpenAI-compatible chat completions)\n const result = await Promise.race([\n chatCompletion({\n model: config.model,\n messages: [\n { role: \"system\", content: SYSTEM_PROMPT },\n { role: \"user\", content: buildUserPrompt(ctx) },\n ],\n maxTokens: 400,\n temperature: 0.1,\n disableThinking: true,\n }),\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error(\"Judge timeout\")), config.timeoutMs),\n ),\n ]);\n\n // Parse response\n const parsed = parseVerdict(result.content);\n await writeCache(key, parsed);\n\n return { verdict: parsed, cached: false, latencyMs: Date.now() - start };\n } catch (err) {\n console.error(chalk.dim(` [judge] Error: ${(err as Error).message} — falling back to confirm`));\n return { verdict: FALLBACK_VERDICT, cached: false, latencyMs: Date.now() - start };\n }\n}\n\n/**\n * Determine which tokens should be judged (budget gate).\n * Returns token IDs sorted by |score| descending, capped at topN.\n */\nexport function selectJudgeCandidates(\n results: Array<{ token: string; score: number; action: string }>,\n config: JudgeConfig,\n): Set<string> {\n const candidates = results\n .filter((r) => {\n if (r.action === \"HOLD\") return false;\n const abs = Math.abs(r.score);\n // Upper bound is exclusive to match `judge()` gate at line 192\n // (`absScore > config.scoreBand[1]` rejects). Inclusive here would\n // pick a candidate that `judge()` then rejects → fallback confirm.\n return abs >= config.scoreBand[0] && abs < config.scoreBand[1];\n })\n .sort((a, b) => Math.abs(b.score) - Math.abs(a.score))\n .slice(0, config.topN);\n\n return new Set(candidates.map((c) => c.token));\n}\n\n// ── Parsing ──\n\nfunction parseVerdict(raw: string): JudgeVerdict {\n // Strip markdown fences if model wraps in ```json\n let cleaned = raw.trim();\n if (cleaned.startsWith(\"```\")) {\n cleaned = cleaned.replace(/^```(?:json)?\\s*/, \"\").replace(/\\s*```$/, \"\");\n }\n\n const obj = JSON.parse(cleaned);\n\n // Validate required fields\n if (obj.verdict !== \"confirm\" && obj.verdict !== \"veto\") {\n throw new Error(`Invalid verdict: ${obj.verdict}`);\n }\n\n return {\n verdict: obj.verdict,\n reasoning: typeof obj.reasoning === \"string\" ? obj.reasoning.slice(0, 240) : \"\",\n risks: Array.isArray(obj.risks) ? obj.risks.slice(0, 4).map((r: unknown) => String(r).slice(0, 80)) : [],\n confidence: typeof obj.confidence === \"number\" ? Math.max(0, Math.min(1, obj.confidence)) : 0.5,\n };\n}\n","/**\n * Signal smoother — rolling-window average for fast/noisy signals.\n *\n * Some signals (HL orderbook + whale flow, Nansen smart money, DEX 1h flow,\n * funding rate) are measured at a granularity finer than their actual\n * information content. Per-scan readings can flip 100% direction within\n * minutes when the underlying state hasn't meaningfully changed.\n *\n * This module wraps the per-scan reading in a rolling N-reading average\n * before it enters the scoring engine. Disagreement across the window\n * also reduces the signal's confidence — a smoothed value of 0.16 from\n * [+0.5, -0.5, +0.5] reports lower confidence than 0.16 from\n * [0.16, 0.16, 0.17].\n *\n * Slow signals (RSI/MACD/EMA — already smoothed by their indicators,\n * F&G — daily index, multi-timeframe — uses 7d candles) are passed\n * through unchanged.\n */\n\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport type { Signal } from './scoring.js';\n\n/** Signal names whose values benefit from rolling-window smoothing. */\nexport const FAST_SIGNAL_NAMES = new Set<string>([\n 'hyperliquidFlow',\n 'smartMoney',\n 'onchain', // legacy direct on-chain signal name\n 'dexFlow',\n 'fundingRate',\n]);\n\ninterface SignalReading {\n ts: number; // epoch ms\n value: number; // -1 to +1\n confidence: number;\n}\n\n/** Per-token, per-signal rolling buffer. */\ntype Cache = Record<string, Record<string, SignalReading[]>>;\n\nexport interface SmootherConfig {\n /** How many readings to retain per (token, signal) buffer. */\n windowSize: number;\n /** Maximum age (ms) for a reading to count toward the average. */\n maxAgeMs: number;\n /** Confidence multiplier when the window has zero std-dev (perfect agreement). */\n agreementBoost: number;\n /** Cap on confidence reduction from disagreement. */\n maxConfidencePenalty: number;\n}\n\nexport const DEFAULT_SMOOTHER_CONFIG: SmootherConfig = {\n windowSize: 3,\n maxAgeMs: 6 * 60 * 60 * 1000, // 6 hours — drop stale readings\n agreementBoost: 1.0,\n maxConfidencePenalty: 0.5,\n};\n\n/** Storage backend interface — disk for live, in-memory map for backtest. */\nexport interface SmootherStorage {\n load(): Promise<Cache>;\n save(cache: Cache): Promise<void>;\n}\n\n/** Disk-backed storage. Atomic write via temp file + rename. */\nexport class FileSmootherStorage implements SmootherStorage {\n constructor(private filePath: string) {}\n\n async load(): Promise<Cache> {\n try {\n const raw = await readFile(this.filePath, 'utf-8');\n return JSON.parse(raw) as Cache;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return {};\n throw err;\n }\n }\n\n async save(cache: Cache): Promise<void> {\n await mkdir(dirname(this.filePath), { recursive: true });\n const tmp = this.filePath + '.tmp';\n await writeFile(tmp, JSON.stringify(cache), 'utf-8');\n const { rename } = await import('node:fs/promises');\n await rename(tmp, this.filePath);\n }\n}\n\n/** In-memory storage — for backtest where state is per-simulation. */\nexport class MemorySmootherStorage implements SmootherStorage {\n private cache: Cache = {};\n async load(): Promise<Cache> { return this.cache; }\n async save(cache: Cache): Promise<void> { this.cache = cache; }\n}\n\nexport class SignalSmoother {\n /** In-process mutex chain — serializes load→mutate→save across tokens in\n * the same process. Does NOT protect against multiple processes racing\n * the same file; for that, the disk storage does atomic replace and\n * re-reads the freshest cache inside the mutex. See README / H1 below. */\n private writeLock: Promise<void> = Promise.resolve();\n\n constructor(\n private storage: SmootherStorage,\n private config: SmootherConfig = DEFAULT_SMOOTHER_CONFIG,\n ) {}\n\n /**\n * Smooth the given signals for a token. Fast signals get rolling-window\n * averages with confidence penalties for disagreement. Slow signals\n * pass through unchanged. Side effect: appends current readings to the\n * cache and persists.\n *\n * Serialized via writeLock: concurrent calls from within the same\n * process (e.g. parallel per-token smooths) queue on the shared mutex\n * so each sees the freshest cache. Cross-process concurrency (two agent\n * processes on the same host) is NOT fully safe — the second-to-save\n * wins. Document: run one agent per host, or use MemorySmootherStorage\n * in contexts with multiple simulation runs.\n */\n async smooth(tokenId: string, signals: Signal[], nowMs: number = Date.now()): Promise<Signal[]> {\n // Queue on the shared mutex\n const prev = this.writeLock;\n let release: () => void = () => {};\n this.writeLock = new Promise<void>((resolve) => { release = resolve; });\n await prev;\n\n try {\n return await this.smoothUnlocked(tokenId, signals, nowMs);\n } finally {\n release();\n }\n }\n\n private async smoothUnlocked(tokenId: string, signals: Signal[], nowMs: number): Promise<Signal[]> {\n const cache = await this.storage.load();\n // M2: sweep stale tokens out of the cache entirely so rotated-out\n // tokens don't leak JSON size indefinitely. A token is \"stale\" if\n // every signal buffer's newest reading is older than maxAgeMs.\n const cutoff = nowMs - this.config.maxAgeMs;\n for (const t of Object.keys(cache)) {\n if (t === tokenId) continue;\n const buffers = cache[t];\n if (!buffers) continue;\n const bufferList = Object.values(buffers);\n const anyFresh = bufferList.some((b) => b.length > 0 && b[b.length - 1]!.ts >= cutoff);\n if (!anyFresh) {\n delete cache[t];\n }\n }\n if (!cache[tokenId]) cache[tokenId] = {};\n\n const out: Signal[] = [];\n for (const sig of signals) {\n if (!FAST_SIGNAL_NAMES.has(sig.name)) {\n out.push(sig);\n continue;\n }\n\n const buffer = cache[tokenId][sig.name] ?? [];\n\n // Append current reading\n buffer.push({ ts: nowMs, value: sig.value, confidence: sig.confidence });\n\n // Drop stale readings\n const cutoff = nowMs - this.config.maxAgeMs;\n const fresh = buffer.filter((r) => r.ts >= cutoff);\n\n // Trim to window size — keep newest N\n const trimmed = fresh.slice(-this.config.windowSize);\n cache[tokenId][sig.name] = trimmed;\n\n // Compute smoothed value + confidence\n const values = trimmed.map((r) => r.value);\n const meanValue = values.reduce((a, b) => a + b, 0) / values.length;\n const meanConfidence = trimmed.reduce((a, r) => a + r.confidence, 0) / trimmed.length;\n\n // Std-dev in [-1,1] domain — max possible is 1.0 (alternating +1/-1)\n let smoothedConfidence = meanConfidence;\n if (trimmed.length > 1) {\n const variance = values.reduce((sum, v) => sum + (v - meanValue) ** 2, 0) / values.length;\n const stdDev = Math.sqrt(variance);\n // Penalize confidence proportional to std-dev (clamped)\n const penalty = Math.min(this.config.maxConfidencePenalty, stdDev);\n smoothedConfidence = Math.max(0.05, meanConfidence * (1 - penalty));\n }\n\n out.push({\n ...sig,\n value: meanValue,\n confidence: smoothedConfidence,\n details: `${sig.details} [smoothed n=${trimmed.length}, raw=${sig.value.toFixed(2)}]`,\n });\n }\n\n await this.storage.save(cache);\n return out;\n }\n}\n","/**\n * Entry gates — post-scoring filters that can downgrade an action before the\n * executor sees it. Adjacent in spirit to the LLM judge: the score + action\n * can still fire, but a cross-check against fresh market data may veto it.\n *\n * ── Velocity gate (Orca-inspired \"signal-velocity freshness\") ──\n *\n * Multi-signal stacks can fire BUY/SELL the moment the composite score crosses\n * a threshold — but if the underlying price move is already exhausted (flat or\n * reversing over the last ~1h), we're buying local tops (or shorting bottoms).\n * Orca's fix: refuse the entry if the most recent short-term velocity is not\n * aligned with the direction.\n *\n * - BUY rejected if 1h velocity < BUY_MIN_PCT (default -1.0%)\n * - SELL rejected if 1h velocity > SELL_MAX_PCT (default +1.0%)\n * - HOLD never gated.\n *\n * When velocity data is unavailable (no Hyperliquid fields, insufficient candle\n * history), the gate skips — never reject blindly.\n */\nimport type { Candle } from \"./technical.js\";\nimport type { TokenAnalysis } from \"./index.js\";\nimport type { MarketRegime } from \"./regime.js\";\n\n// ── Configurable constants (easy to calibrate) ──\n\n/** BUY rejected if 1h velocity is below this (fraction, not percent).\n * Relaxed from ±0.3% to ±1.0% — on 4h candles the velocity proxy covers\n * a 4h window, where ±0.3% is normal noise even in trending markets. */\nexport const VELOCITY_GATE_BUY_MIN_PCT = -0.01;\n\n/** SELL rejected if 1h velocity is above this (fraction, not percent). */\nexport const VELOCITY_GATE_SELL_MAX_PCT = 0.01;\n\n/** Regimes where short entries are allowed. Shorts are blocked in all other\n * regimes to prevent counter-trend fading — trade log analysis showed 25%\n * short WR in non-bearish regimes vs 67% long WR. */\nexport const SHORT_ALLOWED_REGIMES: Set<MarketRegime> = new Set([\n \"trending-down\",\n \"high-volatility\",\n]);\n\n/** EntryGate configuration — exposed on AgentConfig for easy calibration. */\nexport interface EntryGateConfig {\n /** Master toggle. When false, the gate is skipped entirely. Default true. */\n velocityGateEnabled: boolean;\n /** BUY is rejected when 1h velocity is strictly less than this. Default -0.01 (-1%). */\n velocityGateBuyMinPct: number;\n /** SELL is rejected when 1h velocity is strictly greater than this. Default +0.01 (+1%). */\n velocityGateSellMaxPct: number;\n /** When true, SELL/STRONG_SELL signals are blocked in non-bearish regimes\n * (trending-up, ranging, low-volatility). Default true. */\n regimeGateEnabled: boolean;\n /** When true, actionable entries must be backed by at least one aligned\n * high-quality signal, not just noisy/lagging components. Default true. */\n realAlphaGateEnabled: boolean;\n}\n\nexport const DEFAULT_ENTRY_GATE_CONFIG: EntryGateConfig = {\n velocityGateEnabled: true,\n velocityGateBuyMinPct: VELOCITY_GATE_BUY_MIN_PCT, // -1.0%\n velocityGateSellMaxPct: VELOCITY_GATE_SELL_MAX_PCT, // +1.0%\n regimeGateEnabled: true,\n realAlphaGateEnabled: true,\n};\n\n/** Signals allowed to justify an entry after the noisy-signal score filter.\n * Only includes signals that are actually populated by active strategies —\n * dexFlow / meanReversion / sentimentContrarian were removed when their\n * strategies were disabled. */\nconst REAL_ALPHA_THRESHOLDS: Record<string, number> = {\n smartMoney: 0.15,\n whaleIntent: 0.15,\n fundamental: 0.15,\n narrativeVacuum: 0.25,\n};\n\nfunction hasAlignedRealAlpha(result: TokenAnalysis, direction: 1 | -1): boolean {\n for (const signal of result.decision.signals) {\n const threshold = REAL_ALPHA_THRESHOLDS[signal.name];\n if (threshold === undefined) continue;\n if (direction > 0 && signal.value >= threshold) return true;\n if (direction < 0 && signal.value <= -threshold) return true;\n }\n return false;\n}\n\n/**\n * Derive a short-term velocity (fractional change) from candles.\n *\n * CoinGecko's /ohlc endpoint at days=30 returns 4h candles; at days=1 returns\n * 30m candles. Since the agent currently fetches days=30, the best proxy for\n * \"recent 1h-ish velocity\" is the last candle's close-over-previous-close\n * change (a 4h window — tight enough to capture fresh moves while respecting\n * the data we actually have).\n *\n * Returns undefined if we don't have enough data.\n */\nexport function deriveVelocityFromCandles(candles: Candle[] | undefined): number | undefined {\n if (!candles || candles.length < 2) return undefined;\n const last = candles[candles.length - 1]!;\n const prev = candles[candles.length - 2]!;\n if (!prev.close || prev.close <= 0) return undefined;\n return last.close / prev.close - 1;\n}\n\n/**\n * Resolve the velocity to use for the gate:\n * 1. Prefer an explicit Hyperliquid 1h price change when available.\n * (Units: decimal fraction, e.g. 0.012 for +1.2%. If the provider returns\n * a percent instead, callers must divide by 100 before passing it in.)\n * 2. Fall back to the most-recent candle-over-candle change.\n * 3. Otherwise undefined — gate will skip.\n */\nexport function resolveVelocity(\n priceChg1h: number | undefined,\n candles: Candle[] | undefined,\n): number | undefined {\n if (typeof priceChg1h === \"number\" && Number.isFinite(priceChg1h)) {\n return priceChg1h;\n }\n return deriveVelocityFromCandles(candles);\n}\n\n/**\n * Post-scoring hook that downgrades BUY/SELL → HOLD when recent price\n * velocity is not aligned with the proposed direction. Preserves the original\n * action/score on `result.preVelocity` (mirrors the `preJudge` pattern).\n *\n * Rules:\n * - BUY / STRONG_BUY: downgrade if velocity <= buyMinPct (flat or falling)\n * - SELL / STRONG_SELL: downgrade if velocity >= sellMaxPct (flat or rising)\n * - HOLD: never gated\n * - If velocity is undefined (no data): skip gate, return unchanged\n * - If !config.velocityGateEnabled: skip gate, return unchanged\n *\n * Edge case: exactly 0 velocity on a BUY downgrades (strict <=). Same on SELL\n * for >=. This is intentional — \"flat\" price action is not confirmation.\n */\nexport function applyVelocityGate(\n result: TokenAnalysis,\n velocity: number | undefined,\n config: EntryGateConfig = DEFAULT_ENTRY_GATE_CONFIG,\n logger: (msg: string) => void = () => {},\n): TokenAnalysis {\n if (!config.velocityGateEnabled) return result;\n if (velocity === undefined || !Number.isFinite(velocity)) return result;\n\n const action = result.decision.action;\n const isBuy = action === \"BUY\" || action === \"STRONG_BUY\";\n const isSell = action === \"SELL\" || action === \"STRONG_SELL\";\n\n if (!isBuy && !isSell) return result;\n\n const rejectBuy = isBuy && velocity <= config.velocityGateBuyMinPct;\n const rejectSell = isSell && velocity >= config.velocityGateSellMaxPct;\n\n if (!rejectBuy && !rejectSell) return result;\n\n const direction = isBuy ? \"BUY\" : \"SELL\";\n const threshold = isBuy ? config.velocityGateBuyMinPct : config.velocityGateSellMaxPct;\n const pct = (velocity * 100).toFixed(2);\n const thresholdPct = (threshold * 100).toFixed(2);\n logger(\n ` [velocity] DOWNGRADE ${result.token}: ${direction} ${action} blocked — 1h velocity ${pct}% ` +\n `vs threshold ${thresholdPct}% (signal stale / exhausted)`,\n );\n\n return {\n ...result,\n preVelocity: { action: result.decision.action, score: result.decision.score },\n decision: { ...result.decision, action: \"HOLD\" },\n };\n}\n\n/**\n * Post-scoring gate that blocks SELL/STRONG_SELL when the market regime does\n * not support shorting. Trade log analysis (32 trades): shorts had 25% WR\n * (-$194) while longs had 67% WR (+$279). Most short losses came from fading\n * a recovery trend — the regime was ranging or trending-up, not bearish.\n *\n * Only allows shorts in:\n * - trending-down: confirmed bearish regime\n * - high-volatility: directional moves can go either way\n *\n * Blocks shorts in:\n * - trending-up: counter-trend fade, historically destructive\n * - ranging: choppy, no directional edge for shorts\n * - low-volatility: not enough movement to profit from shorts\n *\n * When regime is undefined (no BTC data), the gate skips — never reject blindly.\n */\nexport function applyRegimeGate(\n result: TokenAnalysis,\n regime: MarketRegime | undefined,\n config: EntryGateConfig = DEFAULT_ENTRY_GATE_CONFIG,\n logger: (msg: string) => void = () => {},\n): TokenAnalysis {\n if (!config.regimeGateEnabled) return result;\n if (regime === undefined) return result;\n\n const action = result.decision.action;\n const isSell = action === \"SELL\" || action === \"STRONG_SELL\";\n\n if (!isSell) return result;\n\n if (SHORT_ALLOWED_REGIMES.has(regime)) return result;\n\n logger(\n ` [regime] DOWNGRADE ${result.token}: ${action} blocked — regime \"${regime}\" ` +\n `does not support shorts (allowed: ${[...SHORT_ALLOWED_REGIMES].join(\", \")})`,\n );\n\n return {\n ...result,\n preRegime: { action: result.decision.action, score: result.decision.score },\n decision: { ...result.decision, action: \"HOLD\" },\n };\n}\n\n/**\n * Blocks entries whose score is created only by noisy/lagging signals. Recent\n * signal analysis showed momentum, TradingView, funding, and HL flow were\n * negative-edge inputs; this gate requires a separate aligned alpha source.\n */\nexport function applyRealAlphaGate(\n result: TokenAnalysis,\n config: EntryGateConfig = DEFAULT_ENTRY_GATE_CONFIG,\n logger: (msg: string) => void = () => {},\n): TokenAnalysis {\n if (!config.realAlphaGateEnabled) return result;\n\n const action = result.decision.action;\n const isBuy = action === \"BUY\" || action === \"STRONG_BUY\";\n const isSell = action === \"SELL\" || action === \"STRONG_SELL\";\n if (!isBuy && !isSell) return result;\n\n const direction = isBuy ? 1 : -1;\n if (hasAlignedRealAlpha(result, direction)) return result;\n\n logger(\n ` [alpha] DOWNGRADE ${result.token}: ${action} blocked — no aligned real-alpha ` +\n `signal (requires smartMoney, fundamental, narrativeVacuum, or whaleIntent)`,\n );\n\n return {\n ...result,\n preAlpha: { action: result.decision.action, score: result.decision.score },\n decision: { ...result.decision, action: \"HOLD\" },\n };\n}\n","/**\n * Market regime detection module — analyzes BTC to classify market state.\n * Helps adjust strategy confidence based on current macro conditions.\n */\n\nimport { readFile, writeFile, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport type { Candle } from \"./technical.js\";\nimport { calculateEMA, calculateBollingerBands, calculateATR } from \"./technical.js\";\n\nexport type MarketRegime = \"trending-up\" | \"trending-down\" | \"ranging\" | \"high-volatility\" | \"low-volatility\";\n\nexport interface RegimeAnalysis {\n regime: MarketRegime;\n confidence: number; // 0-1 how sure we are about the regime\n btcTrend: \"up\" | \"down\" | \"neutral\";\n volatilityLevel: \"low\" | \"normal\" | \"high\" | \"extreme\";\n details: string;\n strategyAdjustments: Record<string, number>; // multiplier per strategy name (0.0 to 1.5)\n}\n\ninterface RegimeCache {\n timestamp: number;\n analysis: RegimeAnalysis;\n}\n\nexport class MarketRegimeDetector {\n private cacheDir: string;\n private cacheFile: string;\n\n constructor() {\n this.cacheDir = join(homedir(), '.sherwood', 'agent', 'cache');\n this.cacheFile = join(this.cacheDir, 'regime.json');\n }\n\n /**\n * Detect current market regime using BTC candles.\n * Returns cached result if less than 5 minutes old.\n */\n async detect(btcCandles: Candle[]): Promise<RegimeAnalysis> {\n // Check cache first\n try {\n const cached = await this.loadCache();\n const now = Date.now();\n const cacheAge = now - cached.timestamp;\n // 5-minute TTL — cycles run every ~25min, but a 15min cache lagged real\n // regime shifts by up to a full cycle. 5min keeps the cache useful for\n // back-to-back short scans while staying close to live state.\n const CACHE_DURATION = 5 * 60 * 1000;\n\n if (cacheAge < CACHE_DURATION) {\n return cached.analysis;\n }\n } catch {\n // Cache miss or invalid, continue to fresh analysis\n }\n\n const analysis = await this.analyzeBtc(btcCandles);\n\n // Save to cache\n try {\n await this.saveCache(analysis);\n } catch {\n // Non-critical, continue without caching\n }\n\n return analysis;\n }\n\n /**\n * Pure regime classification from a candle window — no network calls,\n * no cache, no BTC dominance lookup. Use this from the backtester to\n * compute regime per-candle without look-ahead bias or external IO.\n */\n static classifyFromCandles(candles: Candle[]): RegimeAnalysis {\n const detector = new MarketRegimeDetector();\n return detector.classifySync(candles);\n }\n\n /** Synchronous candle-only analysis — same logic as analyzeBtc minus dominance/IO. */\n classifySync(candles: Candle[]): RegimeAnalysis {\n if (candles.length < 60) {\n return this.fallbackAnalysis(\"Insufficient BTC data for regime analysis\");\n }\n\n const closes = candles.map((c) => c.close);\n const currentPrice = closes[closes.length - 1]!;\n\n // ── Fast momentum override — shared with analyzeBtc ──\n const momentumResult = this.checkMomentumOverride(candles, currentPrice);\n if (momentumResult) return momentumResult;\n\n // ── Standard EMA/ADX regime classification ──\n // Apr 2026 audit: switched from EMA(50,200) + ADX(14) to EMA(21,50) + ADX(7).\n // On 4h candles EMA(50,200) lagged 200+ hours — missed the Apr 13-14 BTC rally\n // entirely. Faster EMAs + ADX(7) respond in ~28-50 hours instead.\n const ema21 = calculateEMA(closes, 21);\n const ema50 = calculateEMA(closes, 50);\n const currentEma21 = this.getLastValidValue(ema21);\n const currentEma50 = this.getLastValidValue(ema50);\n\n const bb = calculateBollingerBands(candles, 20, 2.0);\n const currentBbWidth = this.getLastValidValue(bb.width);\n\n const atr = calculateATR(candles, 14);\n const currentAtr = this.getLastValidValue(atr);\n const atrRatio = currentAtr / currentPrice;\n\n const adx = this.calculateADX(candles, 7);\n const currentAdx = this.getLastValidValue(adx);\n\n let btcTrend: RegimeAnalysis[\"btcTrend\"] = \"neutral\";\n if (!isNaN(currentEma21) && !isNaN(currentEma50)) {\n if (currentPrice > currentEma21 && currentEma21 > currentEma50) {\n btcTrend = \"up\";\n } else if (currentPrice < currentEma21 && currentEma21 < currentEma50) {\n btcTrend = \"down\";\n }\n }\n\n let volatilityLevel: RegimeAnalysis[\"volatilityLevel\"] = \"normal\";\n if (!isNaN(currentBbWidth)) {\n if (currentBbWidth < 0.04) volatilityLevel = \"low\";\n else if (currentBbWidth > 0.20) volatilityLevel = \"extreme\";\n else if (currentBbWidth > 0.10) volatilityLevel = \"high\";\n }\n\n let regime: MarketRegime;\n let confidence: number;\n let details: string;\n\n if (volatilityLevel === \"extreme\") {\n regime = \"high-volatility\";\n confidence = 0.9;\n details = `Extreme volatility (BB ${currentBbWidth.toFixed(3)}, ATR ${atrRatio.toFixed(3)})`;\n } else if (volatilityLevel === \"low\" && currentAdx < 15) {\n regime = \"low-volatility\";\n confidence = 0.8;\n details = `Low volatility (BB ${currentBbWidth.toFixed(3)}, ADX ${currentAdx.toFixed(1)})`;\n } else {\n if (currentAdx > 20 && btcTrend !== \"neutral\") {\n if (btcTrend === \"up\") {\n regime = \"trending-up\";\n confidence = Math.min(0.9, 0.6 + (currentAdx - 20) * 0.01);\n details = `Strong uptrend (ADX ${currentAdx.toFixed(1)})`;\n } else {\n regime = \"trending-down\";\n confidence = Math.min(0.9, 0.6 + (currentAdx - 20) * 0.01);\n details = `Strong downtrend (ADX ${currentAdx.toFixed(1)})`;\n }\n } else {\n regime = \"ranging\";\n confidence = currentAdx < 15 ? 0.7 : 0.5;\n details = `Ranging (ADX ${currentAdx.toFixed(1)})`;\n }\n }\n\n return {\n regime,\n confidence,\n btcTrend,\n volatilityLevel,\n details,\n strategyAdjustments: this.getStrategyAdjustments(regime),\n };\n }\n\n private async analyzeBtc(candles: Candle[]): Promise<RegimeAnalysis> {\n // Delegate to the synchronous classifier — the async BTC dominance\n // fetch was dead code (checked absolute level, not trend; result\n // only appended to details string, never used in classification).\n return this.classifySync(candles);\n }\n\n private calculateADX(candles: Candle[], period: number): number[] {\n if (candles.length < period + 1) return [];\n\n // Calculate True Range and Directional Movement\n const trueRanges: number[] = [];\n const dmPlus: number[] = [];\n const dmMinus: number[] = [];\n\n for (let i = 1; i < candles.length; i++) {\n const curr = candles[i]!;\n const prev = candles[i - 1]!;\n\n // True Range\n const tr = Math.max(\n curr.high - curr.low,\n Math.abs(curr.high - prev.close),\n Math.abs(curr.low - prev.close)\n );\n trueRanges.push(tr);\n\n // Directional Movement\n const upMove = curr.high - prev.high;\n const downMove = prev.low - curr.low;\n\n dmPlus.push(upMove > downMove && upMove > 0 ? upMove : 0);\n dmMinus.push(downMove > upMove && downMove > 0 ? downMove : 0);\n }\n\n // Smooth the values using Wilder's smoothing\n const smoothTR = this.wilderSmooth(trueRanges, period);\n const smoothDMPlus = this.wilderSmooth(dmPlus, period);\n const smoothDMMinus = this.wilderSmooth(dmMinus, period);\n\n // Calculate DI+ and DI-\n const diPlus: number[] = [];\n const diMinus: number[] = [];\n const dx: number[] = [];\n\n for (let i = 0; i < smoothTR.length; i++) {\n if (smoothTR[i] > 0) {\n const diP = (smoothDMPlus[i] / smoothTR[i]) * 100;\n const diM = (smoothDMMinus[i] / smoothTR[i]) * 100;\n diPlus.push(diP);\n diMinus.push(diM);\n\n // Calculate DX\n const diSum = diP + diM;\n if (diSum > 0) {\n dx.push((Math.abs(diP - diM) / diSum) * 100);\n } else {\n dx.push(0);\n }\n } else {\n diPlus.push(0);\n diMinus.push(0);\n dx.push(0);\n }\n }\n\n // ADX is the smoothed DX\n return this.wilderSmooth(dx, period);\n }\n\n private wilderSmooth(values: number[], period: number): number[] {\n const result: number[] = [];\n\n for (let i = 0; i < values.length; i++) {\n if (i < period - 1) {\n result.push(NaN);\n } else if (i === period - 1) {\n // Initial smoothed value is simple average\n const sum = values.slice(i - period + 1, i + 1).reduce((a, b) => a + b, 0);\n result.push(sum / period);\n } else {\n // Subsequent values use Wilder's smoothing\n const prev = result[i - 1]!;\n const current = values[i]!;\n result.push((prev * (period - 1) + current) / period);\n }\n }\n\n return result;\n }\n\n /**\n * Fast momentum check — catches intraday trends that EMA/ADX miss.\n * Returns a RegimeAnalysis if momentum override fires, or null to fall\n * through to the standard EMA/ADX classification.\n *\n * Threshold is volatility-adjusted: 1.5× the 14-candle ATR as a\n * percentage of current price, floored at 2% and capped at 8%.\n * - BTC (daily ATR ~2%): threshold ≈ 3% — same as before\n * - SOL (daily ATR ~5%): threshold ≈ 7.5% — avoids false triggers on normal alt vol\n * - FARTCOIN (daily ATR ~10%): threshold = 8% cap — only fires on genuinely unusual moves\n *\n * Requirements for trending-up: price above threshold AND in the upper\n * half of the range (prevents false positives on bounces within a larger\n * downtrend). Symmetric for trending-down.\n */\n private checkMomentumOverride(candles: Candle[], currentPrice: number): RegimeAnalysis | null {\n const MOMENTUM_LOOKBACK = 24;\n const MIN_THRESHOLD = 0.02; // floor: 2%\n const MAX_THRESHOLD = 0.08; // cap: 8%\n const ATR_MULTIPLIER = 1.5;\n\n // Compute 14-period ATR as a percentage of current price for vol-adjustment\n const atrValues = calculateATR(candles, 14);\n const currentAtr = this.getLastValidValue(atrValues);\n const atrPct = currentPrice > 0 && !isNaN(currentAtr) ? currentAtr / currentPrice : 0.02;\n const threshold = Math.min(MAX_THRESHOLD, Math.max(MIN_THRESHOLD, atrPct * ATR_MULTIPLIER));\n\n // Compute volatility level BEFORE the override so it propagates\n // into the returned RegimeAnalysis. Previously hardcoded \"normal\"\n // which masked extreme volatility during fast moves.\n const bb = calculateBollingerBands(candles, 20, 2.0);\n const currentBbWidth = this.getLastValidValue(bb.width);\n let volLevel: RegimeAnalysis[\"volatilityLevel\"] = \"normal\";\n if (!isNaN(currentBbWidth)) {\n if (currentBbWidth < 0.04) volLevel = \"low\";\n else if (currentBbWidth > 0.20) volLevel = \"extreme\";\n else if (currentBbWidth > 0.10) volLevel = \"high\";\n }\n\n const recentCandles = candles.slice(-MOMENTUM_LOOKBACK);\n const recentLow = Math.min(...recentCandles.map((c) => c.low));\n const recentHigh = Math.max(...recentCandles.map((c) => c.high));\n const pctAboveLow = recentLow > 0 ? (currentPrice - recentLow) / recentLow : 0;\n const pctBelowHigh = recentHigh > 0 ? (recentHigh - currentPrice) / recentHigh : 0;\n\n const recentMid = (recentHigh + recentLow) / 2;\n const inUpperHalf = currentPrice >= recentMid;\n const inLowerHalf = currentPrice <= recentMid;\n\n if (pctAboveLow >= threshold && inUpperHalf) {\n return {\n regime: \"trending-up\",\n confidence: Math.min(0.85, 0.5 + pctAboveLow * 5),\n btcTrend: \"up\",\n volatilityLevel: volLevel,\n details: `Momentum override: price +${(pctAboveLow * 100).toFixed(1)}% above ${MOMENTUM_LOOKBACK}-candle low (threshold ${(threshold * 100).toFixed(1)}%, ATR-adjusted)`,\n strategyAdjustments: this.getStrategyAdjustments(\"trending-up\"),\n };\n }\n if (pctBelowHigh >= threshold && inLowerHalf) {\n return {\n regime: \"trending-down\",\n confidence: Math.min(0.85, 0.5 + pctBelowHigh * 5),\n btcTrend: \"down\",\n volatilityLevel: volLevel,\n details: `Momentum override: price -${(pctBelowHigh * 100).toFixed(1)}% below ${MOMENTUM_LOOKBACK}-candle high (threshold ${(threshold * 100).toFixed(1)}%, ATR-adjusted)`,\n strategyAdjustments: this.getStrategyAdjustments(\"trending-down\"),\n };\n }\n return null;\n }\n\n private getStrategyAdjustments(regime: MarketRegime): Record<string, number> {\n // Only active strategies. Trend-aligned signals boosted during trends,\n // contrarian signals boosted during ranging.\n const baseMultipliers: Record<MarketRegime, Record<string, number>> = {\n \"trending-up\": {\n breakoutOnChain: 1.3,\n fundingRate: 1.0,\n dexFlow: 1.2,\n sentimentContrarian: 0.7,\n hyperliquidFlow: 1.2,\n multiTimeframe: 1.2,\n crossSectionalMomentum: 1.2,\n tradingviewSignal: 1.0,\n },\n \"trending-down\": {\n breakoutOnChain: 1.3,\n fundingRate: 1.2,\n dexFlow: 1.2,\n sentimentContrarian: 0.8,\n hyperliquidFlow: 1.3,\n multiTimeframe: 1.2,\n crossSectionalMomentum: 1.2,\n tradingviewSignal: 1.0,\n },\n \"ranging\": {\n breakoutOnChain: 0.5,\n fundingRate: 1.0,\n dexFlow: 0.8,\n sentimentContrarian: 1.3,\n hyperliquidFlow: 1.0,\n multiTimeframe: 0.8,\n crossSectionalMomentum: 1.0,\n tradingviewSignal: 1.0,\n },\n \"high-volatility\": {\n breakoutOnChain: 1.0,\n fundingRate: 1.0,\n dexFlow: 0.7,\n sentimentContrarian: 0.7,\n hyperliquidFlow: 0.7,\n multiTimeframe: 0.7,\n crossSectionalMomentum: 0.8,\n tradingviewSignal: 0.8,\n },\n \"low-volatility\": {\n breakoutOnChain: 1.3,\n fundingRate: 0.8,\n dexFlow: 0.8,\n sentimentContrarian: 0.8,\n hyperliquidFlow: 0.8,\n multiTimeframe: 1.2,\n crossSectionalMomentum: 1.0,\n tradingviewSignal: 1.0,\n },\n };\n\n return baseMultipliers[regime];\n }\n\n private getLastValidValue(arr: number[]): number {\n for (let i = arr.length - 1; i >= 0; i--) {\n if (!isNaN(arr[i]!)) return arr[i]!;\n }\n return NaN;\n }\n\n private fallbackAnalysis(reason: string): RegimeAnalysis {\n return {\n regime: \"ranging\",\n confidence: 0.3,\n btcTrend: \"neutral\",\n volatilityLevel: \"normal\",\n details: reason,\n strategyAdjustments: {\n breakoutOnChain: 1.0,\n fundingRate: 1.0,\n dexFlow: 1.0,\n sentimentContrarian: 1.0,\n hyperliquidFlow: 1.0,\n multiTimeframe: 1.0,\n crossSectionalMomentum: 1.0,\n tradingviewSignal: 1.0,\n },\n };\n }\n\n private async loadCache(): Promise<RegimeCache> {\n const data = await readFile(this.cacheFile, 'utf-8');\n return JSON.parse(data) as RegimeCache;\n }\n\n private async saveCache(analysis: RegimeAnalysis): Promise<void> {\n await mkdir(this.cacheDir, { recursive: true });\n const cache: RegimeCache = {\n timestamp: Date.now(),\n analysis,\n };\n await writeFile(this.cacheFile, JSON.stringify(cache, null, 2), 'utf-8');\n }\n}","/**\n * Correlation guard module — prevents going long on alts when BTC is bearish.\n * Uses BTC technicals to assess market structure and suppress/boost alt signals.\n */\n\nimport { readFile, writeFile, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport type { Candle } from \"./technical.js\";\nimport { calculateEMA, calculateRSI, calculateMACD } from \"./technical.js\";\nimport { CoinGeckoProvider } from \"../providers/data/coingecko.js\";\nimport { HyperliquidProvider } from \"../providers/data/hyperliquid.js\";\n\nexport interface CorrelationCheck {\n btcBias: \"bullish\" | \"bearish\" | \"neutral\";\n btcScore: number; // -1 to +1\n shouldSuppress: boolean; // true if alt long signal should be reduced\n suppressionFactor: number; // 0.0 to 1.0 multiplier on alt signals\n reason: string;\n}\n\ninterface CorrelationCache {\n timestamp: number;\n btcStructure: BtcStructure;\n}\n\ninterface BtcStructure {\n price: number;\n ema50: number;\n ema200: number;\n rsi: number;\n macdDirection: \"bullish\" | \"bearish\" | \"neutral\";\n score: number;\n}\n\nexport class CorrelationGuard {\n private cacheDir: string;\n private cacheFile: string;\n private coingecko: CoinGeckoProvider;\n private hyperliquid: HyperliquidProvider;\n\n constructor() {\n this.cacheDir = join(homedir(), '.sherwood', 'agent', 'cache');\n this.cacheFile = join(this.cacheDir, 'btc-correlation.json');\n this.coingecko = new CoinGeckoProvider();\n this.hyperliquid = new HyperliquidProvider();\n }\n\n /**\n * Check correlation and return suppression factors for a given token.\n * BTC itself is never suppressed. Stablecoins are never suppressed.\n */\n async checkCorrelation(tokenId: string): Promise<CorrelationCheck> {\n // Skip correlation check for BTC and stablecoins\n if (this.shouldSkipCorrelation(tokenId)) {\n return this.neutralCheck(\"BTC or stablecoin - no correlation suppression\");\n }\n\n // Get BTC structure (cached for 10 minutes)\n const btcStructure = await this.getBtcStructure();\n\n // Determine BTC bias and score\n const { bias, score, reason } = this.assessBtcBias(btcStructure);\n\n // Calculate suppression for alt tokens\n const { shouldSuppress, suppressionFactor } = this.calculateSuppression(bias, score);\n\n return {\n btcBias: bias,\n btcScore: score,\n shouldSuppress,\n suppressionFactor,\n reason,\n };\n }\n\n private shouldSkipCorrelation(tokenId: string): boolean {\n // Skip BTC itself\n if (tokenId === \"bitcoin\") return true;\n\n // Skip stablecoins\n const stablecoins = [\n \"tether\", \"usd-coin\", \"binance-usd\", \"dai\", \"frax\",\n \"trueusd\", \"paxos-standard\", \"gemini-dollar\", \"liquity-usd\"\n ];\n\n return stablecoins.includes(tokenId);\n }\n\n private async getBtcStructure(): Promise<BtcStructure> {\n // Check cache first (5-min TTL — HL data is free and fast, no rate-limit concern).\n let cached: CorrelationCache | undefined;\n try {\n cached = await this.loadCache();\n const cacheAge = Date.now() - cached.timestamp;\n const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes — HL data is real-time and free\n if (cacheAge < CACHE_DURATION && cached.btcStructure.price > 0) {\n return cached.btcStructure;\n }\n } catch {\n // Cache miss — continue to fresh analysis.\n }\n\n // PRIMARY: Hyperliquid native data (free, no rate limits, real-time)\n const hlStructure = await this.analyzeBtcFromHL();\n if (hlStructure && hlStructure.price > 0) {\n try { await this.saveCache(hlStructure); } catch { /* non-critical */ }\n return hlStructure;\n }\n\n // FALLBACK: CoinGecko OHLC (rate-limited on free tier, often 429s)\n const cgStructure = await this.analyzeBtcFromCG();\n if (cgStructure.price > 0) {\n try { await this.saveCache(cgStructure); } catch { /* non-critical */ }\n return cgStructure;\n }\n\n // Both failed — prefer stale-but-real cache over neutral fallback.\n if (cached && cached.btcStructure.price > 0) {\n return cached.btcStructure;\n }\n\n return cgStructure; // neutral fallback\n }\n\n /**\n * PRIMARY path: derive BTC structure from Hyperliquid exchange data.\n *\n * Uses mark price, 24h price change, funding rate, OI change, order-book\n * imbalance, and large-trades bias — all fetched in a single free API call\n * with no rate-limit risk. Replaces the CG OHLC path that was permanently\n * 429-ing on the free tier and poisoning the correlation cache.\n *\n * Scoring weights mirror the CG path's 40/30/30 split:\n * - 24h momentum (40%) — replaces EMA alignment\n * - Funding direction (30%) — replaces MACD histogram\n * - Flow bias (30%) — replaces RSI, derived from OB imbalance + large trades\n */\n private async analyzeBtcFromHL(): Promise<BtcStructure | null> {\n try {\n const data = await this.hyperliquid.getHyperliquidData(\"bitcoin\");\n if (!data || data.markPrice <= 0) return null;\n\n let score = 0;\n\n // 24h momentum (40% weight) — replaces EMA alignment\n const pctChange24h = data.prevDayPrice > 0\n ? (data.markPrice - data.prevDayPrice) / data.prevDayPrice\n : 0;\n if (pctChange24h > 0.01) score += 0.4; // >+1% day → bullish\n else if (pctChange24h < -0.01) score -= 0.4; // <-1% day → bearish\n else score += pctChange24h * 40; // linear between ±1%\n\n // Funding direction (30% weight) — replaces MACD\n // Positive funding = longs overleveraged = potentially bearish\n // Negative = shorts overleveraged = potentially bullish\n const fr = data.fundingRate;\n if (fr > 0.0001) score -= 0.3; // strong positive funding → bearish signal\n else if (fr < -0.0001) score += 0.3; // strong negative funding → bullish signal\n else score -= fr * 3000; // linear in between (inverted — high funding is bearish)\n\n // Flow bias (30% weight) — replaces RSI\n // Combine order-book imbalance + large-trades net direction\n const flowBias = (data.orderBookImbalance + data.largeTradesBias) / 2;\n if (flowBias > 0.2) score += 0.3;\n else if (flowBias < -0.2) score -= 0.3;\n else score += flowBias * 1.5; // linear scale\n\n score = Math.max(-1, Math.min(1, score));\n\n // Map to BtcStructure — ema50/ema200/rsi aren't computed from HL data,\n // but score + price + macdDirection are the only fields consumed by\n // assessBtcBias / calculateSuppression downstream. Set the rest to\n // sentinel values that won't confuse debuggers.\n const macdDirection: BtcStructure[\"macdDirection\"] =\n fr > 0.0001 ? \"bearish\" : fr < -0.0001 ? \"bullish\" : \"neutral\";\n\n return {\n price: data.markPrice,\n ema50: 0, // not available from HL — unused in scoring\n ema200: 0, // not available from HL — unused in scoring\n rsi: 50, // not available from HL — unused in scoring\n macdDirection,\n score,\n };\n } catch (err) {\n console.warn(`BTC structure from Hyperliquid failed: ${(err as Error).message}`);\n return null;\n }\n }\n\n /** FALLBACK: CoinGecko OHLC-based BTC analysis. Rate-limited on free tier. */\n private async analyzeBtcFromCG(): Promise<BtcStructure> {\n try {\n // Fetch BTC candles (90 days for EMAs — CoinGecko returns daily candles at days>=90,\n // giving us ~90 bars, enough for the 50-bar floor required by EMA/RSI/MACD history).\n const ohlcData = await this.coingecko.getOHLC(\"bitcoin\", 90);\n\n if (!ohlcData || ohlcData.length < 50) {\n throw new Error(\"Insufficient BTC data\");\n }\n\n const candles: Candle[] = ohlcData.map((c: number[]) => ({\n timestamp: c[0]!,\n open: c[1]!,\n high: c[2]!,\n low: c[3]!,\n close: c[4] ?? c[3]!,\n volume: 0, // Not needed for structure assessment\n }));\n\n const closes = candles.map(c => c.close);\n const currentPrice = closes[closes.length - 1]!;\n\n // Calculate technical indicators\n const ema50Array = calculateEMA(closes, 50);\n const ema200Array = calculateEMA(closes, 200);\n const rsiArray = calculateRSI(candles, 14);\n const macd = calculateMACD(candles, 12, 26, 9);\n\n // Get latest valid values\n const ema50 = this.getLastValidValue(ema50Array);\n const ema200 = this.getLastValidValue(ema200Array);\n const rsi = this.getLastValidValue(rsiArray);\n const macdHistogram = this.getLastValidValue(macd.histogram);\n\n // Determine MACD direction\n let macdDirection: BtcStructure[\"macdDirection\"] = \"neutral\";\n if (!isNaN(macdHistogram)) {\n if (macdHistogram > 0.1) macdDirection = \"bullish\";\n else if (macdHistogram < -0.1) macdDirection = \"bearish\";\n }\n\n // Calculate composite score (-1 to +1)\n let score = 0;\n\n // EMA alignment (40% weight)\n if (!isNaN(ema50) && !isNaN(ema200)) {\n if (currentPrice > ema50 && ema50 > ema200) {\n score += 0.4; // Bullish alignment\n } else if (currentPrice < ema50 && ema50 < ema200) {\n score -= 0.4; // Bearish alignment\n }\n // Neutral if mixed\n }\n\n // RSI (30% weight)\n if (!isNaN(rsi)) {\n if (rsi < 40) {\n score += 0.3; // Oversold = potentially bullish\n } else if (rsi > 60) {\n score -= 0.3; // Overbought = potentially bearish\n }\n // Scale linearly between 40-60\n else {\n const rsiScore = -((rsi - 50) / 10) * 0.15; // Max ±0.15 in neutral range\n score += rsiScore;\n }\n }\n\n // MACD direction (30% weight)\n if (macdDirection === \"bullish\") {\n score += 0.3;\n } else if (macdDirection === \"bearish\") {\n score -= 0.3;\n }\n\n // Clamp score to [-1, 1]\n score = Math.max(-1, Math.min(1, score));\n\n return {\n price: currentPrice,\n ema50: isNaN(ema50) ? 0 : ema50,\n ema200: isNaN(ema200) ? 0 : ema200,\n rsi: isNaN(rsi) ? 50 : rsi,\n macdDirection,\n score,\n };\n\n } catch (error) {\n console.warn(`BTC structure analysis failed: ${(error as Error).message}`);\n\n // Return neutral structure on failure\n return {\n price: 0,\n ema50: 0,\n ema200: 0,\n rsi: 50,\n macdDirection: \"neutral\",\n score: 0,\n };\n }\n }\n\n private assessBtcBias(structure: BtcStructure): { bias: CorrelationCheck[\"btcBias\"], score: number, reason: string } {\n const score = structure.score;\n\n if (score < -0.3) {\n const components = [];\n if (structure.price > 0 && structure.ema50 > 0 && structure.ema200 > 0) {\n if (structure.price < structure.ema50 && structure.ema50 < structure.ema200) {\n components.push(\"below EMAs\");\n }\n }\n if (structure.rsi < 40) components.push(`RSI ${structure.rsi.toFixed(1)}`);\n if (structure.macdDirection === \"bearish\") components.push(\"MACD bearish\");\n\n return {\n bias: \"bearish\",\n score,\n reason: `BTC bearish (${score.toFixed(2)}) — ${components.join(\", \") || \"weak structure\"}`,\n };\n }\n\n if (score > 0.3) {\n const components = [];\n if (structure.price > 0 && structure.ema50 > 0 && structure.ema200 > 0) {\n if (structure.price > structure.ema50 && structure.ema50 > structure.ema200) {\n components.push(\"above EMAs\");\n }\n }\n if (structure.rsi > 60) components.push(`RSI ${structure.rsi.toFixed(1)}`);\n if (structure.macdDirection === \"bullish\") components.push(\"MACD bullish\");\n\n return {\n bias: \"bullish\",\n score,\n reason: `BTC bullish (${score.toFixed(2)}) — ${components.join(\", \") || \"strong structure\"}`,\n };\n }\n\n return {\n bias: \"neutral\",\n score,\n reason: `BTC neutral (${score.toFixed(2)}) — mixed signals`,\n };\n }\n\n private calculateSuppression(bias: CorrelationCheck[\"btcBias\"], score: number): { shouldSuppress: boolean, suppressionFactor: number } {\n if (bias === \"bearish\") {\n // Strong bearish: suppress longs heavily, boost shorts slightly\n const suppressionFactor = 1 + score; // score is negative, so this reduces the factor\n return {\n shouldSuppress: true,\n suppressionFactor: Math.max(0.1, suppressionFactor), // Min 10% of original signal\n };\n }\n\n if (bias === \"bullish\") {\n // Strong bullish: boost longs slightly, suppress shorts\n const boostFactor = 1 + score * 0.2; // Max 20% boost for longs\n return {\n shouldSuppress: false,\n suppressionFactor: Math.min(1.2, boostFactor), // Max 20% boost\n };\n }\n\n // Neutral: no suppression\n return {\n shouldSuppress: false,\n suppressionFactor: 1.0,\n };\n }\n\n private neutralCheck(reason: string): CorrelationCheck {\n return {\n btcBias: \"neutral\",\n btcScore: 0,\n shouldSuppress: false,\n suppressionFactor: 1.0,\n reason,\n };\n }\n\n private getLastValidValue(arr: number[]): number {\n for (let i = arr.length - 1; i >= 0; i--) {\n if (!isNaN(arr[i]!)) return arr[i]!;\n }\n return NaN;\n }\n\n private async loadCache(): Promise<CorrelationCache> {\n const data = await readFile(this.cacheFile, 'utf-8');\n return JSON.parse(data) as CorrelationCache;\n }\n\n private async saveCache(btcStructure: BtcStructure): Promise<void> {\n await mkdir(this.cacheDir, { recursive: true });\n const cache: CorrelationCache = {\n timestamp: Date.now(),\n btcStructure,\n };\n await writeFile(this.cacheFile, JSON.stringify(cache, null, 2), 'utf-8');\n }\n}","/**\n * Smart alert system for regime transitions and signal changes.\n * Detects meaningful state changes and generates prioritized alerts.\n */\n\nimport { readFile, writeFile, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { createHash } from \"node:crypto\";\nimport type { MarketRegime } from \"./regime.js\";\nimport type { TokenAnalysis } from \"./index.js\";\n\nexport type AlertPriority = \"critical\" | \"high\" | \"medium\" | \"low\";\n\nexport interface Alert {\n id: string; // unique hash\n priority: AlertPriority;\n type: string; // e.g. \"regime-transition\", \"signal-divergence\", \"volume-anomaly\"\n title: string; // short headline\n details: string; // full explanation\n tokenId: string;\n timestamp: number;\n acknowledged: boolean;\n sent: boolean; // whether alert was sent via Telegram\n}\n\nexport interface AlertConfig {\n enableRegimeAlerts: boolean;\n enableSignalAlerts: boolean;\n enableVolumeAlerts: boolean;\n minPriority: AlertPriority;\n}\n\ninterface AlertState {\n lastRegime: MarketRegime | null;\n lastRegimeAt: number;\n lastSignals: Record<string, number>;\n alerts: Alert[];\n lastVolumeCheck: Record<string, { twitterVolume: number; hlVolume?: number; timestamp: number }>;\n}\n\nconst DEFAULT_CONFIG: AlertConfig = {\n enableRegimeAlerts: true,\n enableSignalAlerts: true,\n enableVolumeAlerts: true,\n minPriority: \"medium\",\n};\n\nexport class AlertSystem {\n private stateDir: string;\n private stateFile: string;\n private configFile: string;\n private config: AlertConfig;\n\n constructor() {\n this.stateDir = join(homedir(), '.sherwood', 'agent');\n this.stateFile = join(this.stateDir, 'alerts-state.json');\n this.configFile = join(this.stateDir, 'alerts-config.json');\n this.config = DEFAULT_CONFIG;\n }\n\n /**\n * Process analysis results and generate alerts for state changes.\n */\n async processAnalysis(analyses: TokenAnalysis[]): Promise<Alert[]> {\n const state = await this.loadState();\n const newAlerts: Alert[] = [];\n\n // Process regime transitions (using first analysis result with regime data)\n const regimeAnalysis = analyses.find(a => a.regime);\n if (regimeAnalysis?.regime && this.config.enableRegimeAlerts) {\n const regimeAlerts = this.checkRegimeTransition(state, regimeAnalysis.regime.regime);\n newAlerts.push(...regimeAlerts);\n\n state.lastRegime = regimeAnalysis.regime.regime;\n state.lastRegimeAt = Date.now();\n }\n\n // Process signal divergences for each token\n if (this.config.enableSignalAlerts) {\n for (const analysis of analyses) {\n const signalAlerts = this.checkSignalDivergence(state, analysis);\n newAlerts.push(...signalAlerts);\n\n // Update last signal for this token\n state.lastSignals[analysis.token] = analysis.decision.score;\n }\n }\n\n // Process volume anomalies if data is available\n if (this.config.enableVolumeAlerts) {\n for (const analysis of analyses) {\n const volumeAlerts = await this.checkVolumeAnomalies(state, analysis);\n newAlerts.push(...volumeAlerts);\n }\n }\n\n // Add new alerts to state and deduplicate\n for (const alert of newAlerts) {\n if (!this.isAlertDuplicate(state, alert)) {\n state.alerts.push(alert);\n }\n }\n\n // Prune old alerts (keep last 50)\n if (state.alerts.length > 50) {\n state.alerts = state.alerts.slice(-50);\n }\n\n await this.saveState(state);\n return newAlerts.filter(alert => this.shouldShowAlert(alert));\n }\n\n /**\n * Get recent alerts, optionally filtered by priority.\n */\n async getRecentAlerts(maxAge?: number, minPriority?: AlertPriority): Promise<Alert[]> {\n const state = await this.loadState();\n const now = Date.now();\n const ageLimit = maxAge ?? (24 * 60 * 60 * 1000); // Default: 24 hours\n\n return state.alerts\n .filter(alert => (now - alert.timestamp) <= ageLimit)\n .filter(alert => !minPriority || this.priorityValue(alert.priority) >= this.priorityValue(minPriority))\n .sort((a, b) => b.timestamp - a.timestamp);\n }\n\n /**\n * Clear all alerts by marking them as acknowledged.\n */\n async clearAlerts(): Promise<void> {\n const state = await this.loadState();\n for (const alert of state.alerts) {\n alert.acknowledged = true;\n }\n await this.saveState(state);\n }\n\n /**\n * Get unsent CRITICAL/HIGH alerts from last 30min and mark them as sent.\n */\n async getUrgentAlerts(): Promise<Alert[]> {\n const state = await this.loadState();\n const now = Date.now();\n const thirtyMinutes = 30 * 60 * 1000;\n\n // Find unsent critical/high alerts from last 30min\n const urgentAlerts = state.alerts.filter(alert =>\n !alert.sent &&\n !alert.acknowledged &&\n (alert.priority === \"critical\" || alert.priority === \"high\") &&\n (now - alert.timestamp) <= thirtyMinutes\n );\n\n if (urgentAlerts.length === 0) {\n return [];\n }\n\n // Mark alerts as sent\n for (const alert of urgentAlerts) {\n alert.sent = true;\n }\n\n await this.saveState(state);\n return urgentAlerts.sort((a, b) => b.timestamp - a.timestamp);\n }\n\n /**\n * Check for regime transitions and generate alerts.\n */\n private checkRegimeTransition(state: AlertState, currentRegime: MarketRegime): Alert[] {\n const alerts: Alert[] = [];\n\n if (!state.lastRegime || state.lastRegime === currentRegime) {\n return alerts; // No transition\n }\n\n const transition = `${state.lastRegime} → ${currentRegime}`;\n\n // Define transition priorities and messages\n const transitionMap: Record<string, { priority: AlertPriority; title: string; details: string }> = {\n \"ranging → trending-up\": {\n priority: \"high\",\n title: \"Market structure shifted bullish\",\n details: \"Market regime changed from ranging to trending-up. Breakout strategies now activating. Consider increasing position sizes for momentum plays.\"\n },\n \"ranging → trending-down\": {\n priority: \"high\",\n title: \"Market structure shifted bearish\",\n details: \"Market regime changed from ranging to trending-down. Risk-off mode engaged. Consider reducing exposure and defensive positioning.\"\n },\n \"trending-up → ranging\": {\n priority: \"medium\",\n title: \"Uptrend losing momentum\",\n details: \"Market regime changed from trending-up to ranging. Switching to mean reversion strategies. Momentum strategies reduced effectiveness.\"\n },\n \"trending-up → trending-down\": {\n priority: \"critical\",\n title: \"Trend reversal — bull to bear transition\",\n details: \"Major regime shift from trending-up to trending-down. Consider significant risk reduction and potential short positions.\"\n },\n \"trending-down → trending-up\": {\n priority: \"critical\",\n title: \"Trend reversal — bear to bull transition\",\n details: \"Major regime shift from trending-down to trending-up. Consider increasing long exposure and reducing hedges.\"\n },\n \"trending-up → high-volatility\": {\n priority: \"high\",\n title: \"Volatility spike during uptrend\",\n details: \"Market regime changed to high-volatility from trending-up. Reducing position sizes due to increased uncertainty.\"\n },\n \"trending-down → high-volatility\": {\n priority: \"high\",\n title: \"Volatility spike during downtrend\",\n details: \"Market regime changed to high-volatility from trending-down. Extreme caution advised - reducing position sizes.\"\n },\n \"ranging → high-volatility\": {\n priority: \"high\",\n title: \"Volatility breakout from ranging\",\n details: \"Market regime changed to high-volatility from ranging. Preparing for directional moves but reducing sizes due to uncertainty.\"\n },\n \"high-volatility → trending-up\": {\n priority: \"medium\",\n title: \"Volatility normalizing — bullish resolution\",\n details: \"Market regime stabilized from high-volatility to trending-up. Resuming normal position sizing with bullish bias.\"\n },\n \"high-volatility → trending-down\": {\n priority: \"medium\",\n title: \"Volatility normalizing — bearish resolution\",\n details: \"Market regime stabilized from high-volatility to trending-down. Resuming normal position sizing with bearish bias.\"\n },\n \"high-volatility → ranging\": {\n priority: \"medium\",\n title: \"Volatility normalizing — neutral resolution\",\n details: \"Market regime stabilized from high-volatility to ranging. Resuming normal position sizing, mean reversion strategies activated.\"\n }\n };\n\n const transitionData = transitionMap[transition];\n if (transitionData) {\n alerts.push(this.createAlert({\n type: \"regime-transition\",\n priority: transitionData.priority,\n title: transitionData.title,\n details: transitionData.details,\n tokenId: \"bitcoin\", // Regime is based on BTC\n }));\n } else {\n // Generic transition alert for unmapped combinations\n alerts.push(this.createAlert({\n type: \"regime-transition\",\n priority: \"medium\",\n title: `Market regime changed: ${transition}`,\n details: `Market regime transitioned from ${state.lastRegime} to ${currentRegime}. Adjusting strategy weights accordingly.`,\n tokenId: \"bitcoin\",\n }));\n }\n\n return alerts;\n }\n\n /**\n * Check for significant signal changes.\n */\n private checkSignalDivergence(state: AlertState, analysis: TokenAnalysis): Alert[] {\n const alerts: Alert[] = [];\n const currentScore = analysis.decision.score;\n const lastScore = state.lastSignals[analysis.token];\n\n if (lastScore === undefined) {\n return alerts; // First time seeing this token\n }\n\n const scoreChange = currentScore - lastScore;\n const absChange = Math.abs(scoreChange);\n\n // Signal flip (positive to negative or vice versa)\n if ((lastScore > 0 && currentScore < 0) || (lastScore < 0 && currentScore > 0)) {\n alerts.push(this.createAlert({\n type: \"signal-divergence\",\n priority: \"medium\",\n title: `${analysis.token.toUpperCase()} signal flipped ${lastScore > 0 ? 'negative' : 'positive'}`,\n details: `Signal changed from ${lastScore > 0 ? '+' : ''}${lastScore.toFixed(2)} to ${currentScore > 0 ? '+' : ''}${currentScore.toFixed(2)}. Consider reviewing position.`,\n tokenId: analysis.token,\n }));\n }\n // Rapid signal shift (>0.5 change in one scan)\n else if (absChange > 0.5) {\n alerts.push(this.createAlert({\n type: \"signal-divergence\",\n priority: \"high\",\n title: `Rapid signal shift for ${analysis.token.toUpperCase()}`,\n details: `Signal changed by ${scoreChange > 0 ? '+' : ''}${scoreChange.toFixed(2)} in one scan (${lastScore.toFixed(2)} → ${currentScore.toFixed(2)}). Significant momentum detected.`,\n tokenId: analysis.token,\n }));\n }\n\n return alerts;\n }\n\n /**\n * Check for volume anomalies across Twitter and Hyperliquid.\n */\n private async checkVolumeAnomalies(state: AlertState, analysis: TokenAnalysis): Promise<Alert[]> {\n const alerts: Alert[] = [];\n\n // This is a placeholder for volume anomaly detection\n // In a real implementation, you would access Twitter and Hyperliquid volume data\n // from the analysis context and compare against historical averages\n\n const lastVolumeData = state.lastVolumeCheck[analysis.token];\n const now = Date.now();\n\n // Example logic - you would replace this with actual volume data from analysis\n // if (analysis.data.twitterData?.volumeSpike && analysis.data.hyperliquidData?.volumeRatio) {\n // const twitterSpike = analysis.data.twitterData.volumeSpike;\n // const hlVolumeRatio = analysis.data.hyperliquidData.volumeRatio;\n //\n // if (twitterSpike > 5 && hlVolumeRatio > 2) {\n // alerts.push(this.createAlert({\n // type: \"volume-anomaly\",\n // priority: \"high\",\n // title: `Coordinated volume surge for ${analysis.token.toUpperCase()}`,\n // details: `Twitter volume ${twitterSpike.toFixed(1)}x spike with ${hlVolumeRatio.toFixed(1)}x Hyperliquid volume. Monitor for breakout.`,\n // tokenId: analysis.token,\n // }));\n // }\n //\n // // Update volume tracking\n // state.lastVolumeCheck[analysis.token] = {\n // twitterVolume: twitterSpike,\n // hlVolume: hlVolumeRatio,\n // timestamp: now,\n // };\n // }\n\n return alerts;\n }\n\n /**\n * Create an alert with unique ID and timestamp.\n */\n private createAlert(params: Omit<Alert, 'id' | 'timestamp' | 'acknowledged' | 'sent'>): Alert {\n const content = `${params.type}-${params.tokenId}-${params.title}`;\n const id = createHash('sha256').update(content).digest('hex').substring(0, 12);\n\n return {\n id,\n timestamp: Date.now(),\n acknowledged: false,\n sent: false,\n ...params,\n };\n }\n\n /**\n * Check if alert is duplicate within deduplication window.\n */\n private isAlertDuplicate(state: AlertState, newAlert: Alert): boolean {\n const DEDUP_WINDOW = 60 * 60 * 1000; // 1 hour\n const now = Date.now();\n\n return state.alerts.some(existing =>\n existing.type === newAlert.type &&\n existing.tokenId === newAlert.tokenId &&\n existing.title === newAlert.title &&\n (now - existing.timestamp) < DEDUP_WINDOW &&\n !existing.acknowledged\n );\n }\n\n /**\n * Determine if alert should be shown based on config.\n */\n private shouldShowAlert(alert: Alert): boolean {\n return this.priorityValue(alert.priority) >= this.priorityValue(this.config.minPriority);\n }\n\n /**\n * Convert priority to numeric value for comparison.\n */\n private priorityValue(priority: AlertPriority): number {\n const values = { low: 1, medium: 2, high: 3, critical: 4 };\n return values[priority];\n }\n\n /**\n * Load alert state from disk.\n */\n private async loadState(): Promise<AlertState> {\n try {\n const data = await readFile(this.stateFile, 'utf-8');\n return JSON.parse(data) as AlertState;\n } catch {\n return {\n lastRegime: null,\n lastRegimeAt: 0,\n lastSignals: {},\n alerts: [],\n lastVolumeCheck: {},\n };\n }\n }\n\n /**\n * Save alert state to disk.\n */\n private async saveState(state: AlertState): Promise<void> {\n try {\n await mkdir(this.stateDir, { recursive: true });\n await writeFile(this.stateFile, JSON.stringify(state, null, 2), 'utf-8');\n } catch (err) {\n console.error(`Failed to save alert state: ${(err as Error).message}`);\n }\n }\n\n /**\n * Format alerts for CLI display.\n */\n formatAlerts(alerts: Alert[], useMarkdown: boolean = false): string {\n if (alerts.length === 0) {\n return useMarkdown ? \"*No active alerts*\" : \" No active alerts\";\n }\n\n const lines: string[] = [];\n\n if (useMarkdown) {\n lines.push(\"## ALERTS\");\n } else {\n lines.push(\"ALERTS\");\n }\n\n for (const alert of alerts.slice(0, 10)) { // Show max 10\n const ageStr = this.formatAge(Date.now() - alert.timestamp);\n const icon = this.getPriorityIcon(alert.priority);\n\n if (useMarkdown) {\n lines.push(` ${icon} **${alert.priority.toUpperCase()}**: ${alert.title} (${ageStr})`);\n } else {\n lines.push(` ${icon} ${alert.priority.toUpperCase()}: ${alert.title} (${ageStr})`);\n }\n }\n\n return lines.join(\"\\n\");\n }\n\n /**\n * Get emoji icon for alert priority.\n */\n private getPriorityIcon(priority: AlertPriority): string {\n switch (priority) {\n case \"critical\": return \"🔴\";\n case \"high\": return \"🟡\";\n case \"medium\": return \"🔵\";\n case \"low\": return \"⚫\";\n }\n }\n\n /**\n * Format time difference for display.\n */\n private formatAge(ageMs: number): string {\n const minutes = Math.floor(ageMs / (60 * 1000));\n const hours = Math.floor(minutes / 60);\n const days = Math.floor(hours / 24);\n\n if (days > 0) return `${days}d ago`;\n if (hours > 0) return `${hours}h ago`;\n if (minutes > 0) return `${minutes}m ago`;\n return \"just now\";\n }\n}","/**\n * Dynamic token selection from Hyperliquid market data.\n * Selects the most interesting tokens to analyze based on volume, funding rates, and other criteria.\n */\n\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { safeNumber } from '../providers/data/hyperliquid.js';\n\nconst HYPERLIQUID_BASE = 'https://api.hyperliquid.xyz/info';\n\nexport interface TokenSelection {\n tokens: string[]; // CoinGecko IDs for the agent to analyze\n reason: Record<string, string>; // why each token was selected\n totalMarketsScanned: number;\n timestamp: number;\n}\n\nexport interface SelectionCriteria {\n minVolume24h: number; // minimum 24h volume in USD (default: $5M)\n topByVolume: number; // take top N by volume (default: 10)\n fundingExtremes: number; // take N tokens with most extreme funding (default: 5)\n minFundingRate: number; // absolute funding rate threshold (default: 0.03%)\n}\n\ninterface MetaAndAssetCtxs {\n universe: Array<{\n name: string;\n szDecimals: number;\n }>;\n}\n\ninterface AssetCtx {\n funding: string;\n openInterest: string;\n dayNtlVlm: string;\n markPx: string;\n oraclePx: string;\n prevDayPx: string;\n}\n\ninterface MarketInfo {\n name: string;\n volume24h: number;\n funding: number;\n openInterest: number;\n markPrice: number;\n coingeckoId?: string;\n}\n\n// Comprehensive mapping from Hyperliquid coin names to CoinGecko IDs\nconst HYPERLIQUID_TO_COINGECKO: Record<string, string> = {\n BTC: 'bitcoin',\n ETH: 'ethereum',\n SOL: 'solana',\n HYPE: 'hyperliquid', // May not exist on CG, but try\n XRP: 'ripple',\n DOGE: 'dogecoin',\n AVAX: 'avalanche-2',\n LINK: 'chainlink',\n SUI: 'sui',\n NEAR: 'near',\n AAVE: 'aave',\n ARB: 'arbitrum',\n TAO: 'bittensor',\n TRUMP: 'official-trump',\n ZEC: 'zcash',\n BNB: 'binancecoin',\n OP: 'optimism',\n DOT: 'polkadot',\n ADA: 'cardano',\n ATOM: 'cosmos',\n UNI: 'uniswap',\n MKR: 'maker',\n INJ: 'injective-protocol',\n PENDLE: 'pendle',\n PEPE: 'pepe',\n ENA: 'ethena',\n TIA: 'celestia',\n SEI: 'sei-network',\n APT: 'aptos',\n FIL: 'filecoin',\n RENDER: 'render-token',\n JUP: 'jupiter-exchange-solana',\n STX: 'blockstack',\n WLD: 'worldcoin-wld',\n FET: 'fetch-ai',\n ONDO: 'ondo-finance',\n LDO: 'lido-dao',\n CRV: 'curve-dao-token',\n BLUR: 'blur',\n EIGEN: 'eigenlayer',\n POL: 'polygon-ecosystem-token',\n FARTCOIN: 'fartcoin',\n LTC: 'litecoin',\n MATIC: 'polygon-ecosystem-token', // POL is the new MATIC\n TNSR: 'tensor',\n GRASS: 'grass',\n MOVE: 'move-network',\n GOAT: 'goatseus-maximus',\n PNUT: 'peanut-the-squirrel',\n POPCAT: 'popcat',\n NEIRO: 'neiro',\n WIF: 'dogwifcoin',\n BONK: 'bonk',\n FLOKI: 'floki',\n SHIB: 'shiba-inu',\n MEW: 'cat-in-a-dogs-world',\n MEME: 'memecoin',\n AI16Z: 'ai16z',\n VIRTUAL: 'virtual-protocol',\n AIXBT: 'aixbt-by-virtuals',\n ZEREBRO: 'zerebro',\n arc: 'arc',\n HYPURR: 'hypurr',\n ELIZA: 'eliza',\n GRIFFAIN: 'griffain',\n LUNA: 'ai-analysis-token',\n DEEP: 'deep-worm',\n POLY: 'polyhedra-network',\n VANA: 'vana',\n VELODROME: 'velodrome-finance',\n ACX: 'across-protocol',\n COOKIE: 'cookie-dao',\n USUAL: 'usual',\n PENGU: 'pudgy-penguins',\n ETHFi: 'ether-fi',\n ETHFI: 'ether-fi', // Alternative spelling\n RSR: 'reserve-rights',\n CKB: 'nervos-network',\n RUNE: 'thorchain',\n MANA: 'decentraland',\n SAND: 'the-sandbox',\n ENS: 'ethereum-name-service',\n LRC: 'loopring',\n IMX: 'immutable-x',\n GALA: 'gala',\n CHZ: 'chiliz',\n BAT: 'basic-attention-token',\n ZRX: '0x',\n COMP: 'compound-governance-token',\n SUSHI: 'sushi',\n SNX: 'havven',\n DYDX: 'dydx',\n GMX: 'gmx',\n GRT: 'the-graph',\n FLOW: 'flow',\n KAVA: 'kava',\n ROSE: 'oasis-network',\n ONE: 'harmony',\n ALGO: 'algorand',\n XTZ: 'tezos',\n EGLD: 'elrond-erd-2',\n FTM: 'fantom',\n CAKE: 'pancakeswap-token',\n CVX: 'convex-finance',\n SPELL: 'spell-token',\n ALPHA: 'alpha-finance',\n BADGER: 'badger-dao',\n STKETH: 'lido-staked-ether',\n RETH: 'rocket-pool-eth',\n WSTETH: 'wrapped-steth',\n STETH: 'staked-ether',\n USDC: 'usd-coin',\n USDT: 'tether',\n DAI: 'dai',\n FRAX: 'frax',\n USDD: 'usdd',\n TUSD: 'trueusd',\n LUSD: 'liquity-usd',\n MIM: 'magic-internet-money',\n GUSD: 'gemini-dollar',\n PYUSD: 'paypal-usd',\n USDP: 'paxos-standard',\n};\n\nexport class DynamicTokenSelector {\n private cacheDir: string;\n private cacheTTL = 30 * 60 * 1000; // 30 minutes\n\n constructor() {\n this.cacheDir = join(homedir(), '.sherwood', 'agent', 'cache');\n }\n\n /** Get dynamic token selection based on Hyperliquid market data. */\n async selectTokens(criteria?: Partial<SelectionCriteria>): Promise<TokenSelection> {\n const config: SelectionCriteria = {\n minVolume24h: 5_000_000, // $5M minimum\n topByVolume: 18, // bumped 10 → 18 for more shots on goal per cycle\n fundingExtremes: 5,\n minFundingRate: 0.0003, // 0.03%\n ...criteria\n };\n\n // Check cache first\n const cached = await this.readCache();\n if (cached) {\n console.log(`Using cached token selection (${cached.tokens.length} tokens)`);\n return cached;\n }\n\n try {\n // Fetch all Hyperliquid markets\n const marketData = await this.fetchHyperliquidMarkets();\n if (!marketData) {\n console.warn('Failed to fetch Hyperliquid data, using default tokens');\n return this.getDefaultSelection();\n }\n\n // Apply selection algorithm\n const selection = this.applySelectionCriteria(marketData, config);\n\n // Cache the results\n await this.writeCache(selection);\n\n return selection;\n } catch (error) {\n console.error(`Token selection failed: ${(error as Error).message}`);\n return this.getDefaultSelection();\n }\n }\n\n /** Fetch all Hyperliquid market data. */\n private async fetchHyperliquidMarkets(): Promise<MarketInfo[] | null> {\n try {\n const response = await fetch(HYPERLIQUID_BASE, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ type: 'metaAndAssetCtxs' }),\n });\n\n if (!response.ok) return null;\n\n const [meta, assetCtxs] = await response.json() as [MetaAndAssetCtxs, AssetCtx[]];\n\n const markets: MarketInfo[] = [];\n\n for (let i = 0; i < meta.universe.length && i < assetCtxs.length; i++) {\n const coin = meta.universe[i]!;\n const ctx = assetCtxs[i]!;\n\n const name = coin.name;\n // Reject rows with any non-finite field — a single NaN would otherwise\n // corrupt selection criteria downstream (volume filters, funding sort).\n const volume24h = safeNumber(ctx.dayNtlVlm);\n const funding = safeNumber(ctx.funding);\n const openInterest = safeNumber(ctx.openInterest);\n const markPrice = safeNumber(ctx.markPx);\n if (volume24h === null || funding === null || openInterest === null || markPrice === null) {\n continue;\n }\n const coingeckoId = HYPERLIQUID_TO_COINGECKO[name];\n\n markets.push({\n name,\n volume24h,\n funding,\n openInterest,\n markPrice,\n coingeckoId,\n });\n }\n\n return markets;\n } catch (error) {\n console.error(`Failed to fetch Hyperliquid markets: ${(error as Error).message}`);\n return null;\n }\n }\n\n /** Apply selection criteria to market data. */\n private applySelectionCriteria(markets: MarketInfo[], criteria: SelectionCriteria): TokenSelection {\n const selectedTokens = new Set<string>();\n const reasons: Record<string, string> = {};\n\n // Filter markets that have CoinGecko mappings and minimum volume\n const validMarkets = markets.filter(m =>\n m.coingeckoId &&\n m.volume24h >= 1_000_000 // At least $1M for any consideration\n );\n\n // 1. Always include BTC and ETH (macro indicators)\n const btc = validMarkets.find(m => m.name === 'BTC');\n const eth = validMarkets.find(m => m.name === 'ETH');\n\n if (btc?.coingeckoId) {\n selectedTokens.add(btc.coingeckoId);\n reasons[btc.coingeckoId] = 'Always included: macro indicator';\n }\n\n if (eth?.coingeckoId) {\n selectedTokens.add(eth.coingeckoId);\n reasons[eth.coingeckoId] = 'Always included: macro indicator';\n }\n\n // 2. Top N by volume (meeting minimum threshold)\n const topByVolume = validMarkets\n .filter(m => m.volume24h >= criteria.minVolume24h)\n .sort((a, b) => b.volume24h - a.volume24h)\n .slice(0, criteria.topByVolume);\n\n for (const market of topByVolume) {\n if (market.coingeckoId) {\n selectedTokens.add(market.coingeckoId);\n if (!reasons[market.coingeckoId]) {\n const volumeStr = market.volume24h >= 1_000_000_000\n ? `$${(market.volume24h / 1_000_000_000).toFixed(1)}B`\n : `$${(market.volume24h / 1_000_000).toFixed(0)}M`;\n reasons[market.coingeckoId] = `Top volume: ${volumeStr} 24h`;\n }\n }\n }\n\n // 3. Extreme funding rates\n const extremeFunding = validMarkets\n .filter(m => Math.abs(m.funding) >= criteria.minFundingRate && m.volume24h >= 1_000_000)\n .sort((a, b) => Math.abs(b.funding) - Math.abs(a.funding))\n .slice(0, criteria.fundingExtremes);\n\n for (const market of extremeFunding) {\n if (market.coingeckoId) {\n selectedTokens.add(market.coingeckoId);\n if (!reasons[market.coingeckoId] || reasons[market.coingeckoId]?.startsWith('Always')) {\n const fundingPercent = (market.funding * 100).toFixed(3);\n const direction = market.funding > 0 ? 'longs paying' : 'shorts paying';\n reasons[market.coingeckoId] = `Extreme funding: ${fundingPercent}% (${direction})`;\n }\n }\n }\n\n // Cap at 25 tokens — bumped from 20 to fit the wider topByVolume sweep\n // while staying under CoinGecko's free-tier rate limit (~30 req/min).\n const tokens = Array.from(selectedTokens).slice(0, 25);\n\n return {\n tokens,\n reason: Object.fromEntries(\n tokens.map(token => [token, reasons[token] || 'Selected by criteria'])\n ),\n totalMarketsScanned: markets.length,\n timestamp: Date.now(),\n };\n }\n\n /** Get default token selection when Hyperliquid is unavailable. */\n private getDefaultSelection(): TokenSelection {\n const defaultTokens = ['bitcoin', 'ethereum', 'solana', 'arbitrum', 'chainlink', 'aave', 'uniswap'];\n const reasons = Object.fromEntries(\n defaultTokens.map(token => [token, 'Fallback: Hyperliquid unavailable'])\n );\n\n return {\n tokens: defaultTokens,\n reason: reasons,\n totalMarketsScanned: 0,\n timestamp: Date.now(),\n };\n }\n\n /** Read cached token selection. */\n private async readCache(): Promise<TokenSelection | null> {\n try {\n const cacheFile = join(this.cacheDir, 'token-selection.json');\n const raw = await readFile(cacheFile, 'utf-8');\n const cached = JSON.parse(raw) as TokenSelection;\n\n if (Date.now() - cached.timestamp < this.cacheTTL) {\n return cached;\n }\n } catch {\n // No cache or invalid cache\n }\n return null;\n }\n\n /** Write token selection to cache. */\n private async writeCache(selection: TokenSelection): Promise<void> {\n try {\n await mkdir(this.cacheDir, { recursive: true });\n const cacheFile = join(this.cacheDir, 'token-selection.json');\n await writeFile(cacheFile, JSON.stringify(selection, null, 2), 'utf-8');\n } catch {\n // Cache write failure is non-fatal\n }\n }\n\n /** Format token selection summary for display. */\n formatSelectionSummary(selection: TokenSelection): string {\n const lines: string[] = [];\n\n lines.push(`Dynamic Token Selection (${selection.tokens.length} tokens from ${selection.totalMarketsScanned} HL markets)`);\n lines.push('──────────────────────────────────────────');\n\n // Group tokens by reason type\n const topVolume: string[] = [];\n const fundingPlays: string[] = [];\n const always: string[] = [];\n const other: string[] = [];\n\n for (const token of selection.tokens) {\n const reason = selection.reason[token] || '';\n if (reason.includes('Top volume')) {\n topVolume.push(token.toUpperCase());\n } else if (reason.includes('Extreme funding')) {\n const match = reason.match(/(-?\\d+\\.\\d+)%/);\n const rate = match ? ` (${match[1]}%)` : '';\n fundingPlays.push(`${token.toUpperCase()}${rate}`);\n } else if (reason.includes('Always included')) {\n always.push(token.toUpperCase());\n } else {\n other.push(token.toUpperCase());\n }\n }\n\n if (topVolume.length > 0) {\n lines.push(`Top volume: ${topVolume.join(', ')}`);\n }\n if (fundingPlays.length > 0) {\n lines.push(`Funding plays: ${fundingPlays.join(', ')}`);\n }\n if (always.length > 0) {\n lines.push(`Always: ${always.join(', ')}`);\n }\n if (other.length > 0) {\n lines.push(`Other: ${other.join(', ')}`);\n }\n\n return lines.join('\\n');\n }\n}","/**\n * Autonomous agent loop — runs analysis + execution cycles on a timer.\n */\n\nimport chalk from 'chalk';\nimport { mkdir, appendFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { TradingAgent } from './index.js';\nimport type { AgentConfig } from './index.js';\nimport { TradeExecutor } from './executor.js';\nimport type { ExecutionConfig } from './executor.js';\nimport { PortfolioTracker, resetPnlCounters } from './portfolio.js';\nimport { RiskManager, DEFAULT_RISK_CONFIG } from './risk.js';\nimport type { RiskConfig, PortfolioState } from './risk.js';\nimport { CoinGeckoProvider } from '../providers/data/coingecko.js';\nimport { Reporter } from './reporter.js';\nimport { DynamicTokenSelector } from './token-selector.js';\nimport { PriceValidator } from './price-validator.js';\n\nexport interface LoopConfig {\n agent: AgentConfig;\n execution: ExecutionConfig;\n riskConfig?: Partial<RiskConfig>;\n reportToTelegram?: boolean;\n logPath?: string;\n autoDynamicSelection?: boolean;\n}\n\nexport interface CycleResult {\n cycleNumber: number;\n timestamp: number;\n duration: number;\n tokensAnalyzed: number;\n signals: Array<{ token: string; score: number; action: string; regime?: string }>;\n tradesExecuted: number;\n exitsProcessed: number;\n portfolioValue: number;\n /** Realized PnL since UTC day start (closed trades only). */\n dailyRealizedPnl: number;\n /** Sum of mark-to-market pnlUsd across all open positions at cycle end. */\n unrealizedPnl: number;\n /** @deprecated Alias for dailyRealizedPnl — retained for downstream consumers of cycles.jsonl. */\n dailyPnl: number;\n /** Cumulative PnL in USD since the portfolio was initialized\n * (`portfolioValue - portfolio.initialValue`). Includes both realized\n * closes and open-position mark-to-market. */\n totalPnlUsd: number;\n /** Cumulative PnL as a fraction of initial value (e.g. +0.0162 = +1.62%). */\n totalPnlPct: number;\n errors: string[];\n}\n\nexport class AgentLoop {\n private running = false;\n private cycleCount = 0;\n private config: LoopConfig;\n private agent: TradingAgent;\n private executor: TradeExecutor;\n private portfolio: PortfolioTracker;\n private riskManager: RiskManager;\n private coingecko: CoinGeckoProvider;\n private reporter: Reporter;\n /** Rejects bad-price ticks (floor + 20% delta cap). Persisted to disk so\n * anchors survive across cron invocations. */\n private priceValidator: PriceValidator;\n /** Last portfolio state loaded by runCycle(). Exposed to the judge via\n * agent.setPortfolioStateGetter() so portfolio-veto branches can fire. */\n private lastPortfolioState: PortfolioState | undefined;\n\n constructor(config: LoopConfig) {\n this.config = config;\n this.agent = new TradingAgent(config.agent);\n this.portfolio = new PortfolioTracker();\n this.riskManager = new RiskManager(config.riskConfig);\n this.executor = new TradeExecutor(config.execution, this.riskManager, this.portfolio);\n this.coingecko = new CoinGeckoProvider();\n this.reporter = new Reporter();\n this.priceValidator = new PriceValidator();\n // Wire real portfolio state into the judge (replaces hardcoded zeros).\n this.agent.setPortfolioStateGetter(() => this.lastPortfolioState);\n }\n\n /** Start the autonomous loop */\n async start(): Promise<void> {\n this.running = true;\n\n // Ensure state directory exists\n const stateDir = join(homedir(), '.sherwood', 'agent');\n await mkdir(stateDir, { recursive: true });\n\n // Initialize portfolio from on-chain vault balance if no persisted state.\n // This replaces the $10k default with the actual vault USDC balance so\n // position sizing and risk management reflect real capital.\n if (this.config.execution.strategyClone && this.config.execution.chain) {\n await this.portfolio.initFromOnChain(\n this.config.execution.strategyClone,\n this.config.execution.chain,\n );\n }\n\n // Startup banner\n const cfg = this.config.agent;\n console.log('');\n console.log(chalk.bold(' ┌──────────────────────────────────────────────┐'));\n console.log(chalk.bold(' │ Sherwood Trading Agent — Loop │'));\n console.log(chalk.bold(' └──────────────────────────────────────────────┘'));\n console.log(` Mode: ${cfg.dryRun ? chalk.yellow('DRY RUN (paper trading)') : chalk.red('LIVE')}`);\n console.log(` Cycle: ${cfg.cycle}`);\n console.log(` Tokens: ${cfg.tokens.join(', ')}`);\n console.log(` Risk: max ${(this.config.riskConfig?.maxSinglePosition ?? DEFAULT_RISK_CONFIG.maxSinglePosition) * 100}% per position`);\n console.log(` Log: ${this.config.logPath ?? 'console only'}`);\n console.log(chalk.dim('\\n Press Ctrl+C to stop.\\n'));\n\n const cycleMs = parseCycleInterval(this.config.agent.cycle);\n\n while (this.running) {\n try {\n await this.runCycle();\n } catch (err) {\n console.error(chalk.red(` Cycle failed: ${(err as Error).message}`));\n }\n\n // Single-cycle mode: --cycle 1 (bare \"1\") means run exactly one cycle\n // then exit cleanly so the cron skill can proceed to XMTP send.\n // parseCycleInterval returns 0 for this case.\n if (cycleMs === 0 || !this.running) break;\n\n console.log(chalk.dim(` Next cycle in ${this.config.agent.cycle}. Sleeping...`));\n await this.sleepInterruptible(cycleMs);\n }\n\n console.log(chalk.bold('\\n Agent loop stopped.\\n'));\n\n // Force-exit after a short grace period. XMTP client, CoinGecko HTTP\n // agents, and other async handles can keep the Node event loop alive\n // indefinitely. In cron mode (single cycle) this manifests as the\n // process hanging after \"Shutting down gracefully...\" until hermes\n // kills it at script_timeout_seconds. 500ms is enough for any pending\n // file writes (portfolio.json, cycles.jsonl) to flush.\n setTimeout(() => process.exit(0), 500);\n }\n\n /** Run a single analysis + execution cycle */\n async runCycle(): Promise<CycleResult> {\n this.cycleCount++;\n const startTime = Date.now();\n const errors: string[] = [];\n let tradesExecuted = 0;\n let exitsProcessed = 0;\n\n // 1. Load portfolio and check drawdown limits\n const state = await this.portfolio.load();\n\n // Reset PnL counters if time boundaries crossed\n const resetState = resetPnlCounters(state);\n if (resetState.dailyPnl !== state.dailyPnl || resetState.weeklyPnl !== state.weeklyPnl || resetState.monthlyPnl !== state.monthlyPnl) {\n await this.portfolio.save(resetState);\n }\n\n // Publish the refreshed state to the judge (via getter set in the ctor).\n this.lastPortfolioState = resetState;\n this.riskManager.updatePortfolio(resetState);\n const drawdown = this.riskManager.isDrawdownLimitHit();\n if (drawdown.paused) {\n console.log(chalk.red(` ⚠ Trading paused: ${drawdown.message}`));\n const unrealizedPnlAtPause = state.positions.reduce((sum, p) => sum + p.pnlUsd, 0);\n const initValPause = state.initialValue && state.initialValue > 0 ? state.initialValue : 10_000;\n const totalPnlUsdPause = state.totalValue - initValPause;\n const result: CycleResult = {\n cycleNumber: this.cycleCount,\n timestamp: Date.now(),\n duration: Date.now() - startTime,\n tokensAnalyzed: 0,\n signals: [],\n tradesExecuted: 0,\n exitsProcessed: 0,\n portfolioValue: state.totalValue,\n dailyRealizedPnl: state.dailyPnl,\n unrealizedPnl: unrealizedPnlAtPause,\n dailyPnl: state.dailyPnl,\n totalPnlUsd: totalPnlUsdPause,\n totalPnlPct: totalPnlUsdPause / initValPause,\n errors: [drawdown.message],\n };\n await this.logCycle(result);\n return result;\n }\n\n // 2. Update prices for existing positions and process exits\n if (state.positions.length > 0) {\n try {\n const tokenIds = state.positions.map(p => p.tokenId);\n const priceData = await this.coingecko.getPrice(tokenIds, ['usd']);\n const currentPrices: Record<string, number> = {};\n\n for (const id of tokenIds) {\n const price = priceData?.[id]?.usd;\n if (typeof price !== 'number') continue;\n const check = this.priceValidator.check(id, price);\n if (!check.ok) {\n // Skip this token for the cycle — no MTM, no exit check, no trades.\n console.warn(\n chalk.yellow(\n ` ⚠ Bad price tick rejected: ${id} → $${price} (${check.reason})`,\n ),\n );\n errors.push(`Bad price rejected for ${id}: ${check.reason}`);\n continue;\n }\n currentPrices[id] = check.price;\n }\n\n if (Object.keys(currentPrices).length > 0) {\n const priceState = await this.portfolio.updatePrices(currentPrices);\n\n // Ratchet stops (breakeven + profit-lock + percent-trail) before\n // checking exits. This activates trailing-stop logic that was\n // previously dead code (updateStopLosses was defined but never\n // called). Stops only move up — never loosened.\n const tightened = this.riskManager.updateTrailingStops(priceState.positions);\n const anyChanged = tightened.some(\n (p, i) => p.stopLoss !== priceState.positions[i]!.stopLoss,\n );\n if (anyChanged) {\n for (let i = 0; i < tightened.length; i++) {\n const before = priceState.positions[i]!;\n const after = tightened[i]!;\n if (after.stopLoss > before.stopLoss) {\n console.log(\n chalk.dim(\n ` Stop tightened: ${after.symbol} $${before.stopLoss.toFixed(4)} → $${after.stopLoss.toFixed(4)}`,\n ),\n );\n }\n }\n priceState.positions = tightened;\n await this.portfolio.save(priceState);\n }\n\n // Process exits (stop losses, take profits)\n const exits = await this.executor.processExits(currentPrices);\n exitsProcessed = exits.length;\n\n for (const exit of exits) {\n const pnlColor = exit.pnl >= 0 ? chalk.green : chalk.red;\n console.log(\n ` ${exit.reason}: ${exit.position.symbol} @ $${exit.exitPrice.toFixed(4)} ${pnlColor(`PnL: $${exit.pnl.toFixed(2)}`)}`,\n );\n }\n }\n } catch (err) {\n errors.push(`Price update failed: ${(err as Error).message}`);\n }\n }\n\n // 3. Update token list if using dynamic selection\n if (this.config.autoDynamicSelection) {\n try {\n const selector = new DynamicTokenSelector();\n const selection = await selector.selectTokens();\n\n // Update token list without recreating the agent (preserves OI cache, provider state)\n this.config.agent.tokens = selection.tokens;\n this.agent.updateTokens(selection.tokens);\n\n if (this.cycleCount % 6 === 1) { // Show selection summary every 6th cycle (~30min for 5min cycles)\n console.log(chalk.dim(` Updated tokens: ${selection.tokens.length} from ${selection.totalMarketsScanned} HL markets`));\n }\n } catch (err) {\n errors.push(`Dynamic selection failed: ${(err as Error).message}`);\n console.log(chalk.yellow(` Warning: Dynamic selection failed, using existing tokens`));\n }\n }\n\n // 4. Analyze all watchlist tokens\n console.log(chalk.dim(` Analyzing ${this.config.agent.tokens.length} tokens...`));\n const results = await this.agent.analyzeAll();\n\n // 5. Collect signals and execute trades for actionable ones\n const signals: CycleResult['signals'] = [];\n\n for (const result of results) {\n const { action, score } = result.decision;\n const regime = result.regime?.regime;\n signals.push({ token: result.token, score, action, regime });\n\n // Only execute on BUY/SELL signals\n if (action === 'STRONG_BUY' || action === 'BUY' || action === 'SELL' || action === 'STRONG_SELL') {\n try {\n // Get current price — prefer Hyperliquid markPrice (free, real-time, no rate limit),\n // fall back to CoinGecko. The prior CG-only path failed when the circuit breaker was\n // open, blocking execution of valid signals (e.g. SOL SELL at -0.119 failed with\n // \"CoinGecko circuit breaker open\" despite HL markPrice being available in the\n // analysis result).\n let rawPrice: number | undefined = result.data?.price as number | undefined;\n if (typeof rawPrice !== 'number' || rawPrice <= 0) {\n try {\n const priceData = await this.coingecko.getPrice([result.token], ['usd']);\n rawPrice = priceData?.[result.token]?.usd;\n } catch {\n // CG unavailable — rawPrice stays undefined, handled below\n }\n }\n\n if (typeof rawPrice !== 'number' || rawPrice <= 0) {\n errors.push(`No price data for ${result.token} (HL + CG both failed)`);\n continue;\n }\n\n const check = this.priceValidator.check(result.token, rawPrice);\n if (!check.ok) {\n console.warn(\n chalk.yellow(\n ` ⚠ Bad price tick rejected (entry): ${result.token} → $${rawPrice} (${check.reason})`,\n ),\n );\n errors.push(`Bad price rejected for ${result.token}: ${check.reason}`);\n continue;\n }\n\n const currentPrice = check.price;\n const atr = result.data?.technicalSignals?.atr;\n const kronosVol = result.kronosVol4h;\n const execResult = await this.executor.execute(result.decision, result.token, currentPrice, atr, kronosVol);\n if (execResult.success) {\n tradesExecuted++;\n console.log(this.executor.formatExecution(execResult));\n } else if (execResult.error && !execResult.error.includes('does not trigger')) {\n errors.push(`${result.token}: ${execResult.error}`);\n }\n } catch (err) {\n errors.push(`Execution failed for ${result.token}: ${(err as Error).message}`);\n }\n }\n }\n\n // 5. Reload portfolio after trades\n const updatedState = await this.portfolio.load();\n\n // 6. Build cycle result\n const unrealizedPnl = updatedState.positions.reduce((sum, p) => sum + p.pnlUsd, 0);\n const initVal = updatedState.initialValue && updatedState.initialValue > 0 ? updatedState.initialValue : 10_000;\n const totalPnlUsd = updatedState.totalValue - initVal;\n const cycleResult: CycleResult = {\n cycleNumber: this.cycleCount,\n timestamp: Date.now(),\n duration: Date.now() - startTime,\n tokensAnalyzed: results.length,\n signals,\n tradesExecuted,\n exitsProcessed,\n portfolioValue: updatedState.totalValue,\n dailyRealizedPnl: updatedState.dailyPnl,\n unrealizedPnl,\n dailyPnl: updatedState.dailyPnl,\n totalPnlUsd,\n totalPnlPct: totalPnlUsd / initVal,\n errors,\n };\n\n // 7. Log and report\n await this.logCycle(cycleResult);\n this.reportCycle(cycleResult);\n\n return cycleResult;\n }\n\n /** Stop the loop gracefully */\n stop(): void {\n this.running = false;\n }\n\n /** Whether the loop is currently running */\n get isRunning(): boolean {\n return this.running;\n }\n\n /** Log cycle result to a JSONL file */\n private async logCycle(result: CycleResult): Promise<void> {\n const logPath = this.config.logPath ?? join(homedir(), '.sherwood', 'agent', 'cycles.jsonl');\n try {\n await mkdir(join(homedir(), '.sherwood', 'agent'), { recursive: true });\n await appendFile(logPath, JSON.stringify(result) + '\\n', 'utf-8');\n } catch (err) {\n console.error(chalk.dim(` Failed to write cycle log: ${(err as Error).message}`));\n }\n }\n\n /** Display cycle report to console */\n private reportCycle(result: CycleResult): void {\n console.log(this.reporter.formatCycleReport(result));\n }\n\n /** Sleep that can be interrupted by stop() */\n private sleepInterruptible(ms: number): Promise<void> {\n return new Promise<void>((resolve) => {\n let resolved = false;\n const done = () => {\n if (resolved) return;\n resolved = true;\n clearTimeout(timer);\n clearInterval(check);\n resolve();\n };\n const timer = setTimeout(done, ms);\n const check = setInterval(() => {\n if (!this.running) done();\n }, 1000);\n });\n }\n\n}\n\n/** Parse cycle interval string to milliseconds.\n * Accepts: \"15m\", \"1h\", \"4h\", or bare \"1\" (single-cycle mode → returns 0).\n * Bare \"1\" is the cron convention (`sherwood agent start --auto --cycle 1`):\n * run exactly one cycle, then exit so the calling skill can XMTP/report. */\nfunction parseCycleInterval(cycle: string): number {\n // Try with explicit unit first (e.g. \"15m\", \"4h\")\n const match = cycle.match(/^(\\d+)(m|h)$/);\n if (match) {\n const value = parseInt(match[1]!, 10);\n if (match[2] === 'h') return value * 60 * 60 * 1000;\n return value * 60 * 1000; // minutes\n }\n // Bare \"1\" → single-cycle mode (0 = sentinel, loop breaks after first cycle).\n // Any other bare number → treat as minutes for continuous mode.\n const bare = parseInt(cycle, 10);\n if (!isNaN(bare) && bare === 1) return 0; // single cycle\n if (!isNaN(bare) && bare > 1) return bare * 60 * 1000;\n return 4 * 60 * 60 * 1000; // default 4h\n}\n","/**\n * Trade execution module — dry-run paper trading + live execution placeholder.\n */\n\nimport chalk from 'chalk';\nimport type { Address, Hex } from 'viem';\nimport type { TradeDecision } from './scoring.js';\nimport type { Position } from './risk.js';\nimport { RiskManager } from './risk.js';\nimport { PortfolioTracker } from './portfolio.js';\nimport { hlMarketBuy, hlMarketSell, resolveHLCoin, validateHLEnv } from '../lib/hyperliquid-executor.js';\n\nexport interface ExecutionConfig {\n dryRun: boolean;\n mevProtection: boolean;\n maxGasPrice?: bigint;\n chain: string;\n /** Execution mode: 'dry-run' (default) or 'hyperliquid-perp' (live on-chain) */\n mode?: 'dry-run' | 'hyperliquid-perp';\n /** Strategy clone address on HyperEVM (required for hyperliquid-perp mode) */\n strategyClone?: Address;\n /** Proposer private key (read from SHERWOOD_PROPOSER_KEY env) */\n proposerPrivateKey?: Hex;\n /** HyperCore perp asset index (default 3 = ETH) */\n assetIndex?: number;\n}\n\nexport interface OrderParams {\n tokenId: string;\n side: 'buy' | 'sell';\n amountUsd: number;\n maxSlippage: number;\n stopLoss: number;\n takeProfit: number;\n}\n\n/** Directional trade leverage (notional multiplier on order quantity).\n *\n * Cash model: PortfolioTracker debits the same `MARGIN_FRACTION = 0.33`\n * (33%) of notional for both longs and shorts on every open / pyramid add,\n * and credits margin + PnL on close. Live mode (hyperliquid-perp) uses\n * the venue's own margin schedule. See `cli/src/agent/portfolio.ts`.\n *\n * Autoresearch (80 exp, 4 days): 3x→2x→1x each reduced DD dramatically.\n * At 54% WR with avg loss > avg win, leverage amplifies the problem.\n * Revert to 1x until WR > 60% and avg win > avg loss. */\nconst DIRECTIONAL_LEVERAGE = 1;\n\n/** Score-based position sizing multiplier.\n * Nunchi autoresearch (103 experiments): removing strength/volume scaling\n * improved Sharpe by +1.7. Uniform position sizing beats conviction-weighted\n * because the score predicts DIRECTION, not MAGNITUDE. A high-score entry\n * isn't more likely to be a BIG winner — it's just more likely to be RIGHT.\n * Oversizing on high scores amplifies losses on the 40% that still stop out.\n * Kept as a function for easy revert if calibration proves otherwise. */\nfunction convictionMultiplier(_score: number): number {\n return 1.0; // uniform sizing — autoresearch-proven\n}\n\nexport class TradeExecutor {\n private config: ExecutionConfig;\n private riskManager: RiskManager;\n private portfolio: PortfolioTracker;\n private lastAtr?: number;\n\n constructor(config: ExecutionConfig, riskManager: RiskManager, portfolio: PortfolioTracker) {\n this.config = config;\n this.riskManager = riskManager;\n this.portfolio = portfolio;\n }\n\n /** CoinGecko token ID → Hyperliquid perp asset index.\n * Used by multi-asset execution to resolve which HL perp to trade\n * when a signal fires. One strategy clone can trade any of these. */\n private static readonly TOKEN_TO_ASSET_INDEX: Record<string, number> = {\n bitcoin: 0,\n ethereum: 1,\n solana: 2,\n arbitrum: 4,\n dogecoin: 6,\n chainlink: 8,\n aave: 10,\n uniswap: 11,\n ripple: 23,\n polkadot: 28,\n avalanche: 35,\n hyperliquid: 131,\n zcash: 144,\n bittensor: 148,\n \"worldcoin-wld\": 265,\n fartcoin: 343,\n \"fetch-ai\": 373,\n pepe: 166,\n pendle: 249,\n sui: 116,\n near: 71,\n aptos: 83,\n };\n\n /** Resolve a CoinGecko token ID to its HL perp asset index. */\n static resolveAssetIndex(tokenId: string): number | undefined {\n return TradeExecutor.TOKEN_TO_ASSET_INDEX[tokenId];\n }\n\n /** Execute a trade based on a decision.\n * @param predictedVol - Kronos ML-predicted per-candle volatility (fraction).\n * When provided, overrides ATR-based stop distance for more forward-looking risk. */\n async execute(\n decision: TradeDecision,\n tokenId: string,\n currentPrice: number,\n atr?: number,\n predictedVol?: number,\n ): Promise<{\n success: boolean;\n position?: Position;\n error?: string;\n dryRun: boolean;\n }> {\n // Token blacklist — serial losers identified from trade analysis.\n // AAVE: 4 trades, 1W, -$145. FARTCOIN: 2 trades, 0W, -$118.\n const BLACKLISTED_TOKENS = new Set(['aave', 'fartcoin', 'dogecoin']);\n if (BLACKLISTED_TOKENS.has(tokenId)) {\n return {\n success: false,\n error: `Token ${tokenId} is blacklisted (serial loser)`,\n dryRun: this.config.dryRun,\n };\n }\n\n // Handle SELL/STRONG_SELL outside hyperliquid-perp mode.\n // • If an existing LONG exists → close it (signal flip / take-profit-by-signal).\n // • Otherwise (no position OR existing SHORT) → fall through to the\n // standard open/pyramid path below, which opens a paper short or\n // pyramids into the existing short. This was previously a hard error\n // (\"No open position to sell\") — paper shorting was effectively dead\n // code, masking SHORT signal performance in dry-run.\n if ((decision.action === 'SELL' || decision.action === 'STRONG_SELL') && this.config.mode !== 'hyperliquid-perp') {\n const sellState = await this.portfolio.load();\n const existing = sellState.positions.find((p) => p.tokenId === tokenId);\n const existingSide = existing?.side ?? 'long';\n\n if (existing && existingSide === 'long') {\n try {\n const reason = decision.action === 'STRONG_SELL' ? 'Strong sell signal' : 'Sell signal';\n if (this.config.dryRun) {\n console.error(chalk.cyan(`[DRY RUN] Paper trade: SELL ${existing.quantity.toFixed(6)} ${tokenId} @ $${currentPrice.toFixed(4)}`));\n }\n const closeResult = await this.portfolio.closePosition(tokenId, currentPrice, reason);\n return {\n success: true,\n position: { ...existing, currentPrice, pnlUsd: closeResult.pnl, pnlPercent: closeResult.pnlPercent },\n dryRun: this.config.dryRun,\n };\n } catch (err) {\n return {\n success: false,\n error: `Failed to close position: ${(err as Error).message}`,\n dryRun: this.config.dryRun,\n };\n }\n }\n // else: no existing long — fall through to open/pyramid a paper short\n }\n\n // Only execute buys/sells; HOLD does nothing\n if (decision.action !== 'BUY' && decision.action !== 'STRONG_BUY' && decision.action !== 'SELL' && decision.action !== 'STRONG_SELL') {\n return {\n success: false,\n error: `Action ${decision.action} does not trigger execution`,\n dryRun: this.config.dryRun,\n };\n }\n\n // Minimum conviction gate — filter out marginal \"just barely crossed the\n // threshold\" entries. Trade log review (25 trades): the 3 losing shorts\n // scored -0.10 to -0.15, winning longs scored 0.20-0.40+. Entries below\n // 0.18 absolute score have a much higher stop-out rate because they\n // represent weak signal consensus rather than genuine directional edge.\n // Minimum conviction — set below the lowest regime BUY threshold (ranging 0.17)\n // to avoid a dead zone where scores pass the threshold but fail conviction.\n // Prior value 0.12 created a [0.12, 0.17) dead zone.\n const MIN_CONVICTION_SCORE = 0.08;\n if (Math.abs(decision.score) < MIN_CONVICTION_SCORE) {\n return {\n success: false,\n error: `Score ${decision.score.toFixed(3)} below minimum conviction (${MIN_CONVICTION_SCORE})`,\n dryRun: this.config.dryRun,\n };\n }\n\n // Resolve token → HL asset index for multi-asset execution.\n // If the token doesn't have a known HL perp, skip execution.\n if (this.config.mode === 'hyperliquid-perp') {\n const resolvedIndex = TradeExecutor.resolveAssetIndex(tokenId);\n if (resolvedIndex === undefined) {\n return {\n success: false,\n error: `Token ${tokenId} has no known Hyperliquid perp asset index — skipping execution`,\n dryRun: this.config.dryRun,\n };\n }\n }\n\n // Load portfolio to get current state\n const state = await this.portfolio.load();\n this.riskManager.updatePortfolio(state);\n\n // Calculate stop loss and take profit from current price.\n // Calibrated for SHORT-TERM trades (1-2 day hold) with leverage:\n // Stop: 4% price move × 3x leverage = 12% capital risk (capped by riskPerTrade)\n // Take profit: 1.5:1 R:R = 6% price move × 3x = 18% capital gain\n // Partial exit at 2% move × 3x = 6% capital gain (locks half)\n // Time stop: 96h (see risk.ts) — if it hasn't moved, it's dead money\n const isShort = decision.action === 'SELL' || decision.action === 'STRONG_SELL';\n const direction: 'long' | 'short' = isShort ? 'short' : 'long';\n const RR_RATIO = 2.0; // autoresearch (Apr 27): 1.5→2.0 captures more upside from winners\n // Nunchi autoresearch (103 experiments): wider ATR stops let winners run.\n // Their optimal was 5.5x ATR; we use 3.5x as a compromise between letting\n // winners breathe and controlling loss on stopped trades. Prior 1.5x was\n // too tight — 64% of trades hit the stop, many of which reversed after.\n const ATR_STOP_MULTIPLIER = 3.5;\n // Trade log analysis (32 trades): 62.5% of exits were stops, many reversed\n // after. Widened floor from 3% → 4% to give trades more room to breathe\n // in crypto's noisy price action. ATR-based stops still dominate on\n // higher-vol tokens where ATR × 3.5 > 4%.\n const STOP_FLOOR = 0.05; // minimum 5% (was 4% — still noise-stopping, 63% of exits are stops)\n const STOP_CAP = 0.12; // maximum 12% (tightened from 15%)\n const FALLBACK_STOP = 0.05; // when no ATR available (was 4%)\n\n // Kronos ML-predicted vol overrides ATR when available.\n // predictedVol4h is the per-candle (4h) volatility from Monte Carlo paths.\n // Use 2.5× predicted vol as stop distance (captures ~95% of expected move).\n // Falls back to ATR×3.5 when Kronos is unavailable.\n const KRONOS_STOP_MULTIPLIER = 2.5;\n const useKronos = predictedVol && Number.isFinite(predictedVol) && predictedVol > 0;\n const atrPct = (atr && currentPrice > 0 && !isNaN(atr))\n ? atr / currentPrice\n : FALLBACK_STOP / ATR_STOP_MULTIPLIER;\n const kronosPct = useKronos ? predictedVol! * KRONOS_STOP_MULTIPLIER : 0;\n const rawStopPct = useKronos ? kronosPct : atrPct * ATR_STOP_MULTIPLIER;\n const stopPct = Math.min(STOP_CAP, Math.max(STOP_FLOOR, rawStopPct));\n this.lastAtr = atr;\n const stopLossDistance = currentPrice * stopPct;\n const stopLossPrice = isShort\n ? currentPrice + stopLossDistance // stop above for shorts\n : currentPrice - stopLossDistance; // stop below for longs\n const takeProfitPrice = isShort\n ? currentPrice * (1 - stopPct * RR_RATIO) // profit below for shorts\n : currentPrice * (1 + stopPct * RR_RATIO); // profit above for longs\n\n // Detect a pyramid (same-direction add to an existing position) and halve\n // the size for each add. Geometric decay (1.0x → 0.5x → 0.25x) caps total\n // exposure on a single name at 1.75x the original size.\n const existingSameSide = state.positions.find(\n (p) => p.tokenId === tokenId && (p.side ?? 'long') === direction,\n );\n const isPyramid = existingSameSide !== undefined;\n // Short sizing reduction: trade log analysis showed 25% short WR vs 67%\n // long WR. Halve short exposure until the regime gate + higher conviction\n // thresholds improve short performance.\n const SHORT_SIZE_MULTIPLIER = 0.5;\n const directionMultiplier = isShort ? SHORT_SIZE_MULTIPLIER : 1.0;\n const sizeMultiplier = (isPyramid\n ? 0.5 ** ((existingSameSide!.addCount ?? 0) + 1)\n : 1.0) * directionMultiplier;\n\n // Size the position using risk management. Conviction is fed in via the\n // risk budget — the sizer then clamps at maxSinglePosition, so a\n // high-conviction entry cannot blow past the hard cap (previous bug:\n // sizing was clamped, then multiplied by conviction after, yielding 30%\n // sizes on score ≥ 0.35 and 40% on score ≥ 0.45 against a 20% cap).\n const conviction = convictionMultiplier(decision.score);\n const sizing = this.riskManager.calculatePositionSize(\n currentPrice,\n stopLossPrice,\n state.totalValue,\n this.riskManager.getRiskPerTrade() * conviction,\n );\n\n // Apply leverage: multiply position size (not risk). A 3x leveraged position\n // on a $500 risk budget buys $1500 notional — the stop loss distance stays the\n // same in price terms, so the dollar risk per trade stays at riskPerTrade × portfolio.\n // Pyramid haircut then shrinks for each subsequent add (base 1.0x → 0.5x → 0.25x).\n const pyramidQuantity = sizing.quantity * sizeMultiplier * DIRECTIONAL_LEVERAGE;\n const pyramidSizeUsd = sizing.sizeUsd * sizeMultiplier * DIRECTIONAL_LEVERAGE;\n\n if (pyramidQuantity <= 0 || pyramidSizeUsd <= 0) {\n return {\n success: false,\n error: 'Position sizing returned zero — check portfolio value and stop distance',\n dryRun: this.config.dryRun,\n };\n }\n\n // Enforce Hyperliquid minimum notional ($15) AFTER the pyramid haircut.\n // calculatePositionSize() applies its own floor on the BASE size, but\n // halving (0.5x) and quartering (0.25x) for subsequent adds can push\n // the order below minimum. In dry-run this is harmless paper trading;\n // in hyperliquid-perp mode the order would revert at the venue.\n const MIN_PYRAMID_USD = 15;\n if (isPyramid && pyramidSizeUsd < MIN_PYRAMID_USD) {\n return {\n success: false,\n error: `Pyramid add #${(existingSameSide!.addCount ?? 0) + 1} would be $${pyramidSizeUsd.toFixed(2)} — below $${MIN_PYRAMID_USD} HL minimum notional. Skipping.`,\n dryRun: this.config.dryRun,\n };\n }\n\n // Check if risk manager allows this trade (passes pyramid + direction context)\n const check = this.riskManager.canOpenPosition(tokenId, pyramidSizeUsd, direction);\n if (!check.allowed) {\n return {\n success: false,\n error: `Risk check failed: ${check.reason}`,\n dryRun: this.config.dryRun,\n };\n }\n\n const order: OrderParams = {\n tokenId,\n side: isShort ? 'sell' : 'buy',\n amountUsd: pyramidSizeUsd,\n maxSlippage: 0.015,\n stopLoss: stopLossPrice,\n takeProfit: takeProfitPrice,\n };\n\n if (this.config.dryRun) {\n try {\n const position = await this.executeDryRun(order, currentPrice, isPyramid);\n return { success: true, position, dryRun: true };\n } catch (err) {\n return {\n success: false,\n error: `Dry-run failed: ${(err as Error).message}`,\n dryRun: true,\n };\n }\n } else {\n try {\n const result = await this.executeLive(order, currentPrice);\n // If live execution succeeded, also track in portfolio\n const position = isPyramid\n ? await this.portfolio.addToPosition(tokenId, result.executedPrice, pyramidQuantity, direction)\n : await this.portfolio.openPosition({\n tokenId,\n symbol: tokenId.toUpperCase(),\n side: direction,\n entryPrice: result.executedPrice,\n currentPrice: result.executedPrice,\n quantity: pyramidQuantity,\n entryTimestamp: Date.now(),\n stopLoss: order.stopLoss,\n takeProfit: order.takeProfit,\n strategy: decision.signals[0]?.source ?? 'agent',\n atrAtEntry: this.lastAtr,\n });\n return { success: true, position, dryRun: false };\n } catch (err) {\n return {\n success: false,\n error: `Live execution failed: ${(err as Error).message}`,\n dryRun: false,\n };\n }\n }\n }\n\n /** Process all pending exits (stops, take profits, time stops) */\n async processExits(\n currentPrices: Record<string, number>,\n ): Promise<Array<{ position: Position; exitPrice: number; reason: string; pnl: number }>> {\n const state = await this.portfolio.load();\n this.riskManager.updatePortfolio(state);\n\n const { toClose, reasons } = this.riskManager.checkExits(state.positions, currentPrices);\n const results: Array<{ position: Position; exitPrice: number; reason: string; pnl: number }> = [];\n\n for (const pos of toClose) {\n const exitPrice = currentPrices[pos.tokenId] ?? pos.currentPrice;\n const reason = reasons[pos.tokenId] ?? 'Unknown';\n\n try {\n const closeResult = await this.portfolio.closePosition(pos.tokenId, exitPrice, reason);\n results.push({\n position: pos,\n exitPrice,\n reason,\n pnl: closeResult.pnl,\n });\n } catch (err) {\n console.error(chalk.red(`Failed to close ${pos.symbol}: ${(err as Error).message}`));\n }\n }\n\n // --- Partial profit exits (50% at +3%) ---\n // Reload state after each partial close to avoid iterating stale positions.\n const PARTIAL_PROFIT_TRIGGER = 0.015; // +1.5% unrealized gain — lock profits earlier at 1x leverage\n const PARTIAL_FRACTION = 0.5;\n\n // First pass: identify candidates from a fresh load\n const partialCandidates: string[] = [];\n {\n const refreshedState = await this.portfolio.load();\n for (const pos of refreshedState.positions) {\n if (pos.partialTaken) continue;\n const price = currentPrices[pos.tokenId];\n if (price === undefined) continue;\n const isShort = pos.side === 'short';\n const pnlPercent = isShort\n ? (pos.entryPrice - price) / (pos.entryPrice || 1)\n : (price - pos.entryPrice) / (pos.entryPrice || 1);\n if (pnlPercent >= PARTIAL_PROFIT_TRIGGER) {\n partialCandidates.push(pos.tokenId);\n }\n }\n }\n\n // Second pass: execute each partial close with a fresh state reload\n for (const tokenId of partialCandidates) {\n const freshState = await this.portfolio.load();\n const pos = freshState.positions.find((p) => p.tokenId === tokenId);\n if (!pos || pos.partialTaken) continue; // re-check after reload\n\n const price = currentPrices[tokenId];\n if (price === undefined) continue;\n\n const isShort = pos.side === 'short';\n const pnlPercent = isShort\n ? (pos.entryPrice - price) / (pos.entryPrice || 1)\n : (price - pos.entryPrice) / (pos.entryPrice || 1);\n\n if (pnlPercent >= PARTIAL_PROFIT_TRIGGER) {\n try {\n const partial = await this.portfolio.closePartial(\n tokenId, PARTIAL_FRACTION, price, `Partial profit at ${(pnlPercent * 100).toFixed(1)}%`,\n );\n results.push({\n position: { ...pos, currentPrice: price },\n exitPrice: price,\n reason: `PARTIAL_PROFIT (${(pnlPercent * 100).toFixed(1)}%, closed ${(PARTIAL_FRACTION * 100).toFixed(0)}%)`,\n pnl: partial.pnl,\n });\n console.error(\n chalk.cyan(` Partial exit: ${pos.symbol} ${(PARTIAL_FRACTION * 100).toFixed(0)}% @ $${price.toFixed(4)} — locked $${partial.pnl.toFixed(2)}`),\n );\n } catch (err) {\n console.error(chalk.red(`Failed partial close ${pos.symbol}: ${(err as Error).message}`));\n }\n }\n }\n\n return results;\n }\n\n /** Dry-run execution — paper trade */\n private async executeDryRun(order: OrderParams, currentPrice: number, isPyramid = false): Promise<Position> {\n const quantity = order.amountUsd / currentPrice;\n const direction: 'long' | 'short' = order.side === 'sell' ? 'short' : 'long';\n const sideLabel = isPyramid\n ? (direction === 'short' ? 'PYRAMID SHORT' : 'PYRAMID BUY')\n : (direction === 'short' ? 'SHORT' : 'BUY');\n\n console.error(chalk.cyan(`[DRY RUN] Paper trade: ${sideLabel} ${quantity.toFixed(6)} ${order.tokenId} @ $${currentPrice.toFixed(4)} (${DIRECTIONAL_LEVERAGE}x)`));\n console.error(chalk.cyan(` Size: $${order.amountUsd.toFixed(2)} | SL: $${order.stopLoss.toFixed(4)} | TP: $${order.takeProfit.toFixed(4)}`));\n\n if (isPyramid) {\n return this.portfolio.addToPosition(order.tokenId, currentPrice, quantity, direction);\n }\n\n const position = await this.portfolio.openPosition({\n tokenId: order.tokenId,\n symbol: order.tokenId.toUpperCase(),\n side: direction,\n entryPrice: currentPrice,\n currentPrice,\n quantity,\n entryTimestamp: Date.now(),\n stopLoss: order.stopLoss,\n takeProfit: order.takeProfit,\n strategy: 'paper',\n atrAtEntry: this.lastAtr,\n });\n\n return position;\n }\n\n /** Live execution — dispatches to the configured mode */\n private async executeLive(order: OrderParams, currentPrice?: number): Promise<{ txHash: string; executedPrice: number }> {\n if (this.config.mode === 'hyperliquid-perp') {\n return this.executeHyperliquidPerp(order, currentPrice);\n }\n throw new Error(\n 'Live execution requires --mode hyperliquid-perp. ' +\n 'Use --dry-run for paper trading.',\n );\n }\n\n /** Execute a trade on Hyperliquid perps via the hermes HL skill script. */\n private async executeHyperliquidPerp(order: OrderParams, currentPrice?: number): Promise<{ txHash: string; executedPrice: number }> {\n validateHLEnv();\n if (!currentPrice || currentPrice <= 0) throw new Error('currentPrice is required for live execution');\n\n const hlCoin = resolveHLCoin(order.tokenId);\n if (!hlCoin) {\n throw new Error(`Token ${order.tokenId} has no known Hyperliquid ticker — cannot execute live`);\n }\n\n const quantity = order.amountUsd / currentPrice;\n const isShort = order.side === 'sell';\n\n console.error(chalk.yellow(`[LIVE] Submitting ${isShort ? 'SELL' : 'BUY'} ${quantity.toFixed(6)} ${hlCoin} ($${order.amountUsd.toFixed(2)}) via HL SDK...`));\n\n const result = isShort\n ? await hlMarketSell(hlCoin, quantity)\n : await hlMarketBuy(hlCoin, quantity);\n\n if (!result.success) {\n throw new Error(`Hyperliquid order failed: ${result.error}`);\n }\n\n const executedPrice = result.executedPrice ?? currentPrice;\n const orderId = result.orderId ?? 'unknown';\n\n console.error(chalk.green(`[LIVE] Order filled: ${hlCoin} ${isShort ? 'SHORT' : 'LONG'} @ $${executedPrice.toFixed(4)} (oid: ${orderId})`));\n\n return {\n txHash: orderId, // HL order ID serves as the \"tx hash\" identifier\n executedPrice,\n };\n }\n\n /** Format execution result for display */\n formatExecution(result: {\n success: boolean;\n position?: Position;\n error?: string;\n dryRun: boolean;\n }): string {\n const lines: string[] = [];\n const prefix = result.dryRun ? chalk.cyan('[DRY RUN]') : chalk.green('[LIVE]');\n\n if (result.success && result.position) {\n const p = result.position;\n lines.push('');\n lines.push(`${prefix} ${chalk.bold('Trade Executed')}`);\n lines.push(chalk.dim('─'.repeat(40)));\n lines.push(`Token: ${p.symbol}`);\n lines.push(`Entry: $${p.entryPrice.toFixed(4)}`);\n lines.push(`Quantity: ${p.quantity.toFixed(6)}`);\n lines.push(`Size: $${(p.quantity * p.entryPrice).toFixed(2)}`);\n lines.push(`Stop Loss: $${p.stopLoss.toFixed(4)}`);\n lines.push(`Take Profit: $${p.takeProfit.toFixed(4)}`);\n lines.push(`Strategy: ${p.strategy}`);\n lines.push('');\n } else {\n lines.push('');\n lines.push(`${prefix} ${chalk.red('Trade Failed')}`);\n lines.push(`Reason: ${result.error}`);\n lines.push('');\n }\n\n return lines.join('\\n');\n }\n}\n\n","/**\n * Portfolio tracking with JSON file persistence.\n */\n\nimport { mkdir, readFile, writeFile, rename } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { homedir } from 'node:os';\nimport chalk from 'chalk';\nimport type { Position, PortfolioState } from './risk.js';\n\n/** Margin fraction for perp positions — both longs and shorts. */\nconst MARGIN_FRACTION = 0.33;\n\n/**\n * Compute total portfolio value with margin-based position accounting.\n * For leveraged perp positions, the portfolio \"owns\" the margin + unrealized PnL,\n * not the full notional. This prevents phantom value when cash is debited at\n * margin rate but positions are counted at full notional.\n */\nfunction computeTotalValue(cash: number, positions: Position[]): number {\n const positionEquity = positions.reduce((sum, p) => {\n const isShort = p.side === 'short';\n const pnl = isShort\n ? (p.entryPrice - p.currentPrice) * p.quantity\n : (p.currentPrice - p.entryPrice) * p.quantity;\n return sum + p.quantity * p.entryPrice * MARGIN_FRACTION + pnl;\n }, 0);\n return cash + positionEquity;\n}\n\n/** Check and reset PnL counters based on time boundaries */\nexport function resetPnlCounters(state: PortfolioState): PortfolioState {\n const now = Date.now();\n const updated = { ...state };\n\n // Reset daily PnL at midnight UTC\n const todayMidnight = new Date();\n todayMidnight.setUTCHours(0, 0, 0, 0);\n const todayMs = todayMidnight.getTime();\n if (!updated.lastDailyReset || updated.lastDailyReset < todayMs) {\n updated.dailyPnl = 0;\n updated.lastDailyReset = now;\n }\n\n // Orca-inspired: PnL-aware daily cap, see README / PR #223.\n // Reset the daily-entry counter on the same UTC day boundary as dailyPnl.\n // Tracked independently (lastDailyEntriesReset) so legacy files missing\n // `dailyEntries` reset cleanly on first load.\n if (!updated.lastDailyEntriesReset || updated.lastDailyEntriesReset < todayMs) {\n updated.dailyEntries = 0;\n updated.lastDailyEntriesReset = now;\n }\n\n // Reset weekly PnL on Monday midnight UTC\n const dayOfWeek = new Date(now).getUTCDay(); // 0=Sun, 1=Mon\n const daysSinceMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;\n const mondayMidnight = new Date();\n mondayMidnight.setUTCHours(0, 0, 0, 0);\n mondayMidnight.setUTCDate(mondayMidnight.getUTCDate() - daysSinceMonday);\n const mondayMs = mondayMidnight.getTime();\n if (!updated.lastWeeklyReset || updated.lastWeeklyReset < mondayMs) {\n updated.weeklyPnl = 0;\n updated.lastWeeklyReset = now;\n }\n\n // Reset monthly PnL on 1st of month midnight UTC\n const firstOfMonth = new Date();\n firstOfMonth.setUTCHours(0, 0, 0, 0);\n firstOfMonth.setUTCDate(1);\n const firstMs = firstOfMonth.getTime();\n if (!updated.lastMonthlyReset || updated.lastMonthlyReset < firstMs) {\n updated.monthlyPnl = 0;\n updated.lastMonthlyReset = now;\n }\n\n return updated;\n}\n\nexport interface TradeRecord {\n tokenId: string;\n symbol: string;\n side: 'long' | 'short';\n entryPrice: number;\n exitPrice: number;\n quantity: number;\n pnlUsd: number;\n pnlPercent: number;\n entryTimestamp: number;\n exitTimestamp: number;\n duration: number;\n strategy: string;\n exitReason: string;\n}\n\nconst DEFAULT_PORTFOLIO: PortfolioState = {\n totalValue: 10000,\n positions: [],\n cash: 10000,\n dailyPnl: 0,\n weeklyPnl: 0,\n monthlyPnl: 0,\n dailyEntries: 0,\n initialValue: 10000,\n};\n\nfunction createDefaultPortfolio(): PortfolioState {\n return {\n ...DEFAULT_PORTFOLIO,\n positions: [],\n stopCooldowns: {},\n tokenConsecLosses: {},\n tokenLossCooldowns: {},\n };\n}\n\nexport class PortfolioTracker {\n private statePath: string;\n private historyPath: string;\n private state: PortfolioState;\n /** In-process mutex — serializes load/modify/save sequences to prevent\n * overlapping cycles from double-closing positions or double-debiting cash. */\n private _lock: Promise<void> = Promise.resolve();\n\n constructor() {\n const base = join(homedir(), '.sherwood', 'agent');\n this.statePath = join(base, 'portfolio.json');\n this.historyPath = join(base, 'trades.json');\n this.state = createDefaultPortfolio();\n }\n\n /** Acquire the in-process lock. Callers that perform load→modify→save\n * should wrap their sequence: `const release = await this.acquireLock();`\n * then `release()` in a finally block. */\n private acquireLock(): Promise<() => void> {\n let release: () => void;\n const next = new Promise<void>((resolve) => { release = resolve; });\n const prev = this._lock;\n this._lock = next;\n return prev.then(() => release!);\n }\n\n /**\n * Initialize portfolio from on-chain vault balance instead of the\n * hardcoded $10k default. Reads totalAssets() from the vault that\n * the strategy clone is deployed to.\n *\n * Call once on startup (before the first cycle) when no persisted\n * portfolio.json exists. If a portfolio already exists on disk,\n * this is a no-op — we trust the persisted state.\n *\n * @param strategyClone - HyperliquidPerpStrategy clone address\n * @param chain - 'hyperevm' | 'hyperevm-testnet'\n */\n async initFromOnChain(strategyClone: string, chain: string): Promise<void> {\n // Only initialize if no persisted state exists\n try {\n await readFile(this.statePath, 'utf-8');\n return; // file exists — trust persisted state\n } catch {\n // No file — initialize from on-chain\n }\n\n try {\n const { createPublicClient, http } = await import('viem');\n const { getChain } = await import('../lib/network.js');\n const { BASE_STRATEGY_ABI, SYNDICATE_VAULT_ABI } = await import('../lib/abis.js');\n\n // HyperEVM (this strategy's original venue) is not in the current\n // deployment target; read against the active chain's client.\n void chain;\n const client = createPublicClient({\n chain: getChain(),\n transport: http(),\n });\n\n // Read vault address from the strategy clone\n const vaultAddr = await client.readContract({\n address: strategyClone as `0x${string}`,\n abi: BASE_STRATEGY_ABI,\n functionName: 'vault',\n }) as `0x${string}`;\n\n if (!vaultAddr || vaultAddr === '0x0000000000000000000000000000000000000000') {\n console.error('[portfolio] Strategy clone has no vault — using default portfolio');\n return;\n }\n\n // Read total assets from the vault (USDC, 6 decimals)\n const totalAssets = await client.readContract({\n address: vaultAddr,\n abi: SYNDICATE_VAULT_ABI,\n functionName: 'totalAssets',\n }) as bigint;\n\n const vaultValueUsd = Number(totalAssets) / 1e6; // USDC 6 decimals\n\n if (vaultValueUsd > 0) {\n this.state = {\n ...createDefaultPortfolio(),\n totalValue: vaultValueUsd,\n cash: vaultValueUsd,\n initialValue: vaultValueUsd, // anchor cumulative PnL% to the on-chain starting balance\n };\n await this.save(this.state);\n console.error(`[portfolio] Initialized from on-chain vault: $${vaultValueUsd.toFixed(2)} USDC`);\n } else {\n console.error('[portfolio] Vault has 0 assets — using default portfolio');\n }\n } catch (err) {\n console.error(`[portfolio] Failed to read on-chain balance: ${(err as Error).message} — using default`);\n }\n }\n\n /**\n * Force-refresh portfolio value from on-chain vault balance.\n * Unlike initFromOnChain(), this runs even if portfolio.json exists.\n * Call via `sherwood agent config --sync-vault` or on explicit user request.\n */\n async syncFromOnChain(strategyClone: string, chain: string): Promise<void> {\n // Temporarily remove the file so initFromOnChain doesn't short-circuit\n try { await import('node:fs/promises').then(fs => fs.unlink(this.statePath)); } catch { /* ok */ }\n await this.initFromOnChain(strategyClone, chain);\n }\n\n /** Load portfolio state from disk with validation */\n async load(): Promise<PortfolioState> {\n try {\n const data = await readFile(this.statePath, 'utf-8');\n const parsed = JSON.parse(data) as PortfolioState;\n\n // Validate critical numeric fields are finite and non-negative\n if (\n !Number.isFinite(parsed.totalValue) || parsed.totalValue < 0\n || !Number.isFinite(parsed.cash) || parsed.cash < 0\n || !Number.isFinite(parsed.dailyPnl)\n || !Number.isFinite(parsed.weeklyPnl)\n || !Number.isFinite(parsed.monthlyPnl)\n || !Array.isArray(parsed.positions)\n ) {\n console.error('Portfolio file has invalid data — resetting to defaults');\n this.state = createDefaultPortfolio();\n return this.state;\n }\n\n // Validate each position\n for (const p of parsed.positions) {\n if (\n !Number.isFinite(p.entryPrice) || p.entryPrice <= 0\n || !Number.isFinite(p.quantity) || p.quantity <= 0\n || !Number.isFinite(p.currentPrice) || p.currentPrice <= 0\n || !Number.isFinite(p.stopLoss) || p.stopLoss <= 0\n || !Number.isFinite(p.takeProfit) || p.takeProfit <= 0\n ) {\n console.error(`Invalid position data for ${p.tokenId} — resetting portfolio`);\n this.state = createDefaultPortfolio();\n return this.state;\n }\n }\n\n // Legacy portfolios pre-dating the `initialValue` field get the default\n // $10k anchor — safe because paper-trading always started there. Live\n // funds that went through `initFromOnChain` already have this set.\n if (!Number.isFinite(parsed.initialValue) || (parsed.initialValue ?? 0) <= 0) {\n parsed.initialValue = DEFAULT_PORTFOLIO.initialValue;\n }\n\n this.state = parsed;\n } catch {\n // File doesn't exist or is invalid — start fresh\n this.state = createDefaultPortfolio();\n }\n return this.state;\n }\n\n /** Save portfolio state to disk (atomic write to prevent corruption) */\n async save(state: PortfolioState): Promise<void> {\n this.state = state;\n await mkdir(dirname(this.statePath), { recursive: true });\n const tmpPath = this.statePath + '.tmp';\n await writeFile(tmpPath, JSON.stringify(state, null, 2), 'utf-8');\n await rename(tmpPath, this.statePath);\n }\n\n /** Open a new position */\n async openPosition(position: Omit<Position, 'pnlPercent' | 'pnlUsd'>): Promise<Position> {\n if (position.quantity <= 0) {\n throw new Error(`Invalid position quantity: ${position.quantity}`);\n }\n if (position.entryPrice <= 0) {\n throw new Error(`Invalid entry price: ${position.entryPrice}`);\n }\n const release = await this.acquireLock();\n try {\n await this.load();\n\n const fullPosition: Position = {\n ...position,\n addCount: position.addCount ?? 0,\n lastAddTimestamp: position.lastAddTimestamp ?? position.entryTimestamp,\n // Orca-inspired HWM profit-lock, see README / PR #223.\n // Seed peakPrice at entry so subsequent cycles can ratchet from\n // the first favorable move.\n peakPrice: position.peakPrice ?? position.entryPrice,\n pnlPercent: 0,\n pnlUsd: 0,\n };\n\n this.state.positions.push(fullPosition);\n // Both longs and shorts on perp venues only require margin, not full notional.\n // With 3x directional leverage, a $3,500 notional position only locks $1,167\n // margin (33%). Prior code debited full notional for longs, depleting cash 3x\n // faster than reality and blocking subsequent trades.\n const cashDebit = position.quantity * position.entryPrice * MARGIN_FRACTION;\n this.state.cash -= cashDebit;\n this.state.totalValue = computeTotalValue(this.state.cash, this.state.positions);\n\n // Orca-inspired: PnL-aware daily cap, see README / PR #223.\n // Count this NEW entry against the day's turnover budget.\n // Pyramid adds go through addToPosition and do NOT count.\n const prior = Number.isFinite(this.state.dailyEntries) ? (this.state.dailyEntries ?? 0) : 0;\n this.state.dailyEntries = prior + 1;\n\n await this.save(this.state);\n return fullPosition;\n } finally { release(); }\n }\n\n /**\n * Pyramid into an existing position.\n * Computes a quantity-weighted average entry price and increments `addCount`.\n * Cash is debited at the new fill price. Stops/take-profits stay anchored to\n * the prior values (caller is free to widen them after).\n *\n * Throws if no existing position exists or the side disagrees.\n */\n async addToPosition(\n tokenId: string,\n addPrice: number,\n addQuantity: number,\n side: 'long' | 'short',\n ): Promise<Position> {\n if (addQuantity <= 0) throw new Error(`Invalid add quantity: ${addQuantity}`);\n if (addPrice <= 0) throw new Error(`Invalid add price: ${addPrice}`);\n const release = await this.acquireLock();\n try {\n await this.load();\n\n const idx = this.state.positions.findIndex((p) => p.tokenId === tokenId);\n if (idx === -1) throw new Error(`No open position for ${tokenId} to pyramid into`);\n\n const pos = this.state.positions[idx]!;\n const existingSide = pos.side ?? 'long';\n if (existingSide !== side) {\n throw new Error(`Cannot pyramid ${side} on existing ${existingSide} position in ${tokenId}`);\n }\n\n // Weighted-average entry price across the combined quantity\n const newQty = pos.quantity + addQuantity;\n const weightedEntry = (pos.entryPrice * pos.quantity + addPrice * addQuantity) / newQty;\n\n const updated: Position = {\n ...pos,\n entryPrice: weightedEntry,\n quantity: newQty,\n currentPrice: addPrice,\n addCount: (pos.addCount ?? 0) + 1,\n lastAddTimestamp: Date.now(),\n pnlPercent: 0, // recomputed on next price update\n pnlUsd: 0,\n };\n\n this.state.positions[idx] = updated;\n // Both longs and shorts use margin-based cash debit (33% = 3x leverage).\n this.state.cash -= addPrice * addQuantity * MARGIN_FRACTION;\n this.state.totalValue = computeTotalValue(this.state.cash, this.state.positions);\n\n await this.save(this.state);\n return updated;\n } finally { release(); }\n }\n\n /** Close a position and record trade */\n async closePosition(\n tokenId: string,\n exitPrice: number,\n reason: string,\n ): Promise<{ pnl: number; pnlPercent: number; duration: number }> {\n const release = await this.acquireLock();\n try {\n await this.load();\n\n const idx = this.state.positions.findIndex((p) => p.tokenId === tokenId);\n if (idx === -1) {\n throw new Error(`No open position for ${tokenId}`);\n }\n\n const pos = this.state.positions[idx]!;\n const isShort = pos.side === 'short';\n const pnlUsd = isShort\n ? (pos.entryPrice - exitPrice) * pos.quantity\n : (exitPrice - pos.entryPrice) * pos.quantity;\n const pnlPercent = isShort\n ? (pos.entryPrice - exitPrice) / (pos.entryPrice || 1)\n : (exitPrice - pos.entryPrice) / (pos.entryPrice || 1);\n const duration = Math.floor((Date.now() - pos.entryTimestamp) / 1000);\n\n // Record the trade\n const record: TradeRecord = {\n tokenId: pos.tokenId,\n symbol: pos.symbol,\n side: pos.side ?? 'long',\n entryPrice: pos.entryPrice,\n exitPrice,\n quantity: pos.quantity,\n pnlUsd,\n pnlPercent,\n entryTimestamp: pos.entryTimestamp,\n exitTimestamp: Date.now(),\n duration,\n strategy: pos.strategy,\n exitReason: reason,\n };\n\n await this.appendTradeRecord(record);\n\n // Remove position and update cash.\n // Both longs and shorts return margin (33% of notional) + PnL.\n this.state.positions.splice(idx, 1);\n const cashCredit = pos.quantity * pos.entryPrice * MARGIN_FRACTION + pnlUsd;\n this.state.cash += cashCredit;\n this.state.dailyPnl += pnlUsd;\n this.state.weeklyPnl += pnlUsd;\n this.state.monthlyPnl += pnlUsd;\n this.state.totalValue = computeTotalValue(this.state.cash, this.state.positions);\n\n // Record stop-loss cooldown to prevent rapid re-entry\n if (reason.toLowerCase().includes('stop')) {\n if (!this.state.stopCooldowns) this.state.stopCooldowns = {};\n this.state.stopCooldowns[tokenId] = Date.now();\n }\n\n // Track per-token consecutive losses for the serial-loser gate.\n // A losing trade increments the counter; a winning trade resets it to 0.\n // When the counter hits TOKEN_CONSEC_LOSS_LIMIT, record the cooldown timestamp.\n if (!this.state.tokenConsecLosses) this.state.tokenConsecLosses = {};\n if (!this.state.tokenLossCooldowns) this.state.tokenLossCooldowns = {};\n if (pnlUsd < 0) {\n this.state.tokenConsecLosses[tokenId] = (this.state.tokenConsecLosses[tokenId] ?? 0) + 1;\n if (this.state.tokenConsecLosses[tokenId]! >= 2) {\n this.state.tokenLossCooldowns[tokenId] = Date.now();\n }\n } else {\n // Win or breakeven — reset consecutive loss counter\n this.state.tokenConsecLosses[tokenId] = 0;\n delete this.state.tokenLossCooldowns[tokenId];\n }\n\n await this.save(this.state);\n\n return { pnl: pnlUsd, pnlPercent, duration };\n } finally { release(); }\n }\n\n /**\n * Close a fraction of a position. Records a partial trade and reduces\n * the position's quantity. Entry price and stops are preserved.\n */\n async closePartial(\n tokenId: string,\n fraction: number,\n exitPrice: number,\n reason: string,\n ): Promise<{ pnl: number; pnlPercent: number; quantityClosed: number }> {\n if (fraction <= 0 || fraction >= 1) {\n throw new Error(`Invalid fraction: ${fraction} (must be between 0 and 1 exclusive)`);\n }\n const release = await this.acquireLock();\n try {\n await this.load();\n\n const idx = this.state.positions.findIndex((p) => p.tokenId === tokenId);\n if (idx === -1) throw new Error(`No open position for ${tokenId}`);\n\n const pos = this.state.positions[idx]!;\n const isShort = pos.side === 'short';\n const quantityClosed = pos.quantity * fraction;\n const pnlUsd = isShort\n ? (pos.entryPrice - exitPrice) * quantityClosed\n : (exitPrice - pos.entryPrice) * quantityClosed;\n const pnlPercent = isShort\n ? (pos.entryPrice - exitPrice) / (pos.entryPrice || 1)\n : (exitPrice - pos.entryPrice) / (pos.entryPrice || 1);\n\n // Record partial trade\n const record: TradeRecord = {\n tokenId: pos.tokenId,\n symbol: pos.symbol,\n side: pos.side ?? 'long',\n entryPrice: pos.entryPrice,\n exitPrice,\n quantity: quantityClosed,\n pnlUsd,\n pnlPercent,\n entryTimestamp: pos.entryTimestamp,\n exitTimestamp: Date.now(),\n duration: Math.floor((Date.now() - pos.entryTimestamp) / 1000),\n strategy: pos.strategy,\n exitReason: reason,\n };\n await this.appendTradeRecord(record);\n\n // Reduce position, mark partial taken\n pos.quantity -= quantityClosed;\n pos.partialTaken = true;\n // Return margin fraction (33%) + PnL for both longs and shorts.\n const partialCashCredit = pos.entryPrice * quantityClosed * MARGIN_FRACTION + pnlUsd;\n this.state.cash += partialCashCredit;\n this.state.dailyPnl += pnlUsd;\n this.state.weeklyPnl += pnlUsd;\n this.state.monthlyPnl += pnlUsd;\n this.state.totalValue = computeTotalValue(this.state.cash, this.state.positions);\n\n await this.save(this.state);\n return { pnl: pnlUsd, pnlPercent, quantityClosed };\n } finally { release(); }\n }\n\n /** Update current prices for all positions */\n async updatePrices(prices: Record<string, number>): Promise<PortfolioState> {\n await this.load();\n\n for (const pos of this.state.positions) {\n const price = prices[pos.tokenId];\n if (price !== undefined) {\n // Guard against NaN/Infinity — a bad price update would corrupt PnL\n // calculations and make stops evaluate to false (NaN <= X is always false),\n // permanently trapping positions.\n if (!Number.isFinite(price) || price <= 0) {\n console.error(`[portfolio] Skipping invalid price for ${pos.tokenId}: ${price}`);\n continue;\n }\n pos.currentPrice = price;\n const isShort = pos.side === 'short';\n pos.pnlUsd = isShort\n ? (pos.entryPrice - price) * pos.quantity\n : (price - pos.entryPrice) * pos.quantity;\n pos.pnlPercent = isShort\n ? (pos.entryPrice - price) / (pos.entryPrice || 1)\n : (price - pos.entryPrice) / (pos.entryPrice || 1);\n }\n }\n\n this.state.totalValue = computeTotalValue(this.state.cash, this.state.positions);\n\n await this.save(this.state);\n return this.state;\n }\n\n /** Get trade history */\n async getHistory(days?: number): Promise<TradeRecord[]> {\n let records: TradeRecord[] = [];\n try {\n const data = await readFile(this.historyPath, 'utf-8');\n records = JSON.parse(data) as TradeRecord[];\n } catch {\n return [];\n }\n\n if (days !== undefined) {\n const cutoff = Date.now() - days * 24 * 60 * 60 * 1000;\n records = records.filter((r) => r.exitTimestamp >= cutoff);\n }\n\n return records;\n }\n\n /** Get performance metrics */\n async getMetrics(days?: number): Promise<{\n totalTrades: number;\n winRate: number;\n avgPnlPercent: number;\n totalPnlUsd: number;\n sharpeRatio: number;\n maxDrawdown: number;\n bestTrade: TradeRecord;\n worstTrade: TradeRecord;\n }> {\n const trades = await this.getHistory(days);\n\n if (trades.length === 0) {\n const empty: TradeRecord = {\n tokenId: '', symbol: '', side: 'long', entryPrice: 0, exitPrice: 0,\n quantity: 0, pnlUsd: 0, pnlPercent: 0, entryTimestamp: 0,\n exitTimestamp: 0, duration: 0, strategy: '', exitReason: '',\n };\n return {\n totalTrades: 0, winRate: 0, avgPnlPercent: 0, totalPnlUsd: 0,\n sharpeRatio: 0, maxDrawdown: 0, bestTrade: empty, worstTrade: empty,\n };\n }\n\n const wins = trades.filter((t) => t.pnlUsd > 0);\n const pnls = trades.map((t) => t.pnlPercent);\n const totalPnlUsd = trades.reduce((s, t) => s + t.pnlUsd, 0);\n const avgPnlPercent = pnls.reduce((s, p) => s + p, 0) / pnls.length;\n\n // Sharpe ratio: annualize returns based on average holding period\n // Standard formula: (mean return - risk-free rate) / stddev of returns\n const avgHoldingDays = trades.length > 0\n ? trades.reduce((sum, t) => sum + t.duration, 0) / trades.length / (24 * 60 * 60)\n : 1;\n const tradesPerYear = Math.max(252 / Math.max(avgHoldingDays, 1), 1);\n\n const riskFreePerTrade = 0.05 / tradesPerYear; // Risk-free rate per trade\n const mean = avgPnlPercent;\n const excessMean = mean - riskFreePerTrade;\n const variance = pnls.length > 1\n ? pnls.reduce((s, p) => s + (p - mean) ** 2, 0) / (pnls.length - 1)\n : 0;\n const stdDev = Math.sqrt(variance);\n const sharpeRatio = stdDev > 0 ? (excessMean / stdDev) * Math.sqrt(tradesPerYear) : 0;\n\n // Max drawdown from cumulative equity curve (peak-to-trough)\n // Start with a reasonable initial portfolio value\n const initialValue = 10000; // Start with default $10k portfolio\n let equityPeak = initialValue;\n let equity = initialValue;\n let maxDrawdown = 0;\n\n for (const t of trades) {\n equity += t.pnlUsd;\n if (equity > equityPeak) {\n equityPeak = equity;\n }\n if (equityPeak > 0 && equity < equityPeak) {\n const drawdown = (equityPeak - equity) / equityPeak;\n if (drawdown > maxDrawdown) {\n maxDrawdown = drawdown;\n }\n }\n }\n\n const sorted = [...trades].sort((a, b) => a.pnlUsd - b.pnlUsd);\n const worstTrade = sorted[0]!;\n const bestTrade = sorted[sorted.length - 1]!;\n\n return {\n totalTrades: trades.length,\n winRate: wins.length / trades.length,\n avgPnlPercent,\n totalPnlUsd,\n sharpeRatio,\n maxDrawdown,\n bestTrade,\n worstTrade,\n };\n }\n\n /** Format portfolio summary for display */\n formatSummary(): string {\n const s = this.state;\n const lines: string[] = [];\n\n lines.push('');\n lines.push(chalk.bold(' Portfolio Summary'));\n lines.push(chalk.dim(' ' + '─'.repeat(50)));\n lines.push(` Total Value: $${s.totalValue.toFixed(2)}`);\n lines.push(` Cash: $${s.cash.toFixed(2)}`);\n lines.push(` Positions: ${s.positions.length}`);\n lines.push('');\n\n if (s.positions.length > 0) {\n lines.push(chalk.bold(' Open Positions:'));\n for (const p of s.positions) {\n const pnlColor = p.pnlUsd >= 0 ? chalk.green : chalk.red;\n const pnlStr = pnlColor(\n `${p.pnlUsd >= 0 ? '+' : ''}$${p.pnlUsd.toFixed(2)} (${(p.pnlPercent * 100).toFixed(1)}%)`,\n );\n lines.push(\n ` ${p.symbol.padEnd(10)} ${p.quantity.toFixed(4)} @ $${p.entryPrice.toFixed(4)} PnL: ${pnlStr} [${p.strategy}]`,\n );\n }\n lines.push('');\n }\n\n const dailyColor = s.dailyPnl >= 0 ? chalk.green : chalk.red;\n lines.push(` Daily PnL: ${dailyColor((s.dailyPnl >= 0 ? '+' : '') + '$' + s.dailyPnl.toFixed(2))}`);\n lines.push(` Weekly PnL: ${s.weeklyPnl >= 0 ? '+' : ''}$${s.weeklyPnl.toFixed(2)}`);\n lines.push(` Monthly PnL: ${s.monthlyPnl >= 0 ? '+' : ''}$${s.monthlyPnl.toFixed(2)}`);\n lines.push('');\n\n return lines.join('\\n');\n }\n\n /** Append a trade record to history file */\n private async appendTradeRecord(record: TradeRecord): Promise<void> {\n let records: TradeRecord[] = [];\n try {\n const data = await readFile(this.historyPath, 'utf-8');\n records = JSON.parse(data) as TradeRecord[];\n } catch {\n // Start fresh\n }\n\n records.push(record);\n await mkdir(dirname(this.historyPath), { recursive: true });\n const tmpPath = this.historyPath + '.tmp';\n await writeFile(tmpPath, JSON.stringify(records, null, 2), 'utf-8');\n await rename(tmpPath, this.historyPath);\n }\n}\n","/**\n * Risk management module — position sizing, drawdown limits, stop-loss management.\n */\n\nimport chalk from 'chalk';\n\nexport interface PortfolioState {\n totalValue: number;\n positions: Position[];\n cash: number;\n dailyPnl: number;\n weeklyPnl: number;\n monthlyPnl: number;\n lastDailyReset?: number;\n lastWeeklyReset?: number;\n lastMonthlyReset?: number;\n /** Token → timestamp of last stop-loss exit. Used to enforce cooldown\n * before re-entry to prevent the stop-reentry-stop pattern. */\n stopCooldowns?: Record<string, number>;\n /** Token → count of consecutive losses. Reset to 0 on a winning trade.\n * When count >= TOKEN_CONSEC_LOSS_LIMIT, a 24h cooldown is enforced\n * before re-entry to prevent serial-loser patterns (e.g. AAVE: 4 trades,\n * 3 losses, -$144.75). */\n tokenConsecLosses?: Record<string, number>;\n /** Token → timestamp when the consecutive-loss cooldown was triggered.\n * Used alongside tokenConsecLosses to enforce a 24h ban after N losses. */\n tokenLossCooldowns?: Record<string, number>;\n /** Orca-inspired: PnL-aware daily cap, see README / PR #223.\n * Count of NEW position entries (not pyramid adds) opened since the\n * last daily reset. Used together with getDynamicDailyCap() to throttle\n * entry frequency based on the day's realized PnL. */\n dailyEntries?: number;\n /** Timestamp of the last `dailyEntries` reset — tracked independently\n * from `lastDailyReset` so future boundary logic can diverge if needed\n * (and so legacy portfolio.json files without this field reset cleanly). */\n lastDailyEntriesReset?: number;\n /** Portfolio value at inception — used to compute cumulative PnL%.\n * Defaults to the DEFAULT_PORTFOLIO value ($10k) for legacy files, or\n * the on-chain vault balance for live funds initialized via\n * `initFromOnChain`. Never updated after the first load. */\n initialValue?: number;\n}\n\nexport interface Position {\n tokenId: string;\n symbol: string;\n /** 'long' (default for backward compat) or 'short'. */\n side?: 'long' | 'short';\n entryPrice: number;\n currentPrice: number;\n quantity: number;\n entryTimestamp: number;\n stopLoss: number;\n takeProfit: number;\n trailingStop?: number;\n strategy: string;\n pnlPercent: number;\n pnlUsd: number;\n /** Number of pyramid adds since initial entry (0 = base position).\n * Capped by RiskManager.canOpenPosition; each add halves the prior add size. */\n addCount?: number;\n /** Timestamp of the most recent add (or initial entry).\n * Used to enforce minimum spacing between adds. */\n lastAddTimestamp?: number;\n /** ATR-14 at the time of entry. Used for per-position trailing stop\n * distance (1.0×ATR) and breakeven trigger calibration. */\n atrAtEntry?: number;\n /** Whether the 50% partial-profit exit has been taken. Prevents\n * re-triggering on subsequent cycles. */\n partialTaken?: boolean;\n /** Orca-inspired: HWM-based profit-lock, see README / PR #223.\n * For LONG: the highest price seen since entry (>= entryPrice).\n * For SHORT: the lowest price seen since entry (<= entryPrice).\n * Updated each cycle in `updateTrailingStops`. Enables stop-loss\n * ratcheting based on peak-to-date gain rather than unrealized PnL\n * alone — so a retrace followed by a new peak always advances the\n * lock, never resets it. Persisted to portfolio.json. Legacy positions\n * loaded without this field default to entryPrice (safest — never\n * locks too aggressively on the first cycle post-upgrade). */\n peakPrice?: number;\n}\n\nexport interface RiskConfig {\n maxPortfolioRisk: number;\n maxSinglePosition: number;\n maxCorrelatedExposure: number;\n maxConcurrentTrades: number;\n hardStopPercent: number;\n trailingStopAtr: number;\n /** Trailing stop as a fraction of current price (e.g. 0.05 = stop at price × 0.95).\n * Used by updateTrailingStops() which runs each loop cycle without needing ATR.\n * Set to 0 to disable. */\n trailingStopPct: number;\n /** Move stop to entry (breakeven) once position gains this percent.\n * E.g. 0.02 = after +2% unrealized gain, stop tightens to entry price.\n * Set to 0 to disable. */\n breakevenTriggerPct: number;\n /** Orca-inspired HWM profit-lock, see README / PR #223.\n * Stepped table: each {trigger, lockPct} pair means \"once PnL% from\n * entry crosses `trigger`, ratchet stop-loss to lock in `lockPct` of\n * the peak move from entry\".\n * For LONG: stop = entry + (peakPrice - entry) * lockPct.\n * For SHORT: stop = entry - (entry - peakPrice) * lockPct.\n * Entries iterate in order; highest-triggered tier wins (stricter\n * locks override looser ones) and stops only ratchet up — never down. */\n profitLockSteps: Array<{ trigger: number; lockPct: number }>;\n dailyLossLimit: number;\n weeklyLossLimit: number;\n monthlyLossLimit: number;\n maxSlippage: Record<string, number>;\n riskPerTrade: number;\n /** Orca-inspired: PnL-aware daily cap, see README / PR #223.\n * When set, short-circuits the tiered step function in\n * `getDynamicDailyCap()` and uses this value directly. Useful for\n * tests or an aggressive-mode config override. Leave unset for the\n * default PnL-tiered behavior. */\n dailyCapOverride?: number;\n}\n\n/**\n * Default risk config.\n *\n * `trailingStopPct` / `breakevenTriggerPct` / `profitLockSteps` are now ON\n * by default based on paper-trading analysis showing active trailing\n * significantly improves risk-adjusted returns. These match\n * RECOMMENDED_TRAILING_CONFIG (minus the third profit-lock step).\n *\n * Override via config:\n * sherwood agent config --set trailingStopPct=0\n * sherwood agent config --set breakevenTriggerPct=0\n */\nexport const DEFAULT_RISK_CONFIG: RiskConfig = {\n maxPortfolioRisk: 0.15,\n maxSinglePosition: 0.55, // 55% notional (with 3x leverage: ~18% margin)\n maxCorrelatedExposure: 0.25,\n maxConcurrentTrades: 8,\n hardStopPercent: 0.10, // 10% hard stop (was 5% — wider to match 3.5x ATR stops)\n trailingStopAtr: 3.5, // match executor ATR multiplier (was 1.5x — autoresearch: wider = better)\n trailingStopPct: 0.03, // 3% trail (autoresearch: tighter trail locks profits faster)\n breakevenTriggerPct: 0.015, // move to breakeven after +1.5% gain\n // Orca-inspired HWM profit-lock (see README / PR #223): each entry locks a\n // percentage of the peak-to-date move. Tiers cascade — the highest\n // triggered tier wins — and stops never ratchet down.\n profitLockSteps: [\n { trigger: 0.05, lockPct: 0.30 }, // +5% gain → lock 30% of peak move\n { trigger: 0.10, lockPct: 0.50 }, // +10% gain → lock 50% of peak move\n { trigger: 0.15, lockPct: 0.70 }, // +15% gain → lock 70% of peak move\n { trigger: 0.20, lockPct: 0.85 }, // +20% gain → lock 85% of peak move\n ],\n dailyLossLimit: 0.05,\n weeklyLossLimit: 0.10,\n monthlyLossLimit: 0.15,\n maxSlippage: { large: 0.005, mid: 0.015, small: 0.03 },\n riskPerTrade: 0.02,\n};\n\n/**\n * Opinionated trailing-stop preset.\n *\n * To enable all three mechanisms at once:\n * sherwood agent config --set trailingStopPct=0.05\n * sherwood agent config --set breakevenTriggerPct=0.02\n * (profitLockSteps currently requires editing config.json directly)\n */\n/**\n * Short-term trailing config (1-2 day holds).\n * Tighter stops and faster profit-locking than swing trading.\n */\nexport const RECOMMENDED_TRAILING_CONFIG = {\n trailingStopPct: 0.04, // 4% trail (autoresearch: wider stops let winners run)\n breakevenTriggerPct: 0.02, // move to breakeven after +2% gain (matches partial profit trigger)\n // HWM tiers: lock a growing fraction of the peak move from entry.\n profitLockSteps: [\n { trigger: 0.05, lockPct: 0.30 }, // +5% gain → lock 30% of peak move\n { trigger: 0.10, lockPct: 0.50 }, // +10% gain → lock 50% of peak move\n { trigger: 0.15, lockPct: 0.70 }, // +15% gain → lock 70% of peak move\n { trigger: 0.20, lockPct: 0.85 }, // +20% gain → lock 85% of peak move\n ],\n} as const;\n\n/** Pyramiding configuration — conservative defaults to limit blowup risk\n * on a single name. With MAX_PYRAMID_ADDS=2 and halving size each add,\n * total exposure caps at 1.0x + 0.5x + 0.25x = 1.75x of the base size. */\nexport const MAX_PYRAMID_ADDS = 2;\nexport const PYRAMID_MIN_SPACING_MS = 4 * 60 * 60 * 1000; // 4 hours\nexport const STOP_COOLDOWN_MS = 4 * 60 * 60 * 1000; // 4 hours after a stop-loss\n\n/** Number of consecutive losses on a single token before enforcing a longer cooldown. */\nexport const TOKEN_CONSEC_LOSS_LIMIT = 2;\n/** Cooldown duration after hitting the consecutive-loss limit (24 hours). */\nexport const TOKEN_LOSS_COOLDOWN_MS = 24 * 60 * 60 * 1000;\n\nconst EMPTY_PORTFOLIO: PortfolioState = {\n totalValue: 0,\n positions: [],\n cash: 0,\n dailyPnl: 0,\n weeklyPnl: 0,\n monthlyPnl: 0,\n};\n\nexport class RiskManager {\n private config: RiskConfig;\n private portfolio: PortfolioState;\n\n constructor(config?: Partial<RiskConfig>) {\n this.config = { ...DEFAULT_RISK_CONFIG, ...config };\n this.portfolio = { ...EMPTY_PORTFOLIO };\n }\n\n /** Base risk-per-trade fraction (e.g. 0.02 = 2%). Exposed so callers can\n * apply conviction / profile multipliers without duplicating the config. */\n getRiskPerTrade(): number {\n return this.config.riskPerTrade;\n }\n\n /**\n * Orca-inspired PnL-aware daily cap (see README / PR #223).\n *\n * Returns the maximum number of NEW position entries allowed today based\n * on realized daily PnL as a fraction of the start-of-day portfolio\n * value. Tiers (step function):\n *\n * >= +5% → 12 (\"hot hand\")\n * [ 0%, +5%) → 8\n * [-5%, 0%) → 5\n * [-15%, -5%) → 3\n * [-25%,-15%) → 1\n * < -25% → 0 (circuit breaker)\n *\n * `config.dailyCapOverride` short-circuits to a fixed value when set\n * (tests / aggressive mode). The start-of-day value is reconstructed\n * as `totalValue - dailyPnl` — same trick loop.ts uses to derive\n * dailyPnlPct for the judge.\n *\n * Orthogonal to `maxConcurrentTrades`: that limits simultaneous open\n * positions; this limits turnover per day.\n */\n getDynamicDailyCap(): number {\n // Override short-circuit\n if (\n this.config.dailyCapOverride !== undefined\n && Number.isFinite(this.config.dailyCapOverride)\n ) {\n return Math.max(0, Math.floor(this.config.dailyCapOverride));\n }\n\n const totalValue = Number.isFinite(this.portfolio.totalValue) ? this.portfolio.totalValue : 0;\n const dailyPnl = Number.isFinite(this.portfolio.dailyPnl) ? this.portfolio.dailyPnl : 0;\n // Reconstruct start-of-day value (mirrors loop.ts:691 logic). Fall\n // back to cash when totalValue is unset to avoid a false 0% baseline.\n const startOfDayValue = (totalValue - dailyPnl) > 0\n ? totalValue - dailyPnl\n : (Number.isFinite(this.portfolio.cash) ? this.portfolio.cash : 0);\n\n if (startOfDayValue <= 0) {\n // No baseline to measure against — fall back to the middle tier.\n return 5;\n }\n\n const pnlPct = dailyPnl / startOfDayValue;\n\n if (pnlPct >= 0.05) return 12;\n if (pnlPct >= 0) return 8;\n if (pnlPct >= -0.05) return 5;\n if (pnlPct >= -0.15) return 3;\n if (pnlPct >= -0.25) return 1;\n return 0; // circuit breaker — no new entries today\n }\n\n /** Check if we can open a new position (or pyramid into an existing one).\n * When `direction` is omitted, defaults to 'long' for backward compatibility.\n * An existing position with a different direction always rejects (no flips\n * via pyramid — flips must explicitly close the prior position first). */\n canOpenPosition(token: string, sizeUsd: number, direction: 'long' | 'short' = 'long'): { allowed: boolean; reason?: string } {\n // Check concurrent trades limit\n if (this.portfolio.positions.length >= this.config.maxConcurrentTrades) {\n return { allowed: false, reason: `Max concurrent trades (${this.config.maxConcurrentTrades}) reached` };\n }\n\n // Orca-inspired: PnL-aware daily entry cap (see README / PR #223).\n // Only applies to NEW positions — pyramid adds into an existing token\n // go through their own spacing / addCount guards below and do not\n // count toward the daily turnover budget.\n const isNewEntry = !this.portfolio.positions.find((p) => p.tokenId === token);\n if (isNewEntry) {\n const dailyCap = this.getDynamicDailyCap();\n const entriesToday = Number.isFinite(this.portfolio.dailyEntries)\n ? (this.portfolio.dailyEntries ?? 0)\n : 0;\n if (entriesToday >= dailyCap) {\n const totalValue = Number.isFinite(this.portfolio.totalValue) ? this.portfolio.totalValue : 0;\n const dailyPnl = Number.isFinite(this.portfolio.dailyPnl) ? this.portfolio.dailyPnl : 0;\n const startOfDayValue = (totalValue - dailyPnl) > 0 ? totalValue - dailyPnl : 0;\n const pnlPct = startOfDayValue > 0 ? (dailyPnl / startOfDayValue) * 100 : 0;\n return {\n allowed: false,\n reason: `Dynamic daily cap (${dailyCap}) reached — PnL ${pnlPct >= 0 ? '+' : ''}${pnlPct.toFixed(1)}%`,\n };\n }\n }\n\n // Check single position size limit\n const portfolioValue = this.portfolio.totalValue || this.portfolio.cash;\n if (portfolioValue > 0) {\n const positionPct = sizeUsd / portfolioValue;\n if (positionPct > this.config.maxSinglePosition) {\n return {\n allowed: false,\n reason: `Position size ${(positionPct * 100).toFixed(1)}% exceeds max ${(this.config.maxSinglePosition * 100).toFixed(0)}% per trade`,\n };\n }\n }\n\n // Check total portfolio risk by evaluating potential aggregate loss\n if (portfolioValue > 0) {\n const currentRiskExposure = this.portfolio.positions.reduce(\n (sum, p) => {\n // Estimate max loss per position using stop loss distance\n const maxLossPerPosition = Math.abs(p.entryPrice - p.stopLoss) * p.quantity;\n return sum + maxLossPerPosition;\n },\n 0,\n );\n\n // Estimate new position risk (assuming 8% stop loss)\n const newPositionRisk = sizeUsd * 0.08;\n const totalRiskExposure = currentRiskExposure + newPositionRisk;\n\n if (totalRiskExposure / portfolioValue > this.config.maxPortfolioRisk) {\n return {\n allowed: false,\n reason: `Total portfolio risk ${(totalRiskExposure / portfolioValue * 100).toFixed(1)}% would exceed max ${(this.config.maxPortfolioRisk * 100).toFixed(0)}%`,\n };\n }\n }\n\n // Check post-stop cooldown — prevent rapid re-entry after a stop loss\n const cooldowns = this.portfolio.stopCooldowns ?? {};\n const lastStop = cooldowns[token];\n if (lastStop !== undefined) {\n const elapsed = Date.now() - lastStop;\n if (elapsed < STOP_COOLDOWN_MS) {\n const remainHrs = ((STOP_COOLDOWN_MS - elapsed) / 3_600_000).toFixed(1);\n return { allowed: false, reason: `Stop cooldown active for ${token} (${remainHrs}h remaining)` };\n }\n }\n\n // Check per-token consecutive loss cooldown — prevent serial-loser re-entry.\n // After TOKEN_CONSEC_LOSS_LIMIT consecutive losses on a token, enforce a\n // 24h ban. Prevents the AAVE pattern (4 trades, 3 losses, -$144.75).\n const consecLosses = this.portfolio.tokenConsecLosses ?? {};\n const lossCooldowns = this.portfolio.tokenLossCooldowns ?? {};\n const tokenLosses = consecLosses[token] ?? 0;\n if (tokenLosses >= TOKEN_CONSEC_LOSS_LIMIT) {\n const lossCooldownStart = lossCooldowns[token];\n if (lossCooldownStart !== undefined) {\n const elapsed = Date.now() - lossCooldownStart;\n if (elapsed < TOKEN_LOSS_COOLDOWN_MS) {\n const remainHrs = ((TOKEN_LOSS_COOLDOWN_MS - elapsed) / 3_600_000).toFixed(1);\n return {\n allowed: false,\n reason: `Token loss cooldown: ${token} has ${tokenLosses} consecutive losses (${remainHrs}h remaining of 24h ban)`,\n };\n }\n }\n }\n\n // Check if we already have a position in this token.\n // Pyramiding (adding to a winning position) is allowed up to MAX_PYRAMID_ADDS\n // total adds, with at least PYRAMID_MIN_SPACING_MS between adds. The caller\n // (TradeExecutor) is responsible for halving the size each add and matching\n // the existing direction. If those preconditions aren't met, the executor\n // should not call canOpenPosition for an add.\n const existing = this.portfolio.positions.find((p) => p.tokenId === token);\n if (existing) {\n const existingSide = existing.side ?? 'long';\n if (existingSide !== direction) {\n return { allowed: false, reason: `Conflicting position in ${token} (existing ${existingSide}, signal ${direction}) — close first` };\n }\n const addCount = existing.addCount ?? 0;\n if (addCount >= MAX_PYRAMID_ADDS) {\n return { allowed: false, reason: `Pyramid cap reached for ${token} (${MAX_PYRAMID_ADDS} adds)` };\n }\n const lastAdd = existing.lastAddTimestamp ?? existing.entryTimestamp;\n const elapsed = Date.now() - lastAdd;\n if (elapsed < PYRAMID_MIN_SPACING_MS) {\n const remainHrs = ((PYRAMID_MIN_SPACING_MS - elapsed) / 3_600_000).toFixed(1);\n return { allowed: false, reason: `Pyramid spacing not met for ${token} (next add in ${remainHrs}h)` };\n }\n }\n\n // Check cash availability — compare margin requirement (33%) against cash,\n // not full notional. openPosition debits margin, not full notional.\n const MARGIN_FRACTION = 0.33;\n const marginRequired = sizeUsd * MARGIN_FRACTION;\n if (marginRequired > this.portfolio.cash) {\n return { allowed: false, reason: `Insufficient cash: need $${marginRequired.toFixed(2)} margin (${(MARGIN_FRACTION*100).toFixed(0)}% of $${sizeUsd.toFixed(0)}), have $${this.portfolio.cash.toFixed(2)}` };\n }\n\n // Check drawdown limits\n const drawdown = this.isDrawdownLimitHit();\n if (drawdown.paused) {\n return { allowed: false, reason: drawdown.message };\n }\n\n // Check correlated exposure by token category\n const TOKEN_CATEGORIES: Record<string, string> = {\n bitcoin: 'L1', ethereum: 'L1', solana: 'L1', avalanche: 'L1', cardano: 'L1',\n polkadot: 'L1', near: 'L1', cosmos: 'L1', sui: 'L1', aptos: 'L1',\n uniswap: 'DeFi', aave: 'DeFi', maker: 'DeFi', compound: 'DeFi', curve: 'DeFi',\n lido: 'DeFi', sushi: 'DeFi', pancakeswap: 'DeFi', jupiter: 'DeFi',\n arbitrum: 'L2', optimism: 'L2', polygon: 'L2', 'starknet': 'L2', base: 'L2',\n 'zksync': 'L2', mantle: 'L2',\n };\n\n const tokenCategory = TOKEN_CATEGORIES[token];\n if (tokenCategory && portfolioValue > 0) {\n const correlatedExposure = this.portfolio.positions\n .filter((p) => TOKEN_CATEGORIES[p.tokenId] === tokenCategory)\n .reduce((sum, p) => sum + p.quantity * p.currentPrice, 0);\n const newExposure = (correlatedExposure + sizeUsd) / portfolioValue;\n if (newExposure > this.config.maxCorrelatedExposure) {\n return {\n allowed: false,\n reason: `Correlated exposure for ${tokenCategory} would be ${(newExposure * 100).toFixed(1)}% (limit: ${(this.config.maxCorrelatedExposure * 100).toFixed(0)}%)`,\n };\n }\n }\n\n return { allowed: true };\n }\n\n /** Calculate position size using classic risk-based formula */\n calculatePositionSize(\n entryPrice: number,\n stopLossPrice: number,\n portfolioValue: number,\n maxRiskPercent?: number,\n ): { quantity: number; sizeUsd: number; riskUsd: number } {\n // Guard against invalid inputs\n if (entryPrice <= 0 || portfolioValue <= 0) {\n return { quantity: 0, sizeUsd: 0, riskUsd: 0 };\n }\n\n const riskPct = maxRiskPercent ?? this.config.riskPerTrade;\n let riskUsd = portfolioValue * riskPct;\n const riskPerUnit = Math.abs(entryPrice - stopLossPrice);\n\n if (riskPerUnit <= 0) {\n return { quantity: 0, sizeUsd: 0, riskUsd: 0 };\n }\n\n // positionSize = (portfolioValue * maxRiskPercent) / (entryPrice - stopLossPrice)\n let quantity = riskUsd / riskPerUnit;\n let sizeUsd = quantity * entryPrice;\n\n // Cap at max single position size FIRST — floor must not exceed cap.\n const maxSizeUsd = portfolioValue * this.config.maxSinglePosition;\n if (sizeUsd > maxSizeUsd) {\n sizeUsd = maxSizeUsd;\n quantity = maxSizeUsd / entryPrice;\n riskUsd = quantity * riskPerUnit;\n }\n\n // Minimum position floor: Hyperliquid requires ~$10+ notional per order.\n // Applied AFTER the cap so floor never exceeds maxSinglePosition.\n // Only scales up if vault can afford 2× the floor (safety buffer).\n const MIN_POSITION_USD = 15;\n const effectiveFloor = Math.min(MIN_POSITION_USD, maxSizeUsd);\n if (sizeUsd < effectiveFloor && portfolioValue >= MIN_POSITION_USD * 2) {\n sizeUsd = effectiveFloor;\n quantity = sizeUsd / entryPrice;\n riskUsd = quantity * riskPerUnit;\n }\n\n return { quantity, sizeUsd, riskUsd };\n }\n\n /** Check drawdown limits — returns true if trading should be paused */\n isDrawdownLimitHit(): { paused: boolean; level: 'daily' | 'weekly' | 'monthly' | null; message: string } {\n const portfolioValue = this.portfolio.totalValue || this.portfolio.cash;\n if (portfolioValue <= 0) {\n return { paused: false, level: null, message: 'No portfolio value set' };\n }\n\n const dailyPct = Math.abs(this.portfolio.dailyPnl) / portfolioValue;\n const weeklyPct = Math.abs(this.portfolio.weeklyPnl) / portfolioValue;\n const monthlyPct = Math.abs(this.portfolio.monthlyPnl) / portfolioValue;\n\n if (this.portfolio.dailyPnl < 0 && dailyPct >= this.config.dailyLossLimit) {\n return {\n paused: true,\n level: 'daily',\n message: `Daily loss limit hit: ${(dailyPct * 100).toFixed(1)}% (limit: ${(this.config.dailyLossLimit * 100).toFixed(0)}%)`,\n };\n }\n\n if (this.portfolio.weeklyPnl < 0 && weeklyPct >= this.config.weeklyLossLimit) {\n return {\n paused: true,\n level: 'weekly',\n message: `Weekly loss limit hit: ${(weeklyPct * 100).toFixed(1)}% (limit: ${(this.config.weeklyLossLimit * 100).toFixed(0)}%)`,\n };\n }\n\n if (this.portfolio.monthlyPnl < 0 && monthlyPct >= this.config.monthlyLossLimit) {\n return {\n paused: true,\n level: 'monthly',\n message: `Monthly loss limit hit: ${(monthlyPct * 100).toFixed(1)}% (limit: ${(this.config.monthlyLossLimit * 100).toFixed(0)}%)`,\n };\n }\n\n return { paused: false, level: null, message: 'Within limits' };\n }\n\n /**\n * Ratchet stop-losses each cycle based on current prices.\n * Applies three independent mechanisms, whichever gives the tightest stop:\n *\n * 1. Breakeven: if PnL% crosses +breakevenTriggerPct, move stop to entryPrice.\n * Locks in \"no loss\" once the trade is meaningfully in the money.\n *\n * 2. Orca-inspired HWM profit-lock (see README / PR #223): stepped table\n * keyed on \"gain from entry\". Each triggered tier locks a growing\n * fraction of the peak-to-date move:\n * LONG: stop = entry + (peakPrice - entry) * lockPct\n * SHORT: stop = entry - (entry - peakPrice) * lockPct\n * `peakPrice` tracks the best price observed since entry and is\n * updated on every call (max for LONG, min for SHORT). A retrace\n * followed by a new peak advances the lock — never resets it.\n *\n * 3. Percent-trail: stop tracks currentPrice × (1 ± trailingStopPct).\n * Continuous trail — tracks new highs (LONG) / lows (SHORT).\n *\n * All mechanisms respect the \"stops never loosen\" invariant.\n * `peakPrice` is updated even when no stop-change fires (so future\n * cycles see the ratchet). Returns a new array — does not mutate input.\n *\n * Legacy positions loaded from portfolio.json without `peakPrice`\n * default to `entryPrice` (safest — never triggers a tier on the\n * first cycle after upgrade until price actually advances).\n */\n updateTrailingStops(positions: Position[]): Position[] {\n return positions.map((pos) => {\n if (pos.currentPrice <= 0 || pos.entryPrice <= 0) return pos;\n\n const isShort = pos.side === 'short';\n\n // Update peak-price HWM. For LONG, peak = max observed price.\n // For SHORT, peak = min observed price (most favorable for a short).\n // Legacy positions without `peakPrice` seed from entryPrice — this\n // is the safer default: on the first post-upgrade cycle, no HWM\n // tier will fire until price actually moves beyond entry.\n const priorPeak = Number.isFinite(pos.peakPrice) ? (pos.peakPrice as number) : pos.entryPrice;\n const newPeak = isShort\n ? Math.min(priorPeak, pos.currentPrice)\n : Math.max(priorPeak, pos.currentPrice);\n\n // Direction-aware PnL from entry\n const pnlPct = isShort\n ? (pos.entryPrice - pos.currentPrice) / (pos.entryPrice || 1)\n : (pos.currentPrice - pos.entryPrice) / (pos.entryPrice || 1);\n let newStop = pos.stopLoss;\n\n if (isShort) {\n // For SHORTS: stops sit ABOVE price. \"Tighter\" = LOWER.\n\n // 1. Breakeven: move stop down to entry\n if (this.config.breakevenTriggerPct > 0 && pnlPct >= this.config.breakevenTriggerPct) {\n newStop = Math.min(newStop, pos.entryPrice);\n }\n\n // 2. HWM profit-lock. For shorts, the peak is the lowest price.\n // stop = entry - (entry - peak) * lockPct (peak <= entry)\n // Iterate tiers — highest-triggered tier wins.\n for (const step of this.config.profitLockSteps) {\n if (pnlPct >= step.trigger) {\n const lockedStop = pos.entryPrice - (pos.entryPrice - newPeak) * step.lockPct;\n newStop = Math.min(newStop, lockedStop);\n }\n }\n\n // 3. Percent-trail: track new lows\n if (this.config.trailingStopPct > 0) {\n const trailStop = pos.currentPrice * (1 + this.config.trailingStopPct);\n newStop = Math.min(newStop, trailStop);\n }\n\n if (newStop < pos.stopLoss || newPeak !== priorPeak) {\n return {\n ...pos,\n peakPrice: newPeak,\n stopLoss: newStop < pos.stopLoss ? newStop : pos.stopLoss,\n trailingStop: newStop < pos.stopLoss ? newStop : pos.trailingStop,\n };\n }\n } else {\n // For LONGS: stops sit BELOW price. \"Tighter\" = HIGHER.\n\n // 1. Breakeven\n if (this.config.breakevenTriggerPct > 0 && pnlPct >= this.config.breakevenTriggerPct) {\n newStop = Math.max(newStop, pos.entryPrice);\n }\n\n // 2. HWM profit-lock.\n // stop = entry + (peak - entry) * lockPct (peak >= entry)\n for (const step of this.config.profitLockSteps) {\n if (pnlPct >= step.trigger) {\n const lockedStop = pos.entryPrice + (newPeak - pos.entryPrice) * step.lockPct;\n newStop = Math.max(newStop, lockedStop);\n }\n }\n\n // 3. Percent-trail\n if (this.config.trailingStopPct > 0) {\n const trailStop = pos.currentPrice * (1 - this.config.trailingStopPct);\n newStop = Math.max(newStop, trailStop);\n }\n\n if (newStop > pos.stopLoss || newPeak !== priorPeak) {\n return {\n ...pos,\n peakPrice: newPeak,\n stopLoss: newStop > pos.stopLoss ? newStop : pos.stopLoss,\n trailingStop: newStop > pos.stopLoss ? newStop : pos.trailingStop,\n };\n }\n }\n return pos;\n });\n }\n\n /** Update trailing stop losses using ATR values */\n updateStopLosses(positions: Position[], atrValues: Record<string, number>): Position[] {\n return positions.map((pos) => {\n const atr = atrValues[pos.tokenId];\n if (atr === undefined || atr <= 0) return pos;\n\n const trailingDistance = atr * this.config.trailingStopAtr;\n const currentTrailing = pos.trailingStop ?? pos.stopLoss;\n\n if (pos.side === 'short') {\n // For SHORTS: stop sits ABOVE current price. Tighten only if new\n // candidate is LOWER than the current trailing stop.\n const newTrailingStop = pos.currentPrice + trailingDistance;\n if (newTrailingStop < currentTrailing) {\n return {\n ...pos,\n trailingStop: newTrailingStop,\n // Also update hard stop if trailing is tighter (lower)\n stopLoss: Math.min(pos.stopLoss, newTrailingStop),\n };\n }\n return pos;\n }\n\n // LONGS: stop sits BELOW current price. Only move trailing stop up.\n const newTrailingStop = pos.currentPrice - trailingDistance;\n if (newTrailingStop > currentTrailing) {\n return {\n ...pos,\n trailingStop: newTrailingStop,\n // Also update hard stop if trailing is higher\n stopLoss: Math.max(pos.stopLoss, newTrailingStop),\n };\n }\n\n return pos;\n });\n }\n\n /** Check if any positions should be closed */\n checkExits(\n positions: Position[],\n currentPrices: Record<string, number>,\n ): { toClose: Position[]; reasons: Record<string, string> } {\n const toClose: Position[] = [];\n const reasons: Record<string, string> = {};\n\n for (const pos of positions) {\n const price = currentPrices[pos.tokenId];\n if (price === undefined) continue;\n\n const updatedPos = { ...pos, currentPrice: price };\n const isShort = pos.side === 'short';\n\n // PnL calculation — direction-aware\n const pnlPercent = isShort\n ? (pos.entryPrice - price) / (pos.entryPrice || 1)\n : (price - pos.entryPrice) / (pos.entryPrice || 1);\n\n // Hard stop loss check (direction-aware via pnlPercent)\n if (pnlPercent <= -this.config.hardStopPercent) {\n toClose.push(updatedPos);\n reasons[pos.tokenId] = `Hard stop hit: ${(pnlPercent * 100).toFixed(1)}% loss (limit: -${(this.config.hardStopPercent * 100).toFixed(0)}%)`;\n continue;\n }\n\n // Stop loss check — for shorts, stop is ABOVE entry (price >= stopLoss)\n // for longs, stop is BELOW entry (price <= stopLoss)\n const stopHit = isShort ? price >= pos.stopLoss : price <= pos.stopLoss;\n if (stopHit) {\n toClose.push(updatedPos);\n reasons[pos.tokenId] = `Stop loss hit at $${pos.stopLoss.toFixed(4)} (price: $${price.toFixed(4)})`;\n continue;\n }\n\n // Trailing stop check — same direction logic as stop loss\n if (pos.trailingStop !== undefined) {\n const trailHit = isShort ? price >= pos.trailingStop : price <= pos.trailingStop;\n if (trailHit) {\n toClose.push(updatedPos);\n reasons[pos.tokenId] = `Trailing stop hit at $${pos.trailingStop.toFixed(4)} (price: $${price.toFixed(4)})`;\n continue;\n }\n }\n\n // Take profit check — for shorts, TP is BELOW entry (price <= takeProfit)\n // for longs, TP is ABOVE entry (price >= takeProfit)\n const tpHit = isShort ? price <= pos.takeProfit : price >= pos.takeProfit;\n if (tpHit) {\n toClose.push(updatedPos);\n reasons[pos.tokenId] = `Take profit hit at $${pos.takeProfit.toFixed(4)} (price: $${price.toFixed(4)})`;\n continue;\n }\n\n // Time-based exit: close after 48h if PnL is flat (<1%)\n const holdingHours = (Date.now() - pos.entryTimestamp) / (1000 * 60 * 60);\n // Autoresearch (Apr 24): 96h→84h — kill dead positions sooner for capital recycling.\n if (holdingHours > 84 && pnlPercent < 0.01 && pnlPercent > -0.01) {\n toClose.push(updatedPos);\n reasons[pos.tokenId] = `Time stop: held ${(holdingHours / 24).toFixed(1)} days with only ${(pnlPercent * 100).toFixed(1)}% PnL`;\n continue;\n }\n }\n\n return { toClose, reasons };\n }\n\n /** Update portfolio state */\n updatePortfolio(portfolio: Partial<PortfolioState>): void {\n this.portfolio = { ...this.portfolio, ...portfolio };\n }\n\n /** Get current risk exposure summary */\n getRiskSummary(): string {\n const lines: string[] = [];\n const pv = this.portfolio.totalValue || this.portfolio.cash;\n\n lines.push(chalk.bold('Risk Summary'));\n lines.push(chalk.dim('─'.repeat(40)));\n lines.push(`Portfolio Value: $${pv.toFixed(2)}`);\n lines.push(`Cash: $${this.portfolio.cash.toFixed(2)}`);\n lines.push(`Open Positions: ${this.portfolio.positions.length}/${this.config.maxConcurrentTrades}`);\n\n const totalExposure = this.portfolio.positions.reduce(\n (sum, p) => sum + p.quantity * p.currentPrice,\n 0,\n );\n const exposurePct = pv > 0 ? (totalExposure / pv) * 100 : 0;\n lines.push(`Total Exposure: $${totalExposure.toFixed(2)} (${exposurePct.toFixed(1)}%)`);\n\n const totalUnrealizedPnl = this.portfolio.positions.reduce((sum, p) => sum + p.pnlUsd, 0);\n const pnlColor = totalUnrealizedPnl >= 0 ? chalk.green : chalk.red;\n lines.push(`Unrealized PnL: ${pnlColor('$' + totalUnrealizedPnl.toFixed(2))}`);\n\n lines.push(chalk.dim('─'.repeat(40)));\n lines.push(`Daily PnL: ${this.portfolio.dailyPnl >= 0 ? '+' : ''}$${this.portfolio.dailyPnl.toFixed(2)}`);\n lines.push(`Weekly PnL: ${this.portfolio.weeklyPnl >= 0 ? '+' : ''}$${this.portfolio.weeklyPnl.toFixed(2)}`);\n lines.push(`Monthly PnL: ${this.portfolio.monthlyPnl >= 0 ? '+' : ''}$${this.portfolio.monthlyPnl.toFixed(2)}`);\n\n const drawdown = this.isDrawdownLimitHit();\n if (drawdown.paused) {\n lines.push(chalk.red(`TRADING PAUSED: ${drawdown.message}`));\n }\n\n return lines.join('\\n');\n }\n}\n","/**\n * Reporting module — formats cycle results, portfolio summaries, trade alerts, and metrics.\n */\n\nimport chalk from 'chalk';\nimport type { CycleResult } from './loop.js';\nimport type { PortfolioState, Position } from './risk.js';\nimport type { TradeRecord } from './portfolio.js';\n\nexport class Reporter {\n /** Format a cycle result as a nicely-formatted console report */\n formatCycleReport(result: CycleResult): string {\n const lines: string[] = [];\n const ts = new Date(result.timestamp).toLocaleTimeString();\n\n lines.push('');\n lines.push(chalk.bold(` ┌─ Cycle #${result.cycleNumber} ─ ${ts} ─────────────────────────────┐`));\n lines.push(` │ Duration: ${result.duration}ms | Tokens analyzed: ${result.tokensAnalyzed}`);\n lines.push(` │ Trades executed: ${result.tradesExecuted} | Exits processed: ${result.exitsProcessed}`);\n lines.push(chalk.dim(' ├──────────────────────────────────────────────────┤'));\n\n // Signals\n if (result.signals.length > 0) {\n lines.push(chalk.bold(' │ Signals:'));\n for (const sig of result.signals) {\n const color = sig.action.includes('BUY')\n ? chalk.green\n : sig.action.includes('SELL')\n ? chalk.red\n : chalk.yellow;\n const scoreStr = sig.score >= 0 ? `+${sig.score.toFixed(3)}` : sig.score.toFixed(3);\n lines.push(` │ ${sig.token.padEnd(14)} ${color(sig.action.padEnd(12))} ${scoreStr}`);\n }\n } else {\n lines.push(' │ No actionable signals this cycle.');\n }\n\n lines.push(chalk.dim(' ├──────────────────────────────────────────────────┤'));\n\n // Portfolio — show total PnL% since inception + realized (drawdown-gate driver) + unrealized (mark-to-market).\n const realized = result.dailyRealizedPnl;\n const unrealized = result.unrealizedPnl;\n const total = result.totalPnlUsd;\n const totalPct = result.totalPnlPct;\n const fmt = (v: number) => (v >= 0 ? '+$' : '-$') + Math.abs(v).toFixed(2);\n const fmtPct = (v: number) => (v >= 0 ? '+' : '') + (v * 100).toFixed(2) + '%';\n const rColor = realized >= 0 ? chalk.green : chalk.red;\n const uColor = unrealized >= 0 ? chalk.green : chalk.red;\n const tColor = total >= 0 ? chalk.green : chalk.red;\n lines.push(` │ Portfolio: $${result.portfolioValue.toFixed(2)} Total: ${tColor(fmt(total) + ' (' + fmtPct(totalPct) + ')')}`);\n lines.push(` │ Realized: ${rColor(fmt(realized))} Unrealized: ${uColor(fmt(unrealized))}`);\n\n // Errors\n if (result.errors.length > 0) {\n lines.push(chalk.dim(' ├──────────────────────────────────────────────────┤'));\n lines.push(chalk.red(` │ Errors (${result.errors.length}):`));\n for (const err of result.errors.slice(0, 3)) {\n lines.push(chalk.red(` │ ${err.slice(0, 60)}`));\n }\n }\n\n lines.push(chalk.bold(' └──────────────────────────────────────────────────┘'));\n lines.push('');\n\n return lines.join('\\n');\n }\n\n /** Format portfolio state as a detailed summary */\n formatPortfolioReport(state: PortfolioState): string {\n const lines: string[] = [];\n\n lines.push('');\n lines.push(chalk.bold(' Portfolio Summary'));\n lines.push(chalk.dim(' ' + '═'.repeat(60)));\n lines.push(` Total Value: $${state.totalValue.toFixed(2)}`);\n lines.push(` Cash: $${state.cash.toFixed(2)}`);\n lines.push(` Positions: ${state.positions.length}`);\n lines.push('');\n\n if (state.positions.length > 0) {\n lines.push(chalk.bold(' Open Positions:'));\n lines.push(chalk.dim(` ${'Symbol'.padEnd(10)} ${'Qty'.padEnd(12)} ${'Entry'.padEnd(12)} ${'Current'.padEnd(12)} ${'PnL %'.padEnd(10)} PnL $`));\n lines.push(chalk.dim(' ' + '─'.repeat(68)));\n\n for (const p of state.positions) {\n const pnlColor = p.pnlUsd >= 0 ? chalk.green : chalk.red;\n const pctStr = `${(p.pnlPercent * 100).toFixed(1)}%`;\n const usdStr = `${p.pnlUsd >= 0 ? '+' : ''}$${p.pnlUsd.toFixed(2)}`;\n lines.push(\n ` ${p.symbol.padEnd(10)} ${p.quantity.toFixed(4).padEnd(12)} $${p.entryPrice.toFixed(2).padEnd(11)} $${p.currentPrice.toFixed(2).padEnd(11)} ${pnlColor(pctStr.padEnd(10))} ${pnlColor(usdStr)}`,\n );\n }\n lines.push('');\n }\n\n const dailyColor = state.dailyPnl >= 0 ? chalk.green : chalk.red;\n const weeklyColor = state.weeklyPnl >= 0 ? chalk.green : chalk.red;\n const monthlyColor = state.monthlyPnl >= 0 ? chalk.green : chalk.red;\n\n lines.push(` Daily PnL: ${dailyColor((state.dailyPnl >= 0 ? '+' : '') + '$' + state.dailyPnl.toFixed(2))}`);\n lines.push(` Weekly PnL: ${weeklyColor((state.weeklyPnl >= 0 ? '+' : '') + '$' + state.weeklyPnl.toFixed(2))}`);\n lines.push(` Monthly PnL: ${monthlyColor((state.monthlyPnl >= 0 ? '+' : '') + '$' + state.monthlyPnl.toFixed(2))}`);\n lines.push('');\n\n return lines.join('\\n');\n }\n\n /** Format a trade execution alert */\n formatTradeAlert(trade: {\n token: string;\n side: string;\n price: number;\n amount: number;\n strategy: string;\n }): string {\n const emoji = trade.side === 'buy' ? '🟢' : '🔴';\n const sideStr = trade.side.toUpperCase();\n const lines: string[] = [];\n\n lines.push(`${emoji} ${sideStr} ${trade.token.toUpperCase()}`);\n lines.push(` Price: $${trade.price.toFixed(4)}`);\n lines.push(` Amount: $${trade.amount.toFixed(2)}`);\n lines.push(` Strategy: ${trade.strategy}`);\n lines.push(` Time: ${new Date().toISOString()}`);\n\n return lines.join('\\n');\n }\n\n /** Format performance metrics */\n formatMetrics(metrics: {\n totalTrades: number;\n winRate: number;\n avgPnlPercent: number;\n totalPnlUsd: number;\n sharpeRatio: number;\n maxDrawdown: number;\n bestTrade: TradeRecord;\n worstTrade: TradeRecord;\n }): string {\n const lines: string[] = [];\n\n lines.push('');\n lines.push(chalk.bold(' Performance Metrics'));\n lines.push(chalk.dim(' ' + '═'.repeat(50)));\n\n lines.push(` Total Trades: ${metrics.totalTrades}`);\n\n // Win rate with bar\n const winPct = (metrics.winRate * 100).toFixed(1);\n const winBarLen = Math.round(metrics.winRate * 20);\n const winBar = chalk.green('█'.repeat(winBarLen)) + chalk.dim('░'.repeat(20 - winBarLen));\n const winColor = metrics.winRate >= 0.5 ? chalk.green : chalk.red;\n lines.push(` Win Rate: ${winBar} ${winColor(winPct + '%')}`);\n\n // Avg PnL\n const avgColor = metrics.avgPnlPercent >= 0 ? chalk.green : chalk.red;\n lines.push(` Avg PnL: ${avgColor((metrics.avgPnlPercent * 100).toFixed(2) + '%')}`);\n\n // Total PnL\n const totalColor = metrics.totalPnlUsd >= 0 ? chalk.green : chalk.red;\n lines.push(` Total PnL: ${totalColor('$' + metrics.totalPnlUsd.toFixed(2))}`);\n\n // Sharpe\n const sharpeColor = metrics.sharpeRatio >= 1 ? chalk.green : metrics.sharpeRatio >= 0 ? chalk.yellow : chalk.red;\n lines.push(` Sharpe Ratio: ${sharpeColor(metrics.sharpeRatio.toFixed(2))}`);\n\n // Max Drawdown\n const ddBar = chalk.red('█'.repeat(Math.round(Math.min(metrics.maxDrawdown, 1) * 20))) +\n chalk.dim('░'.repeat(20 - Math.round(Math.min(metrics.maxDrawdown, 1) * 20)));\n lines.push(` Max Drawdown: ${ddBar} ${chalk.red((metrics.maxDrawdown * 100).toFixed(1) + '%')}`);\n\n lines.push('');\n\n // Best/worst trade\n if (metrics.bestTrade?.tokenId) {\n const best = metrics.bestTrade;\n lines.push(chalk.green(` Best Trade: ${best.symbol || best.tokenId} ${best.pnlUsd >= 0 ? '+' : ''}$${best.pnlUsd.toFixed(2)} (${(best.pnlPercent * 100).toFixed(1)}%)`));\n }\n if (metrics.worstTrade?.tokenId) {\n const worst = metrics.worstTrade;\n lines.push(chalk.red(` Worst Trade: ${worst.symbol || worst.tokenId} ${worst.pnlUsd >= 0 ? '+' : ''}$${worst.pnlUsd.toFixed(2)} (${(worst.pnlPercent * 100).toFixed(1)}%)`));\n }\n\n lines.push('');\n\n return lines.join('\\n');\n }\n}\n","/**\n * Per-token last-known-good-price cache with two validation rules:\n *\n * 1. **Floor**: reject prices below PRICE_FLOOR. A zero or near-zero tick\n * collapses risk-per-unit (entry - stop) and produces absurd position\n * sizes; any division against it risks Infinity/NaN propagation.\n *\n * 2. **Delta cap**: if a prior anchor exists and the new tick deviates from\n * it by more than MAX_DELTA_PCT, reject the new price and retain the\n * prior anchor. Genuine moves beyond 20% intra-cycle are rare; an\n * orders-of-magnitude typo is catastrophic.\n *\n * Anchors older than STALE_ANCHOR_MS are treated as absent — a fresh tick\n * is more useful than a day-old reference.\n *\n * Persistence mirrors {@link HyperliquidProvider.persistOiCache}: atomic\n * tmp-rename writes, fire-and-forget, errors logged as warnings so a slow\n * disk never blocks the signal hot path.\n */\n\nimport { mkdir, readFile, writeFile, rename } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\n\n/** Prices below this are rejected outright (USD). */\nexport const PRICE_FLOOR = 1e-9;\n\n/** Max allowed relative deviation from the prior anchor (fraction, 0.20 = 20%). */\nexport const MAX_DELTA_PCT = 0.20;\n\n/** Anchors older than this are ignored — better a fresh tick than a stale anchor. */\nexport const STALE_ANCHOR_MS = 2 * 60 * 60 * 1000; // 2 hours\n\ninterface AnchorEntry {\n price: number;\n timestamp: number;\n}\n\nexport type ValidatorResult =\n | { ok: true; price: number }\n | { ok: false; reason: string };\n\nexport class PriceValidator {\n private anchors = new Map<string, AnchorEntry>();\n private cacheDir: string;\n private cacheFile: string;\n private loaded = false;\n private loadPromise: Promise<void> | null = null;\n /** Monotonic counter so concurrent persist() calls don't collide on the tmp filename. */\n private persistSeq = 0;\n /** Serializes persist() writes. Without this, two back-to-back check()\n * calls fire two parallel mkdir → writeFile → rename chains; whichever\n * rename lands last wins, and that's not necessarily the latest snapshot.\n * Chaining onto this promise forces them to run in scheduling order so\n * the final on-disk state always reflects the most recent check(). */\n private persistChain: Promise<void> = Promise.resolve();\n /** Override Date.now() for tests. */\n private clock: () => number;\n\n constructor(opts?: { cacheDir?: string; clock?: () => number }) {\n this.cacheDir = opts?.cacheDir ?? join(homedir(), '.sherwood', 'agent', 'cache');\n this.cacheFile = join(this.cacheDir, 'price-validator.json');\n this.clock = opts?.clock ?? (() => Date.now());\n // Eager load so first check() finds the anchors populated.\n this.loadPromise = this.load();\n }\n\n /** Load anchors from disk. Called once at construction. Never throws. */\n private async load(): Promise<void> {\n try {\n const raw = await readFile(this.cacheFile, 'utf-8');\n const parsed = JSON.parse(raw) as Record<string, AnchorEntry>;\n if (parsed && typeof parsed === 'object') {\n for (const [token, entry] of Object.entries(parsed)) {\n if (\n entry\n && Number.isFinite(entry.price)\n && Number.isFinite(entry.timestamp)\n && entry.price >= PRICE_FLOOR\n ) {\n this.anchors.set(token, entry);\n }\n }\n }\n } catch {\n // No file, corrupt JSON, or permission error — start empty.\n } finally {\n this.loaded = true;\n }\n }\n\n /** Awaited by tests that need a deterministic load — not required for normal use. */\n async ensureLoaded(): Promise<void> {\n if (this.loaded) return;\n if (this.loadPromise) await this.loadPromise;\n }\n\n /**\n * Validate a new tick for `tokenId`. On ok, the anchor is updated and a\n * persist is queued fire-and-forget. On reject, the prior anchor is kept\n * untouched.\n */\n check(tokenId: string, newPrice: number): ValidatorResult {\n // Floor + finite guard — covers 0, negatives, NaN, Infinity.\n if (!Number.isFinite(newPrice) || newPrice < PRICE_FLOOR) {\n return { ok: false, reason: `price ${newPrice} below floor ${PRICE_FLOOR}` };\n }\n\n const now = this.clock();\n const prior = this.anchors.get(tokenId);\n const priorIsFresh = prior !== undefined && now - prior.timestamp < STALE_ANCHOR_MS;\n\n if (priorIsFresh) {\n const oldPrice = prior!.price;\n const delta = Math.abs(newPrice - oldPrice) / oldPrice;\n if (delta > MAX_DELTA_PCT) {\n return {\n ok: false,\n reason:\n `delta ${(delta * 100).toFixed(1)}% from anchor $${oldPrice} to $${newPrice} exceeds ${(MAX_DELTA_PCT * 100).toFixed(0)}%`,\n };\n }\n }\n\n // Accept: update anchor and queue a persist.\n this.anchors.set(tokenId, { price: newPrice, timestamp: now });\n this.persist();\n return { ok: true, price: newPrice };\n }\n\n /**\n * Persist anchors to disk via atomic tmp-rename. Fire-and-forget — callers\n * do NOT await. Errors are logged as warnings.\n */\n private persist(): void {\n const snapshot: Record<string, AnchorEntry> = {};\n for (const [token, entry] of this.anchors.entries()) {\n snapshot[token] = entry;\n }\n // Unique tmp path per persist() call — keeps the tmp filenames distinct\n // even though the chain serializes their actual writes.\n const seq = ++this.persistSeq;\n const tmp = `${this.cacheFile}.tmp.${process.pid}.${seq}`;\n this.persistChain = this.persistChain.then(async () => {\n try {\n await mkdir(this.cacheDir, { recursive: true });\n await writeFile(tmp, JSON.stringify(snapshot), 'utf-8');\n await rename(tmp, this.cacheFile);\n } catch (err) {\n console.warn(`[price-validator] persist failed: ${(err as Error).message}`);\n }\n });\n }\n}\n","/**\n * Simple backtesting framework — replays historical data through strategies.\n */\n\nimport chalk from 'chalk';\nimport { CoinGeckoProvider } from '../providers/data/coingecko.js';\nimport { SentimentProvider } from '../providers/data/sentiment.js';\nimport { getLatestSignals, calculateATR } from './technical.js';\nimport type { Candle } from './technical.js';\nimport { runStrategies } from './strategies/index.js';\nimport type { StrategyContext } from './strategies/types.js';\nimport { computeTradeDecision } from './scoring.js';\nimport type { TradeDecision } from './scoring.js';\nimport { MarketRegimeDetector } from './regime.js';\nimport type { MarketRegime } from './regime.js';\nimport { SignalSmoother, MemorySmootherStorage, DEFAULT_SMOOTHER_CONFIG } from './signal-smoother.js';\n\nexport interface BacktestConfig {\n tokenId: string;\n startDate: string; // ISO date\n endDate: string;\n initialCapital: number;\n strategies: string[]; // strategy names to test\n cycle: '1h' | '4h' | '1d';\n verbose?: boolean; // show detailed decision logs\n /** When true, classify regime per-candle and apply regime-conditional thresholds.\n * Default false → flat ±0.3/±0.6 thresholds (matches old backtest behavior). */\n useRegime?: boolean;\n /** Trailing-stop percent (e.g. 0.05 = 5%). When set and a long position\n * exists, exit if price drops trailingStopPct from the high-water mark\n * since entry. Independent of SELL signal — stops fire first if hit.\n * Default undefined → SELL-signal-only exit. */\n trailingStopPct?: number;\n /** When true, smooth fast/noisy signals with rolling window (in-memory\n * per-simulation, no disk IO). Default false. */\n smoothFastSignals?: boolean;\n /** Override scoring weights (for calibration sweeps). */\n customWeights?: import('./scoring.js').ScoringWeights;\n /** Override BUY threshold (applied symmetrically to STRONG_BUY as buy+0.3).\n * Used by calibrator to sweep threshold values. When set, takes precedence\n * over regime-based thresholds. */\n buyThreshold?: number;\n /** Override SELL threshold (applied symmetrically to STRONG_SELL as sell-0.3). */\n sellThreshold?: number;\n}\n\nexport interface BacktestTrade {\n entryDate: string;\n exitDate: string;\n entryPrice: number;\n exitPrice: number;\n pnlPercent: number;\n signal: string;\n strategies?: string[]; // Which strategies contributed to this trade\n}\n\nexport interface BacktestResult {\n config: BacktestConfig;\n totalReturn: number;\n totalReturnPercent: number;\n sharpeRatio: number;\n maxDrawdown: number;\n winRate: number;\n totalTrades: number;\n trades: BacktestTrade[];\n equityCurve: Array<{ date: string; value: number }>;\n}\n\nexport interface WalkForwardConfig {\n tokenId: string;\n totalDays: number; // e.g. 180\n trainWindow: number; // e.g. 90 days\n testWindow: number; // e.g. 30 days\n stepSize: number; // e.g. 30 days (how much to advance each fold)\n capital: number;\n strategies: string[];\n /** Forwarded to per-fold Backtester. Default false. */\n useRegime?: boolean;\n /** Forwarded to per-fold Backtester. Default undefined (no trailing stop). */\n trailingStopPct?: number;\n /** Forwarded to per-fold Backtester. Default false. */\n smoothFastSignals?: boolean;\n}\n\nexport interface WalkForwardResult {\n folds: Array<{\n trainPeriod: { from: Date; to: Date };\n testPeriod: { from: Date; to: Date };\n trainSharpe: number;\n testSharpe: number;\n trainReturn: number;\n testReturn: number;\n testMaxDrawdown: number;\n tradesInTest: number;\n }>;\n aggregateTestSharpe: number;\n aggregateTestReturn: number;\n avgTestMaxDrawdown: number;\n overfit: boolean; // true if train sharpe >> test sharpe\n overfitRatio: number; // train sharpe / test sharpe\n}\n\n// Strategies that can work with candles-only data in backtest mode\n// Strategies that work with candle data + F&G available in the backtester.\n// Previously limited to 3 candle-only strategies — expanded to include all\n// that fire with the data the backtester provides (candles + fearAndGreed).\nconst BACKTEST_STRATEGIES = [\n 'breakoutOnChain', // 95% fire rate — candle-based\n 'multiTimeframe', // 78% fire rate — candle-based\n 'sentimentContrarian', // 100% fire rate — uses fearAndGreed from context\n 'momentum', // computed from candles directly in the backtest loop\n 'fundingRate', // fires when ctx.fundingRateData is set (won't fire in backtest — ok)\n 'dexFlow', // fires when ctx.dexData is set (won't fire in backtest — ok)\n 'hyperliquidFlow', // fires when ctx.hyperliquidData is set (won't fire — ok)\n];\n\nexport class Backtester {\n private config: BacktestConfig;\n private cg: CoinGeckoProvider;\n private sentimentProvider: SentimentProvider;\n\n constructor(config: BacktestConfig) {\n this.config = config;\n this.cg = new CoinGeckoProvider();\n this.sentimentProvider = new SentimentProvider();\n }\n\n /** Run backtest using historical data from CoinGecko. */\n async run(): Promise<BacktestResult> {\n const data = await this.fetchData();\n return this.simulate(data.candles, data.fearAndGreedData);\n }\n\n /**\n * Fetch historical OHLC + Fear & Greed data for the configured token/date range.\n * Extracted so callers (e.g. the calibrator) can fetch once and replay many\n * configurations against the same dataset without re-hitting CoinGecko.\n */\n async fetchData(): Promise<{ candles: Candle[]; fearAndGreedData: Record<string, number> }> {\n // 1. Fetch historical OHLC data\n const startMs = new Date(this.config.startDate).getTime();\n const endMs = new Date(this.config.endDate).getTime();\n const totalDays = Math.ceil((endMs - startMs) / (24 * 60 * 60 * 1000));\n\n // Use market_chart endpoint (daily prices) — works for any range up to 365 days.\n // OHLC endpoint only returns 12 candles for >30 day ranges on free tier.\n const cgDays = Math.min(Math.max(totalDays, 1), 365);\n const marketData = await this.cg.getMarketData(this.config.tokenId, cgDays);\n\n if (!marketData?.prices?.length) {\n throw new Error(`No price data returned from CoinGecko for ${this.config.tokenId}`);\n }\n\n // Build daily candles from market_chart prices + volumes\n const prices: number[][] = marketData.prices;\n const volumes: number[][] = marketData.total_volumes ?? [];\n\n // Group prices and volumes by day to create OHLCV candles\n const dayMap = new Map<string, { prices: number[]; totalVolume: number; timestamp: number }>();\n\n // Process prices first to establish daily buckets\n for (const [ts, price] of prices) {\n if (ts! < startMs || ts! > endMs) continue;\n const dayKey = new Date(ts!).toISOString().slice(0, 10);\n if (!dayMap.has(dayKey)) {\n dayMap.set(dayKey, { prices: [], totalVolume: 0, timestamp: ts! });\n }\n dayMap.get(dayKey)!.prices.push(price!);\n }\n\n // Sum volumes for each day (instead of averaging)\n for (const [ts, vol] of volumes) {\n if (ts! < startMs || ts! > endMs) continue;\n const dayKey = new Date(ts!).toISOString().slice(0, 10);\n const dayData = dayMap.get(dayKey);\n if (dayData) {\n dayData.totalVolume += vol!;\n }\n }\n\n const allCandles: Candle[] = [...dayMap.entries()]\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([, day]) => {\n const p = day.prices;\n return {\n timestamp: day.timestamp,\n open: p[0]!,\n high: Math.max(...p),\n low: Math.min(...p),\n close: p[p.length - 1]!,\n volume: day.totalVolume, // Use summed volume for the day\n };\n });\n\n if (allCandles.length < 30) {\n throw new Error(`Insufficient data: only ${allCandles.length} candles in date range (need 30+)`);\n }\n\n // 1.5. Fetch Fear & Greed historical data for sentiment-contrarian strategy\n let fearAndGreedData: Record<string, number> = {};\n try {\n const fgData = await this.sentimentProvider.getFearAndGreed();\n for (const entry of fgData) {\n // Convert timestamp to date string and store\n const date = new Date(Number(entry.timestamp) * 1000).toISOString().split('T')[0]!;\n fearAndGreedData[date] = entry.value;\n }\n if (this.config.verbose && Object.keys(fearAndGreedData).length > 0) {\n console.log(chalk.dim(` Loaded ${Object.keys(fearAndGreedData).length} Fear & Greed historical data points`));\n }\n } catch (error) {\n if (this.config.verbose) {\n console.log(chalk.yellow(` Warning: Could not fetch Fear & Greed data: ${(error as Error).message}`));\n }\n }\n\n return { candles: allCandles, fearAndGreedData };\n }\n\n /**\n * Pre-compute per-candle strategy signals and regime classification.\n * Called once per token by the calibrator; result is fed back into\n * `simulate(..., precomputed)` to skip the network-bound strategy stack.\n *\n * Filters signals to BACKTEST_STRATEGIES (the calibrator does not vary\n * `config.strategies`). Regime is always computed and returned —\n * `simulate()` will only consume it when `config.useRegime` is set.\n */\n async precomputeSignals(\n allCandles: Candle[],\n fearAndGreedData: Record<string, number>,\n ): Promise<Array<{ signals: import('./scoring.js').Signal[]; regime: MarketRegime } | null>> {\n const stepSize = this.config.cycle === '1h' ? 1 : this.config.cycle === '4h' ? 4 : 24;\n const step = Math.max(1, Math.floor(stepSize / 24));\n const windowSize = 30;\n\n const out: Array<{ signals: import('./scoring.js').Signal[]; regime: MarketRegime } | null> =\n new Array(allCandles.length).fill(null);\n\n for (let i = windowSize; i < allCandles.length; i += step) {\n const windowCandles = allCandles.slice(Math.max(0, i - 200), i);\n if (windowCandles.length < windowSize) continue;\n const currentCandle = allCandles[i]!;\n const currentDate = new Date(currentCandle.timestamp).toISOString().split('T')[0]!;\n const currentPrice = currentCandle.close;\n\n try {\n const technicals = getLatestSignals(windowCandles);\n\n let fearAndGreed: { value: number; classification: string } | undefined;\n const fgValue = fearAndGreedData[currentDate];\n if (fgValue !== undefined) {\n const classification = fgValue < 25 ? 'Extreme Fear' :\n fgValue < 40 ? 'Fear' :\n fgValue < 60 ? 'Neutral' :\n fgValue < 75 ? 'Greed' : 'Extreme Greed';\n fearAndGreed = { value: fgValue, classification };\n }\n\n const ctx: StrategyContext = {\n tokenId: this.config.tokenId,\n candles: windowCandles,\n technicals,\n fearAndGreed,\n };\n\n const rawSignals = await runStrategies(ctx);\n\n // Same momentum injection as simulate()\n if (windowCandles.length >= 24) {\n const recent = windowCandles.slice(-24);\n const recentLow = Math.min(...recent.map((c) => c.low));\n const recentHigh = Math.max(...recent.map((c) => c.high));\n const pctFromLow = recentLow > 0 ? (currentPrice - recentLow) / recentLow : 0;\n const pctFromHigh = recentHigh > 0 ? (recentHigh - currentPrice) / recentHigh : 0;\n\n let momentumValue = 0;\n if (pctFromHigh < 0.01) momentumValue = Math.min(0.6, pctFromLow * 5);\n else if (pctFromLow < 0.01) momentumValue = -Math.min(0.6, pctFromHigh * 5);\n else if (recentHigh > recentLow) {\n const rangePosition = (currentPrice - recentLow) / (recentHigh - recentLow);\n momentumValue = (rangePosition - 0.5) * 0.4;\n }\n\n if (Math.abs(momentumValue) > 0.02) {\n rawSignals.push({\n name: 'momentum',\n value: momentumValue,\n confidence: Math.min(0.7, 0.3 + Math.abs(momentumValue)),\n source: 'Price Momentum',\n details: `Backtest momentum`,\n });\n }\n }\n\n // Calibrator path: filter to BACKTEST_STRATEGIES (config.strategies is [])\n const filtered = rawSignals.filter((s) => BACKTEST_STRATEGIES.includes(s.name));\n const regime = MarketRegimeDetector.classifyFromCandles(windowCandles).regime;\n\n out[i] = { signals: filtered.length > 0 ? filtered : rawSignals, regime };\n } catch {\n out[i] = null; // skip — same as simulate()\n }\n }\n\n return out;\n }\n\n /**\n * Run the simulation loop on pre-fetched data. Pure of network IO.\n * The calibrator uses this directly after fetchData() to replay many\n * config permutations without re-fetching per permutation.\n *\n * When `precomputed` is supplied, the inner per-candle strategy stack\n * (network-bound) is skipped and the cached signals/regime are used\n * instead. Smoothing is also skipped on the precomputed path — the\n * caller is responsible for applying it before precomputing if needed.\n */\n async simulate(\n allCandles: Candle[],\n fearAndGreedData: Record<string, number>,\n precomputed?: Array<{ signals: import('./scoring.js').Signal[]; regime: MarketRegime } | null>,\n ): Promise<BacktestResult> {\n // 2. Determine step size based on cycle\n const stepSize = this.config.cycle === '1h' ? 1 : this.config.cycle === '4h' ? 4 : 24;\n // CoinGecko OHLC for 90+ days gives daily candles, so step = 1 candle for '1d'\n const step = Math.max(1, Math.floor(stepSize / 24)); // approximate\n\n // 3. Simulate trading\n let capital = this.config.initialCapital;\n // `extremePrice` tracks the high-water mark for longs and the low-water\n // mark for shorts — i.e. the most favorable price seen since entry, used\n // for trailing-stop calculations.\n let position: {\n side: 'long' | 'short';\n entryPrice: number;\n entryDate: string;\n signal: string;\n extremePrice: number;\n entryTimestamp: number;\n } | null = null;\n // Per-simulation in-memory smoother (no disk IO). Maintains rolling\n // buffers across candles so smoothing reflects the same window logic\n // as live runs.\n const smoother = this.config.smoothFastSignals\n ? new SignalSmoother(new MemorySmootherStorage(), DEFAULT_SMOOTHER_CONFIG)\n : null;\n const trades: BacktestTrade[] = [];\n const equityCurve: Array<{ date: string; value: number }> = [];\n const returns: number[] = [];\n\n const windowSize = 30; // min candles needed for indicators\n\n // Show strategy filtering info\n if (this.config.verbose) {\n if (this.config.strategies.length > 0) {\n console.log(chalk.dim(` Using user-specified strategies: ${this.config.strategies.join(', ')}`));\n } else {\n console.log(chalk.dim(` Using backtest strategies: ${BACKTEST_STRATEGIES.join(', ')} + momentum`));\n if (Object.keys(fearAndGreedData).length > 0) {\n console.log(chalk.dim(` Fear & Greed data available — sentimentContrarian active`));\n }\n }\n console.log();\n }\n\n for (let i = windowSize; i < allCandles.length; i += step) {\n // Use historical data only (excluding current candle to avoid look-ahead bias)\n const windowCandles = allCandles.slice(Math.max(0, i - 200), i);\n const currentCandle = allCandles[i]!;\n const currentDate = new Date(currentCandle.timestamp).toISOString().split('T')[0]!;\n const currentPrice = currentCandle.close;\n\n // Skip if insufficient data for indicators\n if (windowCandles.length < windowSize) continue;\n\n // Track equity (direction-aware: shorts profit when price falls)\n const equity = position\n ? capital * (1 + (position.side === 'short'\n ? (position.entryPrice - currentPrice) / position.entryPrice\n : (currentPrice - position.entryPrice) / position.entryPrice))\n : capital;\n equityCurve.push({ date: currentDate, value: equity });\n\n // Compute indicators using only historical data\n let decision: TradeDecision;\n try {\n let filtered: import('./scoring.js').Signal[];\n let regime: MarketRegime | undefined;\n\n if (precomputed) {\n // Fast path — use cached signals + regime from a prior precomputeSignals() pass.\n // Skips the network-bound runStrategies call. Used by the calibrator to replay\n // hundreds of weight/threshold combinations against the same token in seconds.\n const entry = precomputed[i];\n if (!entry) continue;\n filtered = entry.signals;\n regime = this.config.useRegime ? entry.regime : undefined;\n } else {\n const technicals = getLatestSignals(windowCandles);\n\n // Add Fear & Greed data for the current date if available\n let fearAndGreed: { value: number; classification: string } | undefined;\n const fgValue = fearAndGreedData[currentDate];\n if (fgValue !== undefined) {\n const classification = fgValue < 25 ? 'Extreme Fear' :\n fgValue < 40 ? 'Fear' :\n fgValue < 60 ? 'Neutral' :\n fgValue < 75 ? 'Greed' : 'Extreme Greed';\n fearAndGreed = { value: fgValue, classification };\n }\n\n const ctx: StrategyContext = {\n tokenId: this.config.tokenId,\n candles: windowCandles,\n technicals,\n fearAndGreed,\n };\n\n // Run strategies\n let signals = await runStrategies(ctx);\n\n // Add momentum signal (same logic as live in index.ts — computed from candles)\n if (windowCandles.length >= 24) {\n const recent = windowCandles.slice(-24);\n const recentLow = Math.min(...recent.map((c) => c.low));\n const recentHigh = Math.max(...recent.map((c) => c.high));\n const pctFromLow = recentLow > 0 ? (currentPrice - recentLow) / recentLow : 0;\n const pctFromHigh = recentHigh > 0 ? (recentHigh - currentPrice) / recentHigh : 0;\n\n let momentumValue = 0;\n if (pctFromHigh < 0.01) {\n momentumValue = Math.min(0.6, pctFromLow * 5);\n } else if (pctFromLow < 0.01) {\n momentumValue = -Math.min(0.6, pctFromHigh * 5);\n } else if (recentHigh > recentLow) {\n const rangePosition = (currentPrice - recentLow) / (recentHigh - recentLow);\n momentumValue = (rangePosition - 0.5) * 0.4;\n }\n\n if (Math.abs(momentumValue) > 0.02) {\n signals.push({\n name: 'momentum',\n value: momentumValue,\n confidence: Math.min(0.7, 0.3 + Math.abs(momentumValue)),\n source: 'Price Momentum',\n details: `Backtest momentum`,\n });\n }\n }\n\n // Optional smoothing — uses per-simulation in-memory buffer so each\n // candle adds to a rolling window matching live behavior.\n if (smoother) {\n signals = await smoother.smooth(this.config.tokenId, signals, currentCandle.timestamp);\n }\n\n // Filter strategies: if user specified strategies, honor their choice\n // Otherwise, filter to candle-based strategies only\n if (this.config.strategies.length > 0) {\n // User specified strategies — use their selection\n filtered = signals.filter((s) => this.config.strategies.some(\n (name) => s.name.toLowerCase().includes(name.toLowerCase()),\n ));\n } else {\n // No user selection — filter to candle-based strategies only\n filtered = signals.filter((s) => BACKTEST_STRATEGIES.includes(s.name));\n }\n if (filtered.length === 0) filtered = signals;\n\n // Classify regime from the current rolling window (no look-ahead)\n // when --regime is enabled. The backtester operates on a single\n // token's candles; for a true cross-asset regime read you'd swap\n // BTC candles in here, but per-candle BTC/ETH/SOL trends are\n // highly correlated so the asset's own candles are a fair proxy.\n if (this.config.useRegime) {\n regime = MarketRegimeDetector.classifyFromCandles(windowCandles).regime;\n }\n }\n\n decision = computeTradeDecision(\n filtered,\n this.config.customWeights,\n undefined,\n undefined,\n regime,\n );\n\n // Calibrator uses scalar threshold overrides — re-derive action from\n // score if buyThreshold/sellThreshold are set. STRONG_* thresholds\n // extrapolate ±0.3 from the base (matches the regime threshold spread).\n if (this.config.buyThreshold !== undefined || this.config.sellThreshold !== undefined) {\n const buy = this.config.buyThreshold ?? decision.thresholds?.buy ?? 0.3;\n const sell = this.config.sellThreshold ?? decision.thresholds?.sell ?? -0.3;\n // Invariant: buy must strictly exceed sell. Otherwise a score at\n // the collision point fires BUY (>= runs first) while a symmetric\n // reading of the same market condition would fire SELL — the\n // action becomes asymmetrically path-dependent on comparison order.\n if (buy <= sell) {\n throw new Error(\n `Invalid threshold override: buyThreshold (${buy}) must be strictly greater ` +\n `than sellThreshold (${sell}). Collision would produce ambiguous actions at score == threshold.`,\n );\n }\n const strongBuy = buy + 0.3;\n const strongSell = sell - 0.3;\n let action: typeof decision.action;\n if (decision.score >= strongBuy) action = 'STRONG_BUY';\n else if (decision.score >= buy) action = 'BUY';\n else if (decision.score <= strongSell) action = 'STRONG_SELL';\n else if (decision.score <= sell) action = 'SELL';\n else action = 'HOLD';\n decision = { ...decision, action, thresholds: { strongBuy, buy, sell, strongSell } };\n }\n\n // Verbose logging\n if (this.config.verbose) {\n const activeSignals = filtered.filter(s => Math.abs(s.value) > 0.01);\n const signalSummary = activeSignals.map(s =>\n `${s.name}:${s.value >= 0 ? '+' : ''}${s.value.toFixed(2)}`\n ).join(' ');\n const regimeTag = regime ? ` [${regime}]` : '';\n console.log(`${currentDate}: score=${decision.score.toFixed(2)} ${decision.action}${regimeTag} (${signalSummary || 'no signals'})`);\n }\n } catch {\n continue; // skip candle if analysis fails\n }\n\n // ── Execute paper trades with FULL exit logic ──\n // Matches the live system: stop-loss, take-profit, time stop, trailing,\n // AND signal-based exits. Previously only checked SELL signals, so\n // positions rode forever without stops.\n // ATR-adaptive exits — match executor's 3.5×ATR with 3-15% clamp (autoresearch-tuned)\n const atrValues = calculateATR(windowCandles, 14);\n const currentAtr = atrValues.length > 0 ? atrValues[atrValues.length - 1]! : 0;\n const atrPct = currentPrice > 0 && !isNaN(currentAtr) && currentAtr > 0\n ? currentAtr / currentPrice\n : 0.03; // fallback to 3% (floor) when no ATR data\n const STOP_LOSS_PCT = Math.min(0.15, Math.max(0.03, atrPct * 3.5));\n const TAKE_PROFIT_PCT = STOP_LOSS_PCT * 2.0; // maintain 2:1 R:R\n const TIME_STOP_HOURS = 96; // 96h dead-money exit (autoresearch: patience pays)\n const TRAIL_PCT = this.config.trailingStopPct ?? 0.04; // 4% trail (wider)\n\n // Open a long on BUY/STRONG_BUY or a short on SELL/STRONG_SELL\n // (only when flat — pyramid logic lives in the live executor).\n if (!position && (decision.action === 'BUY' || decision.action === 'STRONG_BUY')) {\n position = {\n side: 'long',\n entryPrice: currentPrice,\n entryDate: currentDate,\n signal: decision.action,\n extremePrice: currentPrice,\n entryTimestamp: currentCandle.timestamp,\n };\n } else if (!position && (decision.action === 'SELL' || decision.action === 'STRONG_SELL')) {\n position = {\n side: 'short',\n entryPrice: currentPrice,\n entryDate: currentDate,\n signal: decision.action,\n extremePrice: currentPrice, // for shorts this becomes a low-water mark\n entryTimestamp: currentCandle.timestamp,\n };\n } else if (position) {\n const isShort = position.side === 'short';\n // Track best favorable price: highest for longs, lowest for shorts\n if (isShort) {\n if (currentPrice < position.extremePrice) position.extremePrice = currentPrice;\n } else {\n if (currentPrice > position.extremePrice) position.extremePrice = currentPrice;\n }\n\n // Direction-aware PnL (shorts profit when price falls)\n const pnlPercent = isShort\n ? (position.entryPrice - currentPrice) / position.entryPrice\n : (currentPrice - position.entryPrice) / position.entryPrice;\n const holdingHours = (currentCandle.timestamp - position.entryTimestamp) / (1000 * 60 * 60);\n\n // Check ALL exit conditions (priority order)\n let exitReason: string | null = null;\n\n // 1. Stop loss (3%) — pnlPercent is already direction-aware\n if (pnlPercent <= -STOP_LOSS_PCT) {\n exitReason = `STOP_LOSS (${(pnlPercent * 100).toFixed(1)}%)`;\n }\n // 2. Take profit (6%)\n else if (pnlPercent >= TAKE_PROFIT_PCT) {\n exitReason = `TAKE_PROFIT (+${(pnlPercent * 100).toFixed(1)}%)`;\n }\n // 3. Trailing stop — for longs, exit if price drops X% from high;\n // for shorts, exit if price rises X% from low.\n else if (TRAIL_PCT > 0 && (isShort\n ? currentPrice >= position.extremePrice * (1 + TRAIL_PCT)\n : currentPrice <= position.extremePrice * (1 - TRAIL_PCT))) {\n exitReason = `TRAILING_STOP (extreme $${position.extremePrice.toFixed(2)}, ${isShort ? '+' : '-'}${(TRAIL_PCT * 100).toFixed(1)}%)`;\n }\n // 4. Time stop (48h with <1% PnL)\n else if (holdingHours > TIME_STOP_HOURS && Math.abs(pnlPercent) < 0.01) {\n exitReason = `TIME_STOP (${(holdingHours / 24).toFixed(1)}d, ${(pnlPercent * 100).toFixed(1)}%)`;\n }\n // 5. Signal-flip exit: longs flip on SELL, shorts flip on BUY\n else if (isShort\n ? (decision.action === 'BUY' || decision.action === 'STRONG_BUY')\n : (decision.action === 'SELL' || decision.action === 'STRONG_SELL')) {\n exitReason = decision.action;\n }\n\n if (exitReason) {\n capital *= (1 + pnlPercent);\n returns.push(pnlPercent);\n trades.push({\n entryDate: position.entryDate,\n exitDate: currentDate,\n entryPrice: position.entryPrice,\n exitPrice: currentPrice,\n pnlPercent,\n signal: `${position.signal}${isShort ? ' [SHORT]' : ''} → ${exitReason}`,\n });\n position = null;\n }\n }\n }\n\n // Close any remaining position at last price (direction-aware)\n if (position && allCandles.length > 0) {\n const lastCandle = allCandles[allCandles.length - 1]!;\n const lastPrice = lastCandle.close;\n const lastDate = new Date(lastCandle.timestamp).toISOString().split('T')[0]!;\n const pnlPercent = position.side === 'short'\n ? (position.entryPrice - lastPrice) / position.entryPrice\n : (lastPrice - position.entryPrice) / position.entryPrice;\n capital *= (1 + pnlPercent);\n returns.push(pnlPercent);\n\n trades.push({\n entryDate: position.entryDate,\n exitDate: lastDate,\n entryPrice: position.entryPrice,\n exitPrice: lastPrice,\n pnlPercent,\n signal: `${position.signal}${position.side === 'short' ? ' [SHORT]' : ''} → CLOSE`,\n });\n }\n\n // 4. Compute metrics\n const totalReturn = capital - this.config.initialCapital;\n const totalReturnPercent = totalReturn / this.config.initialCapital;\n const winRate = returns.length > 0\n ? returns.filter((r) => r > 0).length / returns.length\n : 0;\n\n // Sharpe ratio (annualized from daily equity curve returns, not per-trade returns)\n const dailyReturns: number[] = [];\n for (let i = 1; i < equityCurve.length; i++) {\n const prev = equityCurve[i - 1]!.value;\n if (prev > 0) {\n dailyReturns.push((equityCurve[i]!.value - prev) / prev);\n }\n }\n const meanReturn = dailyReturns.length > 0\n ? dailyReturns.reduce((a, b) => a + b, 0) / dailyReturns.length\n : 0;\n const variance = dailyReturns.length > 1\n ? dailyReturns.reduce((sum, r) => sum + (r - meanReturn) ** 2, 0) / (dailyReturns.length - 1)\n : 0;\n const stdDev = Math.sqrt(variance);\n const riskFreeDaily = 0.05 / 252; // 5% annual risk-free rate\n const sharpeRatio = stdDev > 0 ? ((meanReturn - riskFreeDaily) / stdDev) * Math.sqrt(252) : 0;\n\n // Max drawdown\n let maxDrawdown = 0;\n let peak = this.config.initialCapital;\n for (const point of equityCurve) {\n if (point.value > peak) peak = point.value;\n const drawdown = (peak - point.value) / peak;\n if (drawdown > maxDrawdown) maxDrawdown = drawdown;\n }\n\n return {\n config: this.config,\n totalReturn,\n totalReturnPercent,\n sharpeRatio,\n maxDrawdown,\n winRate,\n totalTrades: trades.length,\n trades,\n equityCurve,\n };\n }\n\n /** Walk-forward optimization to prevent overfitting. */\n async walkForwardTest(config: WalkForwardConfig): Promise<WalkForwardResult> {\n const endDate = new Date();\n endDate.setHours(0, 0, 0, 0); // Start of today\n const startDate = new Date(endDate);\n startDate.setDate(startDate.getDate() - config.totalDays);\n\n const folds: WalkForwardResult['folds'] = [];\n const testReturns: number[] = [];\n const testSharpes: number[] = [];\n const testMaxDrawdowns: number[] = [];\n\n let currentStart = new Date(startDate);\n\n while (currentStart.getTime() + (config.trainWindow + config.testWindow) * 24 * 60 * 60 * 1000 <= endDate.getTime()) {\n // Define train period\n const trainStart = new Date(currentStart);\n const trainEnd = new Date(trainStart);\n trainEnd.setDate(trainEnd.getDate() + config.trainWindow);\n\n // Define test period\n const testStart = new Date(trainEnd);\n const testEnd = new Date(testStart);\n testEnd.setDate(testEnd.getDate() + config.testWindow);\n\n try {\n // Run backtest on training period\n const trainBacktester = new Backtester({\n tokenId: config.tokenId,\n startDate: trainStart.toISOString().slice(0, 10),\n endDate: trainEnd.toISOString().slice(0, 10),\n initialCapital: config.capital,\n strategies: config.strategies,\n cycle: '1d',\n verbose: false, // Don't spam verbose output during walk-forward\n useRegime: config.useRegime,\n trailingStopPct: config.trailingStopPct,\n smoothFastSignals: config.smoothFastSignals,\n });\n const trainResult = await trainBacktester.run();\n\n // Run backtest on test period\n const testBacktester = new Backtester({\n tokenId: config.tokenId,\n startDate: testStart.toISOString().slice(0, 10),\n endDate: testEnd.toISOString().slice(0, 10),\n initialCapital: config.capital,\n strategies: config.strategies,\n cycle: '1d',\n verbose: false, // Don't spam verbose output during walk-forward\n useRegime: config.useRegime,\n trailingStopPct: config.trailingStopPct,\n smoothFastSignals: config.smoothFastSignals,\n });\n const testResult = await testBacktester.run();\n\n const fold = {\n trainPeriod: { from: trainStart, to: trainEnd },\n testPeriod: { from: testStart, to: testEnd },\n trainSharpe: trainResult.sharpeRatio,\n testSharpe: testResult.sharpeRatio,\n trainReturn: trainResult.totalReturnPercent,\n testReturn: testResult.totalReturnPercent,\n testMaxDrawdown: testResult.maxDrawdown,\n tradesInTest: testResult.totalTrades,\n };\n\n folds.push(fold);\n testReturns.push(testResult.totalReturnPercent);\n testSharpes.push(testResult.sharpeRatio);\n testMaxDrawdowns.push(testResult.maxDrawdown);\n } catch (error) {\n // Skip fold if data is insufficient\n console.warn(`Skipping fold ${trainStart.toISOString().slice(0, 10)} - ${testEnd.toISOString().slice(0, 10)}: ${error}`);\n }\n\n // Advance to next fold\n currentStart.setDate(currentStart.getDate() + config.stepSize);\n }\n\n if (folds.length === 0) {\n throw new Error('No valid folds could be generated - insufficient data or invalid parameters');\n }\n\n // Calculate aggregate metrics\n const aggregateTestReturn = testReturns.reduce((sum, ret) => sum + ret, 0) / testReturns.length;\n const aggregateTestSharpe = testSharpes.filter(s => !isNaN(s) && isFinite(s)).reduce((sum, s) => sum + s, 0) / testSharpes.filter(s => !isNaN(s) && isFinite(s)).length || 0;\n const avgTestMaxDrawdown = testMaxDrawdowns.reduce((sum, dd) => sum + dd, 0) / testMaxDrawdowns.length;\n\n // Calculate overfitting metrics\n const avgTrainSharpe = folds.map(f => f.trainSharpe).filter(s => !isNaN(s) && isFinite(s)).reduce((sum, s) => sum + s, 0) / folds.filter(f => !isNaN(f.trainSharpe) && isFinite(f.trainSharpe)).length || 0;\n const overfitRatio = avgTrainSharpe > 0 && aggregateTestSharpe > 0 ? avgTrainSharpe / aggregateTestSharpe : 1;\n const overfit = overfitRatio > 2.0; // Flag if train performance is more than 2x better than test\n\n return {\n folds,\n aggregateTestSharpe,\n aggregateTestReturn,\n avgTestMaxDrawdown,\n overfit,\n overfitRatio,\n };\n }\n\n /** Format results for display. */\n formatResults(result: BacktestResult): string {\n const lines: string[] = [];\n\n lines.push('');\n lines.push(chalk.bold(' ┌──────────────────────────────────────────────┐'));\n lines.push(chalk.bold(' │ Backtest Results │'));\n lines.push(chalk.bold(' └──────────────────────────────────────────────┘'));\n lines.push('');\n lines.push(` Token: ${result.config.tokenId}`);\n lines.push(` Period: ${result.config.startDate} → ${result.config.endDate}`);\n lines.push(` Initial: $${result.config.initialCapital.toLocaleString()}`);\n lines.push(` Strategies: ${result.config.strategies.join(', ') || 'all'}`);\n lines.push(` Cycle: ${result.config.cycle}`);\n lines.push('');\n lines.push(chalk.dim(' ' + '═'.repeat(50)));\n\n // Performance metrics\n const retColor = result.totalReturnPercent >= 0 ? chalk.green : chalk.red;\n lines.push(` Total Return: ${retColor('$' + result.totalReturn.toFixed(2))} (${retColor((result.totalReturnPercent * 100).toFixed(2) + '%')})`);\n lines.push(` Final Capital: $${(result.config.initialCapital + result.totalReturn).toFixed(2)}`);\n lines.push(` Sharpe Ratio: ${result.sharpeRatio.toFixed(2)}`);\n lines.push(` Max Drawdown: ${chalk.red((result.maxDrawdown * 100).toFixed(2) + '%')}`);\n lines.push(` Win Rate: ${(result.winRate * 100).toFixed(1)}%`);\n lines.push(` Total Trades: ${result.totalTrades}`);\n\n // Volume quality warning\n const zeroVolumeTrades = result.trades.filter(t => {\n // Check if trades occurred during periods with potentially missing volume data\n return true; // For now, we'll always show this warning since CoinGecko OHLC had volume issues\n }).length;\n const volumeWarning = result.equityCurve.length > 0; // We have equity data, so we can check volume quality\n if (volumeWarning) {\n lines.push('');\n lines.push(chalk.yellow(' ⚠️ Volume Data Quality:'));\n lines.push(chalk.dim(' Volume data sourced from market_chart endpoint and aggregated daily.'));\n lines.push(chalk.dim(' Intraday volume patterns may not be fully captured.'));\n }\n\n // Monthly returns breakdown\n if (result.trades.length > 0) {\n const monthlyReturns = this.calculateMonthlyReturns(result.equityCurve);\n if (Object.keys(monthlyReturns).length > 1) {\n lines.push('');\n lines.push(chalk.bold(' Monthly Returns:'));\n lines.push(chalk.dim(` ${'Month'.padEnd(12)} ${'Return %'.padEnd(10)} Performance`));\n lines.push(chalk.dim(' ' + '─'.repeat(35)));\n\n for (const [month, returnPct] of Object.entries(monthlyReturns).slice(-12)) { // Show last 12 months\n const returnColor = returnPct >= 0 ? chalk.green : chalk.red;\n const bar = this.renderMiniBar(returnPct / 100, 10);\n lines.push(\n ` ${month.padEnd(12)} ${returnColor((returnPct > 0 ? '+' : '') + returnPct.toFixed(1) + '%').padEnd(18)} ${bar}`,\n );\n }\n }\n }\n\n // Strategy performance breakdown (simplified version)\n if (result.trades.length > 0 && result.config.strategies.length > 0) {\n lines.push('');\n lines.push(chalk.bold(' Strategy Performance:'));\n lines.push(chalk.dim(` ${'Strategy'.padEnd(20)} ${'Trades'.padEnd(8)} Win Rate`));\n lines.push(chalk.dim(' ' + '─'.repeat(40)));\n\n // For now, show overall win rate per configured strategy filter\n // In a future enhancement, we could track which specific strategies triggered each trade\n for (const strategy of result.config.strategies.slice(0, 5)) { // Top 5 strategies\n const strategyTrades = Math.floor(result.totalTrades / result.config.strategies.length); // Approximate distribution\n const winRate = result.winRate; // Use overall win rate as approximation\n const winRateColor = winRate >= 0.6 ? chalk.green : winRate >= 0.4 ? chalk.yellow : chalk.red;\n lines.push(\n ` ${strategy.slice(0, 19).padEnd(20)} ${strategyTrades.toString().padEnd(8)} ${winRateColor((winRate * 100).toFixed(1) + '%')}`,\n );\n }\n if (result.config.strategies.length === 0) {\n lines.push(chalk.dim(' All strategies combined'));\n }\n }\n\n // Trade list\n if (result.trades.length > 0) {\n lines.push('');\n lines.push(chalk.bold(' Trades:'));\n lines.push(chalk.dim(` ${'Entry'.padEnd(12)} ${'Exit'.padEnd(12)} ${'Entry$'.padEnd(10)} ${'Exit$'.padEnd(10)} ${'PnL%'.padEnd(10)} Signal`));\n lines.push(chalk.dim(' ' + '─'.repeat(65)));\n\n for (const t of result.trades.slice(-20)) { // show last 20\n const pnlColor = t.pnlPercent >= 0 ? chalk.green : chalk.red;\n lines.push(\n ` ${t.entryDate.padEnd(12)} ${t.exitDate.padEnd(12)} $${t.entryPrice.toFixed(2).padEnd(9)} $${t.exitPrice.toFixed(2).padEnd(9)} ${pnlColor((t.pnlPercent * 100).toFixed(2).padStart(7) + '%')} ${t.signal}`,\n );\n }\n if (result.trades.length > 20) {\n lines.push(chalk.dim(` ... and ${result.trades.length - 20} more trades`));\n }\n }\n\n // Equity curve ASCII art\n if (result.equityCurve.length > 2) {\n lines.push('');\n lines.push(chalk.bold(' Equity Curve:'));\n lines.push(this.renderEquityCurve(result.equityCurve, result.config.initialCapital));\n }\n\n lines.push('');\n return lines.join('\\n');\n }\n\n /** Format walk-forward results for display. */\n formatWalkForwardResults(result: WalkForwardResult, config: WalkForwardConfig): string {\n const lines: string[] = [];\n\n lines.push('');\n lines.push(chalk.bold(' ┌──────────────────────────────────────────────┐'));\n lines.push(chalk.bold(' │ Walk-Forward Optimization Results │'));\n lines.push(chalk.bold(' └──────────────────────────────────────────────┘'));\n lines.push('');\n lines.push(` Token: ${config.tokenId}`);\n lines.push(` Train Window: ${config.trainWindow} days`);\n lines.push(` Test Window: ${config.testWindow} days`);\n lines.push(` Step Size: ${config.stepSize} days`);\n lines.push(` Total Folds: ${result.folds.length}`);\n lines.push(` Strategies: ${config.strategies.join(', ') || 'all'}`);\n lines.push('');\n lines.push(chalk.dim(' ' + '═'.repeat(50)));\n\n // Aggregate performance\n const retColor = result.aggregateTestReturn >= 0 ? chalk.green : chalk.red;\n const overfitColor = result.overfit ? chalk.red : chalk.green;\n lines.push(` Aggregate Test Return: ${retColor((result.aggregateTestReturn * 100).toFixed(2) + '%')}`);\n lines.push(` Aggregate Test Sharpe: ${result.aggregateTestSharpe.toFixed(2)}`);\n lines.push(` Avg Test Max Drawdown: ${chalk.red((result.avgTestMaxDrawdown * 100).toFixed(2) + '%')}`);\n lines.push(` Overfitting Ratio: ${overfitColor(result.overfitRatio.toFixed(2) + (result.overfit ? ' ⚠️ OVERFIT' : ' ✓'))}`);\n\n // Fold-by-fold breakdown\n if (result.folds.length > 0) {\n lines.push('');\n lines.push(chalk.bold(' Fold-by-Fold Results:'));\n lines.push(chalk.dim(` ${'Train Period'.padEnd(23)} ${'Test Period'.padEnd(23)} ${'Train'.padEnd(8)} ${'Test'.padEnd(8)} ${'Test DD%'.padEnd(9)} Trades`));\n lines.push(chalk.dim(' ' + '─'.repeat(90)));\n\n for (const fold of result.folds) {\n const trainPeriodStr = `${fold.trainPeriod.from.toISOString().slice(0, 10)} to ${fold.trainPeriod.to.toISOString().slice(0, 10)}`;\n const testPeriodStr = `${fold.testPeriod.from.toISOString().slice(0, 10)} to ${fold.testPeriod.to.toISOString().slice(0, 10)}`;\n const trainSharpeStr = isFinite(fold.trainSharpe) ? fold.trainSharpe.toFixed(2) : 'N/A';\n const testSharpeStr = isFinite(fold.testSharpe) ? fold.testSharpe.toFixed(2) : 'N/A';\n const testReturnColor = fold.testReturn >= 0 ? chalk.green : chalk.red;\n const testDdStr = (fold.testMaxDrawdown * 100).toFixed(1) + '%';\n\n lines.push(\n ` ${trainPeriodStr.padEnd(23)} ${testPeriodStr.padEnd(23)} ${trainSharpeStr.padEnd(8)} ${testReturnColor(testSharpeStr.padEnd(8))} ${chalk.red(testDdStr.padEnd(9))} ${fold.tradesInTest}`,\n );\n }\n\n // Summary stats\n const winningFolds = result.folds.filter(f => f.testReturn > 0).length;\n const winRate = result.folds.length > 0 ? (winningFolds / result.folds.length * 100).toFixed(1) : '0.0';\n\n lines.push('');\n lines.push(chalk.dim(' ' + '─'.repeat(50)));\n lines.push(` Winning Folds: ${winningFolds}/${result.folds.length} (${winRate}%)`);\n\n if (result.overfit) {\n lines.push('');\n lines.push(chalk.red(' ⚠️ WARNING: Strategy shows signs of overfitting!'));\n lines.push(chalk.red(' Train performance significantly exceeds test performance.'));\n lines.push(chalk.red(' Consider simplifying the strategy or using longer test periods.'));\n }\n }\n\n lines.push('');\n return lines.join('\\n');\n }\n\n /** Calculate monthly returns from equity curve. */\n private calculateMonthlyReturns(equityCurve: Array<{ date: string; value: number }>): Record<string, number> {\n const monthlyReturns: Record<string, number> = {};\n\n if (equityCurve.length === 0) return monthlyReturns;\n\n let currentMonth = '';\n let monthStartValue = equityCurve[0]!.value;\n let monthEndValue = equityCurve[0]!.value;\n\n for (const point of equityCurve) {\n const pointMonth = point.date.slice(0, 7); // YYYY-MM format\n\n if (currentMonth === '') {\n currentMonth = pointMonth;\n monthStartValue = point.value;\n } else if (pointMonth !== currentMonth) {\n // Month changed, calculate return for previous month\n if (monthStartValue > 0) {\n const returnPct = ((monthEndValue - monthStartValue) / monthStartValue) * 100;\n monthlyReturns[currentMonth] = returnPct;\n }\n\n // Start new month\n currentMonth = pointMonth;\n monthStartValue = point.value;\n }\n\n monthEndValue = point.value;\n }\n\n // Don't forget the last month\n if (currentMonth && monthStartValue > 0) {\n const returnPct = ((monthEndValue - monthStartValue) / monthStartValue) * 100;\n monthlyReturns[currentMonth] = returnPct;\n }\n\n return monthlyReturns;\n }\n\n /** Render a mini bar chart for monthly returns. */\n private renderMiniBar(value: number, width: number): string {\n const magnitude = Math.min(Math.abs(value), 0.3); // Cap at 30% for display\n const filled = Math.round(magnitude * width / 0.3);\n\n if (value > 0) {\n return chalk.green('█'.repeat(filled)) + chalk.dim('░'.repeat(width - filled));\n } else if (value < 0) {\n return chalk.red('█'.repeat(filled)) + chalk.dim('░'.repeat(width - filled));\n }\n return chalk.dim('░'.repeat(width));\n }\n\n /** Render a simple ASCII equity curve using box-drawing chars. */\n private renderEquityCurve(\n curve: Array<{ date: string; value: number }>,\n initialCapital: number,\n ): string {\n const width = 60;\n const height = 15;\n\n // Sample curve to fit width\n const sampled: number[] = [];\n const step = Math.max(1, Math.floor(curve.length / width));\n for (let i = 0; i < curve.length; i += step) {\n sampled.push(curve[i]!.value);\n }\n if (sampled.length > width) sampled.length = width;\n\n const minVal = Math.min(...sampled);\n const maxVal = Math.max(...sampled);\n const range = maxVal - minVal || 1;\n\n // Build grid\n const grid: string[][] = [];\n for (let row = 0; row < height; row++) {\n grid.push(new Array(sampled.length).fill(' '));\n }\n\n // Plot points\n for (let col = 0; col < sampled.length; col++) {\n const normalized = (sampled[col]! - minVal) / range;\n const row = height - 1 - Math.round(normalized * (height - 1));\n grid[row]![col] = sampled[col]! >= initialCapital ? '█' : '▓';\n }\n\n // Render with Y-axis labels\n const lines: string[] = [];\n for (let row = 0; row < height; row++) {\n const yVal = maxVal - (row / (height - 1)) * range;\n const label = `$${yVal.toFixed(0)}`.padStart(8);\n const lineStr = grid[row]!.join('');\n const rowColor = yVal >= initialCapital ? chalk.green : chalk.red;\n lines.push(` ${chalk.dim(label)} │${rowColor(lineStr)}`);\n }\n lines.push(` ${''.padStart(8)} └${'─'.repeat(sampled.length)}`);\n\n // X-axis labels\n const firstDate = curve[0]?.date ?? '';\n const lastDate = curve[curve.length - 1]?.date ?? '';\n const axisLabel = ` ${''.padStart(9)}${firstDate}${''.padStart(Math.max(0, sampled.length - firstDate.length - lastDate.length))}${lastDate}`;\n lines.push(chalk.dim(axisLabel));\n\n return lines.join('\\n');\n }\n}\n","/**\n * Multi-token scoring calibration backtester.\n * Fetches data ONCE per token, then replays with different weight/threshold combos.\n */\n\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { Backtester } from \"./backtest.js\";\nimport type { ScoringWeights } from \"./scoring.js\";\nimport type { Candle } from \"./technical.js\";\n\n// ── Weight Profiles ──\n//\n// IMPORTANT: this set deliberately includes BOTH the live production\n// profiles (default-live, majors-live, altcoin-live — pulled from\n// scoring.ts WEIGHT_PROFILES) AND exploratory profiles for tuning.\n// Without the live profiles in the sweep, the calibrator can never tell\n// you \"the current production weights are the best\" — you'd only see\n// rankings of variants. The live entries are SUFFIXED `-live` so they're\n// obvious in the ranked output.\n//\n// To keep this in sync after a scoring.ts weight change: update the\n// `*-live` rows below to match the new production weights.\n\nexport const WEIGHT_PROFILES: Record<string, ScoringWeights> = {\n // ── Live production profiles (from scoring.ts; keep in sync) ──\n \"default-live\": { smartMoney: 0.15, technical: 0.10, sentiment: 0.40, onchain: 0.20, fundamental: 0.10, event: 0.05 },\n \"majors-live\": { smartMoney: 0.15, technical: 0.10, sentiment: 0.40, onchain: 0.35, fundamental: 0.00, event: 0.00 },\n \"altcoin-live\": { smartMoney: 0.15, technical: 0.15, sentiment: 0.30, onchain: 0.20, fundamental: 0.10, event: 0.10 },\n\n // ── Exploratory profiles (historical baselines + variants) ──\n default: { smartMoney: 0.25, technical: 0.20, sentiment: 0.20, onchain: 0.15, fundamental: 0.10, event: 0.10 },\n techHeavy: { smartMoney: 0.10, technical: 0.40, sentiment: 0.15, onchain: 0.15, fundamental: 0.10, event: 0.10 },\n sentHeavy: { smartMoney: 0.10, technical: 0.15, sentiment: 0.40, onchain: 0.15, fundamental: 0.10, event: 0.10 },\n onchainHvy: { smartMoney: 0.10, technical: 0.20, sentiment: 0.15, onchain: 0.35, fundamental: 0.10, event: 0.10 },\n balanced: { smartMoney: 0.17, technical: 0.17, sentiment: 0.17, onchain: 0.17, fundamental: 0.16, event: 0.16 },\n momentum: { smartMoney: 0.15, technical: 0.35, sentiment: 0.10, onchain: 0.25, fundamental: 0.10, event: 0.05 },\n contrarian: { smartMoney: 0.10, technical: 0.10, sentiment: 0.40, onchain: 0.10, fundamental: 0.15, event: 0.15 },\n flowBased: { smartMoney: 0.20, technical: 0.15, sentiment: 0.10, onchain: 0.35, fundamental: 0.10, event: 0.10 },\n};\n\n// Added 0.25 / -0.25 so the calibrator can validate the lowered ranging-regime\n// BUY threshold against historical data. Grid is now 8 profiles × 5 × 5 = 200.\nexport const BUY_THRESHOLDS = [0.2, 0.25, 0.3, 0.4, 0.5];\nexport const SELL_THRESHOLDS = [-0.2, -0.25, -0.3, -0.4, -0.5];\n\nexport const DEFAULT_CALIBRATION_TOKENS = [\n \"bitcoin\", \"ethereum\", \"solana\", \"aave\", \"uniswap\", \"chainlink\", \"arbitrum\",\n];\n\n// ── Types ──\n\nexport interface CalibrationConfig {\n profileName: string;\n weights: ScoringWeights;\n buyThreshold: number;\n sellThreshold: number;\n}\n\nexport interface TokenResult {\n tokenId: string;\n totalReturn: number;\n sharpeRatio: number;\n maxDrawdown: number;\n winRate: number;\n numTrades: number;\n}\n\nexport interface CalibrationResult {\n config: CalibrationConfig;\n tokenResults: TokenResult[];\n avgReturn: number;\n avgSharpe: number;\n worstDrawdown: number;\n totalTrades: number;\n}\n\nexport interface CalibratorOptions {\n tokens: string[];\n days: number;\n capital: number;\n onProgress?: (msg: string) => void;\n}\n\n// ── Build the configuration grid ──\n\nexport function buildCalibrationConfigs(): CalibrationConfig[] {\n const configs: CalibrationConfig[] = [];\n\n for (const [profileName, weights] of Object.entries(WEIGHT_PROFILES)) {\n for (const buyThreshold of BUY_THRESHOLDS) {\n for (const sellThreshold of SELL_THRESHOLDS) {\n configs.push({ profileName, weights, buyThreshold, sellThreshold });\n }\n }\n }\n\n return configs;\n}\n\n// ── Calibrator ──\n\nexport async function runCalibration(opts: CalibratorOptions): Promise<CalibrationResult[]> {\n const { tokens, days, capital, onProgress } = opts;\n const configs = buildCalibrationConfigs();\n const log = onProgress ?? (() => {});\n\n const endDate = new Date();\n endDate.setDate(endDate.getDate() - 1);\n const startDate = new Date(endDate);\n startDate.setDate(startDate.getDate() - days);\n\n const startStr = startDate.toISOString().slice(0, 10);\n const endStr = endDate.toISOString().slice(0, 10);\n\n // Map: configKey -> CalibrationResult\n const resultMap = new Map<string, CalibrationResult>();\n for (const cfg of configs) {\n const key = configKey(cfg);\n resultMap.set(key, {\n config: cfg,\n tokenResults: [],\n avgReturn: 0,\n avgSharpe: 0,\n worstDrawdown: 0,\n totalTrades: 0,\n });\n }\n\n // Process tokens SEQUENTIALLY — fetch data ONCE per token, then replay all configs\n for (let ti = 0; ti < tokens.length; ti++) {\n const tokenId = tokens[ti]!;\n log(`[${ti + 1}/${tokens.length}] Fetching data for ${tokenId}...`);\n\n // Fetch data once using a baseline backtester\n const baseBt = new Backtester({\n tokenId,\n startDate: startStr,\n endDate: endStr,\n initialCapital: capital,\n strategies: [],\n cycle: \"1d\",\n verbose: false,\n });\n\n let candles: Candle[];\n let fearAndGreedData: Record<string, number>;\n try {\n const data = await baseBt.fetchData();\n candles = data.candles;\n fearAndGreedData = data.fearAndGreedData;\n log(` Got ${candles.length} candles + ${Object.keys(fearAndGreedData).length} F&G points`);\n } catch (err) {\n log(` SKIP ${tokenId}: ${(err as Error).message}`);\n // Record zeros for all configs\n for (const cfg of configs) {\n const key = configKey(cfg);\n resultMap.get(key)!.tokenResults.push({\n tokenId, totalReturn: 0, sharpeRatio: 0, maxDrawdown: 0, winRate: 0, numTrades: 0,\n });\n }\n continue;\n }\n\n // Pre-compute strategy signals + regime ONCE per candle (this is the\n // network-bound work — most strategies hit their own APIs). Each subsequent\n // simulate() call replays only the scoring math against these cached\n // signals, turning a multi-hour calibration into seconds.\n log(` Precomputing signals for ${candles.length} candles...`);\n let precomputed: Awaited<ReturnType<Backtester['precomputeSignals']>>;\n try {\n precomputed = await baseBt.precomputeSignals(candles, fearAndGreedData);\n const valid = precomputed.filter((p) => p !== null).length;\n log(` Precomputed ${valid} candle signal sets`);\n } catch (err) {\n log(` SKIP ${tokenId}: precompute failed: ${(err as Error).message}`);\n for (const cfg of configs) {\n const key = configKey(cfg);\n resultMap.get(key)!.tokenResults.push({\n tokenId, totalReturn: 0, sharpeRatio: 0, maxDrawdown: 0, winRate: 0, numTrades: 0,\n });\n }\n continue;\n }\n\n // Now replay all configs using cached signals (zero API calls!)\n log(` Running ${configs.length} configs on ${tokenId}...`);\n for (let ci = 0; ci < configs.length; ci++) {\n const cfg = configs[ci]!;\n const key = configKey(cfg);\n\n try {\n const bt = new Backtester({\n tokenId,\n startDate: startStr,\n endDate: endStr,\n initialCapital: capital,\n strategies: [],\n cycle: \"1d\",\n verbose: false,\n customWeights: cfg.weights,\n buyThreshold: cfg.buyThreshold,\n sellThreshold: cfg.sellThreshold,\n });\n\n // Use simulate() with precomputed signals — pure of network IO\n const result = await bt.simulate(candles, fearAndGreedData, precomputed);\n\n resultMap.get(key)!.tokenResults.push({\n tokenId,\n totalReturn: result.totalReturnPercent,\n sharpeRatio: result.sharpeRatio,\n maxDrawdown: result.maxDrawdown,\n winRate: result.winRate,\n numTrades: result.totalTrades,\n });\n } catch {\n resultMap.get(key)!.tokenResults.push({\n tokenId, totalReturn: 0, sharpeRatio: 0, maxDrawdown: 0, winRate: 0, numTrades: 0,\n });\n }\n\n if ((ci + 1) % 50 === 0) {\n log(` ${tokenId}: ${ci + 1}/${configs.length} configs done`);\n }\n }\n\n log(` ${tokenId} complete`);\n\n // Rate limit pause between tokens\n if (ti < tokens.length - 1) {\n log(` Pausing 5s for CoinGecko rate limits...`);\n await sleep(5000);\n }\n }\n\n // Aggregate results\n const results: CalibrationResult[] = [];\n for (const entry of resultMap.values()) {\n const tr = entry.tokenResults;\n if (tr.length === 0) continue;\n\n entry.avgReturn = tr.reduce((s, r) => s + r.totalReturn, 0) / tr.length;\n entry.avgSharpe = tr.reduce((s, r) => s + r.sharpeRatio, 0) / tr.length;\n entry.worstDrawdown = Math.max(...tr.map(r => r.maxDrawdown));\n entry.totalTrades = tr.reduce((s, r) => s + r.numTrades, 0);\n results.push(entry);\n }\n\n // Sort: 0-trade configs sink to the bottom (vacuously \"safe\" — sharpe=0\n // ties at the top otherwise, masking real winners). Among configs that\n // actually trade, sort by avgSharpe desc, then avgReturn desc.\n results.sort((a, b) => {\n const aZero = a.totalTrades === 0;\n const bZero = b.totalTrades === 0;\n if (aZero !== bZero) return aZero ? 1 : -1;\n if (Math.abs(b.avgSharpe - a.avgSharpe) > 0.001) return b.avgSharpe - a.avgSharpe;\n return b.avgReturn - a.avgReturn;\n });\n\n await saveResults(results);\n return results;\n}\n\n// ── Helpers ──\n\nfunction configKey(cfg: CalibrationConfig): string {\n return `${cfg.profileName}|buy=${cfg.buyThreshold}|sell=${cfg.sellThreshold}`;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\nasync function saveResults(results: CalibrationResult[]): Promise<void> {\n const dir = join(homedir(), \".sherwood\", \"agent\");\n await mkdir(dir, { recursive: true });\n const path = join(dir, \"calibration-results.json\");\n const payload = {\n timestamp: new Date().toISOString(),\n totalConfigs: results.length,\n results: results.map((r, i) => ({\n rank: i + 1,\n profile: r.config.profileName,\n buyThreshold: r.config.buyThreshold,\n sellThreshold: r.config.sellThreshold,\n weights: r.config.weights,\n avgReturn: r.avgReturn,\n avgSharpe: r.avgSharpe,\n worstDrawdown: r.worstDrawdown,\n totalTrades: r.totalTrades,\n tokenResults: r.tokenResults,\n })),\n };\n await writeFile(path, JSON.stringify(payload, null, 2), \"utf-8\");\n}\n\n// ── Format Results Table ──\n\nexport function formatCalibrationTable(results: CalibrationResult[], topN = 20): string {\n const lines: string[] = [];\n lines.push(\"\");\n lines.push(\" Calibration Results (ranked by Sharpe ratio)\");\n lines.push(\" \" + \"=\".repeat(110));\n lines.push(\n \" \" +\n \"Rank\".padEnd(6) +\n \"Profile\".padEnd(13) +\n \"Buy\".padEnd(7) +\n \"Sell\".padEnd(7) +\n \"Avg Return\".padEnd(13) +\n \"Avg Sharpe\".padEnd(13) +\n \"Worst DD\".padEnd(11) +\n \"Trades\".padEnd(9) +\n \"Tokens\"\n );\n lines.push(\" \" + \"-\".repeat(110));\n\n const top = results.slice(0, topN);\n for (let i = 0; i < top.length; i++) {\n const r = top[i]!;\n const rank = String(i + 1).padEnd(6);\n const profile = r.config.profileName.padEnd(13);\n const buy = String(r.config.buyThreshold).padEnd(7);\n const sell = String(r.config.sellThreshold).padEnd(7);\n const avgRet = (r.avgReturn.toFixed(2) + \"%\").padEnd(13);\n const avgSharpe = r.avgSharpe.toFixed(3).padEnd(13);\n const worstDD = (r.worstDrawdown.toFixed(2) + \"%\").padEnd(11);\n const trades = String(r.totalTrades).padEnd(9);\n const tokenCount = String(r.tokenResults.length);\n\n lines.push(\" \" + rank + profile + buy + sell + avgRet + avgSharpe + worstDD + trades + tokenCount);\n }\n\n lines.push(\" \" + \"-\".repeat(110));\n\n if (results.length > 0) {\n const best = results[0]!;\n lines.push(\"\");\n lines.push(\" BEST CONFIGURATION:\");\n lines.push(` Profile: ${best.config.profileName}`);\n lines.push(` Buy threshold: ${best.config.buyThreshold}`);\n lines.push(` Sell threshold: ${best.config.sellThreshold}`);\n lines.push(` Weights: smartMoney=${best.config.weights.smartMoney} technical=${best.config.weights.technical} sentiment=${best.config.weights.sentiment} onchain=${best.config.weights.onchain} fundamental=${best.config.weights.fundamental} event=${best.config.weights.event}`);\n lines.push(` Avg return: ${best.avgReturn.toFixed(2)}%`);\n lines.push(` Avg Sharpe: ${best.avgSharpe.toFixed(3)}`);\n lines.push(` Worst drawdown: ${best.worstDrawdown.toFixed(2)}%`);\n\n // Show per-token breakdown for best config\n lines.push(\"\");\n lines.push(\" Per-token breakdown (best config):\");\n for (const tr of best.tokenResults) {\n lines.push(` ${tr.tokenId.padEnd(12)} return: ${tr.totalReturn.toFixed(2).padStart(8)}% sharpe: ${tr.sharpeRatio.toFixed(3).padStart(7)} trades: ${tr.numTrades}`);\n }\n }\n\n lines.push(\"\");\n const savePath = join(homedir(), \".sherwood\", \"agent\", \"calibration-results.json\");\n lines.push(` Full results saved to ${savePath}`);\n lines.push(\"\");\n\n return lines.join(\"\\n\");\n}\n","/**\n * Replay calibrator — sweeps weight profiles × thresholds against\n * `signal-history.jsonl` (production captures) instead of recomputing\n * signals from candles.\n *\n * Why this exists: the candle-only `Backtester` cannot replay the\n * production signal stack. Strategies like `fundingRate`, `dexFlow`,\n * `hyperliquidFlow`, and Nansen-sourced `smartMoney` need live HL/DEX\n * data feeds that aren't in OHLC. `signal-history.jsonl` already\n * captured those signal values per scan — replaying through the scoring\n * math (which IS pure) gives a true-to-production calibration.\n *\n * Caveat: PnL accuracy is bounded by the scan cadence. If the scanner\n * fired every ~30min on a token, exit prices used for stop/TP detection\n * are sampled at the same interval. Intra-bar wicks are missed. Sharpe\n * computed from these results is a lower bound on what a tighter exit\n * loop would achieve.\n */\n\nimport { readFile, mkdir, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport { computeTradeDecision } from './scoring.js';\nimport type { ScoringWeights, Signal, TradeDecision } from './scoring.js';\nimport type { MarketRegime } from './regime.js';\nimport {\n WEIGHT_PROFILES,\n BUY_THRESHOLDS,\n SELL_THRESHOLDS,\n type CalibrationConfig,\n type CalibrationResult,\n type TokenResult,\n} from './calibrator.js';\n\n// ── Types ──\n\n/** A single row from signal-history.jsonl — matches SignalLogEntry. */\ninterface SignalHistoryRow {\n timestamp: string;\n tokenId: string;\n tokenSymbol: string;\n price: number;\n decision: 'STRONG_BUY' | 'BUY' | 'HOLD' | 'SELL' | 'STRONG_SELL';\n score: number;\n confidence: number;\n signals: Array<{ name: string; value: number; confidence: number }>;\n regime: string;\n btcCorrelation: number;\n weights: ScoringWeights;\n}\n\nexport interface ReplayCalibratorOptions {\n /** Path to signal-history.jsonl. Defaults to ~/.sherwood/agent/signal-history.jsonl. */\n historyPath?: string;\n /** Initial capital per token (informational — we track returns, not capital). */\n capital?: number;\n /** Filter to specific tokens; defaults to all tokens in the dataset. */\n tokens?: string[];\n /** Only replay rows captured within the last N days. Useful after a scoring\n * change to avoid contaminating calibration with stale signal values from\n * the prior code path. Default: replay all rows in the file. */\n lastDays?: number;\n onProgress?: (msg: string) => void;\n /** Whether to honor the regime field on each row when applying thresholds.\n * Defaults to true — rows record the regime that gated the original\n * decision, replaying with the same regime preserves real-world behavior. */\n useRegime?: boolean;\n}\n\n// ── Exit logic constants — match backtest.ts and executor.ts ──\n\nconst STOP_LOSS_PCT = 0.03; // 3% hard stop\nconst TAKE_PROFIT_PCT = 0.06; // 6% TP (2:1 R:R)\nconst TRAIL_PCT = 0.025; // 2.5% trailing stop\nconst TIME_STOP_HOURS = 48; // 48h stale-trade exit\n\n/** Open position state for replay. Mirrors the `position` struct in backtest.ts. */\ninterface ReplayPosition {\n side: 'long' | 'short';\n entryPrice: number;\n entryTimestamp: number;\n /** High-water mark for longs, low-water mark for shorts. */\n extremePrice: number;\n signal: string;\n}\n\n/**\n * Trade record kept inline during replay so we can compute realistic\n * Sharpe annualization from actual trade cadence rather than guessing.\n */\ninterface ReplayTrade {\n pnlPercent: number;\n durationMs: number;\n}\n\n// ── Loader ──\n\n/** Read signal-history.jsonl into typed rows. Skips malformed lines. */\nexport async function loadSignalHistory(path: string): Promise<SignalHistoryRow[]> {\n const raw = await readFile(path, 'utf-8');\n const rows: SignalHistoryRow[] = [];\n for (const line of raw.split('\\n')) {\n if (!line.trim()) continue;\n try {\n rows.push(JSON.parse(line) as SignalHistoryRow);\n } catch {\n // Skip malformed lines silently — log file may have been mid-write\n }\n }\n return rows;\n}\n\n/** Group rows by tokenId, sorted chronologically within each group. */\nfunction groupByToken(rows: SignalHistoryRow[]): Map<string, SignalHistoryRow[]> {\n const grouped = new Map<string, SignalHistoryRow[]>();\n for (const r of rows) {\n if (!grouped.has(r.tokenId)) grouped.set(r.tokenId, []);\n grouped.get(r.tokenId)!.push(r);\n }\n for (const list of grouped.values()) {\n list.sort((a, b) => Date.parse(a.timestamp) - Date.parse(b.timestamp));\n }\n return grouped;\n}\n\n// ── Per-token replay ──\n\n/**\n * Replay a single token's signal history through one config.\n * Returns {trades, returns, totalReturn, sharpe, maxDrawdown, winRate}.\n */\nfunction replayToken(\n tokenRows: SignalHistoryRow[],\n config: CalibrationConfig,\n useRegime: boolean,\n): TokenResult {\n let position: ReplayPosition | null = null;\n const returns: number[] = [];\n const trades: ReplayTrade[] = []; // hold-duration tracked for Sharpe annualization\n const equityCurve: number[] = [1.0]; // normalized; multiply by (1+ret) per realized trade\n let equity = 1.0;\n\n for (let i = 0; i < tokenRows.length; i++) {\n const row = tokenRows[i]!;\n const ts = Date.parse(row.timestamp);\n const price = row.price;\n\n // Guard: rows with missing/zero/non-finite prices appear in early\n // production captures before the price feed was wired. Skip them\n // entirely — both entry and exit math would produce NaN and poison\n // the equity curve. We do NOT close any open position here; we wait\n // for the next valid price tick.\n if (!Number.isFinite(price) || price <= 0) continue;\n\n // Build Signal[] from row — mirrors what computeTradeDecision expects.\n // Source/details aren't needed for scoring; pass empty strings.\n const signals: Signal[] = row.signals.map((s) => ({\n name: s.name,\n value: s.value,\n confidence: s.confidence,\n source: '',\n details: '',\n }));\n\n const regime = useRegime\n ? (row.regime && row.regime !== 'unknown' ? (row.regime as MarketRegime) : undefined)\n : undefined;\n\n let decision: TradeDecision = computeTradeDecision(\n signals,\n config.weights,\n undefined,\n undefined,\n regime,\n );\n\n // Override action based on calibrator's threshold sweep — same logic as backtest.ts\n const buy = config.buyThreshold;\n const sell = config.sellThreshold;\n if (buy <= sell) continue; // invalid — caller should have filtered, but be safe\n const strongBuy = buy + 0.3;\n const strongSell = sell - 0.3;\n let action: TradeDecision['action'];\n if (decision.score >= strongBuy) action = 'STRONG_BUY';\n else if (decision.score >= buy) action = 'BUY';\n else if (decision.score <= strongSell) action = 'STRONG_SELL';\n else if (decision.score <= sell) action = 'SELL';\n else action = 'HOLD';\n decision = { ...decision, action };\n\n // ── Position management — exit conditions first, then optional entry ──\n if (position) {\n const isShort = position.side === 'short';\n // Track best favorable price\n if (isShort) {\n if (price < position.extremePrice) position.extremePrice = price;\n } else {\n if (price > position.extremePrice) position.extremePrice = price;\n }\n\n const pnlPercent = isShort\n ? (position.entryPrice - price) / position.entryPrice\n : (price - position.entryPrice) / position.entryPrice;\n const holdingHours = (ts - position.entryTimestamp) / (1000 * 60 * 60);\n\n let exitReason: string | null = null;\n if (pnlPercent <= -STOP_LOSS_PCT) exitReason = 'STOP';\n else if (pnlPercent >= TAKE_PROFIT_PCT) exitReason = 'TP';\n else if (TRAIL_PCT > 0 && (isShort\n ? price >= position.extremePrice * (1 + TRAIL_PCT)\n : price <= position.extremePrice * (1 - TRAIL_PCT))) {\n exitReason = 'TRAIL';\n } else if (holdingHours > TIME_STOP_HOURS && Math.abs(pnlPercent) < 0.01) {\n exitReason = 'TIME';\n } else if (isShort\n ? (action === 'BUY' || action === 'STRONG_BUY')\n : (action === 'SELL' || action === 'STRONG_SELL')) {\n exitReason = 'FLIP';\n }\n\n if (exitReason) {\n equity *= 1 + pnlPercent;\n equityCurve.push(equity);\n returns.push(pnlPercent);\n trades.push({ pnlPercent, durationMs: ts - position.entryTimestamp });\n position = null;\n }\n }\n\n // Entries only when flat\n if (!position) {\n if (action === 'BUY' || action === 'STRONG_BUY') {\n position = { side: 'long', entryPrice: price, entryTimestamp: ts, extremePrice: price, signal: action };\n } else if (action === 'SELL' || action === 'STRONG_SELL') {\n position = { side: 'short', entryPrice: price, entryTimestamp: ts, extremePrice: price, signal: action };\n }\n }\n }\n\n // Close any open position at the last VALID observed price.\n // Walking back ensures we don't close on a zero-price row.\n if (position) {\n let lastValidPrice: number | null = null;\n for (let i = tokenRows.length - 1; i >= 0; i--) {\n const p = tokenRows[i]!.price;\n if (Number.isFinite(p) && p > 0) { lastValidPrice = p; break; }\n }\n if (lastValidPrice !== null) {\n const pnlPercent = position.side === 'short'\n ? (position.entryPrice - lastValidPrice) / position.entryPrice\n : (lastValidPrice - position.entryPrice) / position.entryPrice;\n const lastTs = Date.parse(tokenRows[tokenRows.length - 1]!.timestamp);\n equity *= 1 + pnlPercent;\n equityCurve.push(equity);\n returns.push(pnlPercent);\n trades.push({ pnlPercent, durationMs: lastTs - position.entryTimestamp });\n }\n }\n\n // Metrics\n const totalReturn = equity - 1.0;\n const winRate = returns.length > 0 ? returns.filter((r) => r > 0).length / returns.length : 0;\n\n // Sharpe from per-trade returns, annualized using ACTUAL average hold\n // duration (not a fixed 52/year guess). Same formula as PortfolioTracker.\n let sharpeRatio = 0;\n if (returns.length > 1) {\n const mean = returns.reduce((a, b) => a + b, 0) / returns.length;\n const variance = returns.reduce((s, r) => s + (r - mean) ** 2, 0) / (returns.length - 1);\n const stdDev = Math.sqrt(variance);\n if (stdDev > 0) {\n const avgHoldDays = trades.length > 0\n ? trades.reduce((s, t) => s + t.durationMs, 0) / trades.length / (1000 * 60 * 60 * 24)\n : 1;\n const tradesPerYear = Math.max(252 / Math.max(avgHoldDays, 1), 1);\n sharpeRatio = (mean / stdDev) * Math.sqrt(tradesPerYear);\n }\n }\n\n // Max drawdown from equity curve\n let peak = equityCurve[0]!;\n let maxDrawdown = 0;\n for (const v of equityCurve) {\n if (v > peak) peak = v;\n const dd = (peak - v) / peak;\n if (dd > maxDrawdown) maxDrawdown = dd;\n }\n\n return {\n tokenId: tokenRows[0]?.tokenId ?? '',\n totalReturn,\n sharpeRatio,\n maxDrawdown,\n winRate,\n numTrades: returns.length,\n };\n}\n\n// ── Public driver ──\n\n/**\n * Sweep all WEIGHT_PROFILES × BUY_THRESHOLDS × SELL_THRESHOLDS against\n * a captured signal-history.jsonl. Returns the same CalibrationResult[]\n * shape as the candle-based calibrator so `formatCalibrationTable` works.\n */\nexport async function runHistoryReplay(\n options: ReplayCalibratorOptions = {},\n): Promise<CalibrationResult[]> {\n const historyPath = options.historyPath ?? join(homedir(), '.sherwood', 'agent', 'signal-history.jsonl');\n const log = options.onProgress ?? (() => {});\n const useRegime = options.useRegime ?? true;\n\n log(`Loading ${historyPath}...`);\n let allRows = await loadSignalHistory(historyPath);\n log(`Loaded ${allRows.length} rows`);\n\n if (options.lastDays !== undefined && options.lastDays > 0) {\n const cutoff = Date.now() - options.lastDays * 24 * 60 * 60 * 1000;\n const before = allRows.length;\n allRows = allRows.filter((r) => Date.parse(r.timestamp) >= cutoff);\n log(`Filtered to last ${options.lastDays}d: ${allRows.length} rows (dropped ${before - allRows.length})`);\n }\n\n const grouped = groupByToken(allRows);\n let tokens = options.tokens ?? Array.from(grouped.keys());\n // Drop tokens with too few observations to produce meaningful trades\n const MIN_ROWS = 5;\n tokens = tokens.filter((t) => (grouped.get(t)?.length ?? 0) >= MIN_ROWS);\n log(`Replaying ${tokens.length} tokens (>=${MIN_ROWS} observations each)`);\n\n const configs: CalibrationConfig[] = [];\n for (const [profileName, weights] of Object.entries(WEIGHT_PROFILES)) {\n for (const buyThreshold of BUY_THRESHOLDS) {\n for (const sellThreshold of SELL_THRESHOLDS) {\n if (buyThreshold <= sellThreshold) continue; // skip invalid\n configs.push({ profileName, weights, buyThreshold, sellThreshold });\n }\n }\n }\n log(`Sweeping ${configs.length} configs across ${tokens.length} tokens`);\n\n const results: CalibrationResult[] = configs.map((config) => ({\n config,\n tokenResults: [],\n avgReturn: 0,\n avgSharpe: 0,\n worstDrawdown: 0,\n totalTrades: 0,\n }));\n\n // Replay loop — pure of network IO, runs in seconds even for 200 × 15.\n let processed = 0;\n for (const token of tokens) {\n const rows = grouped.get(token)!;\n for (let ci = 0; ci < configs.length; ci++) {\n const cfg = configs[ci]!;\n const tokenResult = replayToken(rows, cfg, useRegime);\n results[ci]!.tokenResults.push(tokenResult);\n }\n processed++;\n log(` ${token}: ${rows.length} rows, ${configs.length} configs done (${processed}/${tokens.length})`);\n }\n\n // Aggregate\n for (const r of results) {\n const tr = r.tokenResults;\n if (tr.length === 0) continue;\n r.avgReturn = tr.reduce((s, x) => s + x.totalReturn, 0) / tr.length;\n r.avgSharpe = tr.reduce((s, x) => s + x.sharpeRatio, 0) / tr.length;\n r.worstDrawdown = Math.max(...tr.map((x) => x.maxDrawdown));\n r.totalTrades = tr.reduce((s, x) => s + x.numTrades, 0);\n }\n\n // Sort: 0-trade configs sink to bottom (same logic as candle calibrator).\n results.sort((a, b) => {\n const aZero = a.totalTrades === 0;\n const bZero = b.totalTrades === 0;\n if (aZero !== bZero) return aZero ? 1 : -1;\n if (Math.abs(b.avgSharpe - a.avgSharpe) > 0.001) return b.avgSharpe - a.avgSharpe;\n return b.avgReturn - a.avgReturn;\n });\n\n await saveResults(results);\n return results;\n}\n\nasync function saveResults(results: CalibrationResult[]): Promise<void> {\n const dir = join(homedir(), '.sherwood', 'agent');\n await mkdir(dir, { recursive: true });\n const path = join(dir, 'replay-calibration-results.json');\n const payload = {\n timestamp: new Date().toISOString(),\n totalConfigs: results.length,\n source: 'signal-history.jsonl replay',\n results: results.map((r, i) => ({\n rank: i + 1,\n profile: r.config.profileName,\n buyThreshold: r.config.buyThreshold,\n sellThreshold: r.config.sellThreshold,\n weights: r.config.weights,\n avgReturn: r.avgReturn,\n avgSharpe: r.avgSharpe,\n worstDrawdown: r.worstDrawdown,\n totalTrades: r.totalTrades,\n tokenResults: r.tokenResults,\n })),\n };\n await writeFile(path, JSON.stringify(payload, null, 2), 'utf-8');\n}\n","/**\n * Telegram-optimized alert formatting for trading notifications.\n * Formats scan results, regime changes, and alerts with emoji indicators.\n */\n\nimport type { Alert, TokenAnalysis } from \"./index.js\";\nimport type { MarketRegime } from \"./regime.js\";\n\nexport class AlertFormatter {\n\n /**\n * Format alerts for Telegram with priority grouping and emoji indicators.\n * Returns null if no alerts to display.\n */\n static formatForTelegram(alerts: Alert[]): string | null {\n if (alerts.length === 0) return null;\n\n // Group by priority\n const grouped: Record<string, Alert[]> = {\n critical: [],\n high: [],\n medium: [],\n low: []\n };\n\n for (const alert of alerts) {\n grouped[alert.priority].push(alert);\n }\n\n const lines: string[] = [];\n lines.push(\"🚨 *TRADING ALERTS*\");\n\n // Critical alerts\n if (grouped.critical.length > 0) {\n lines.push(\"\\n🔴 *CRITICAL*\");\n for (const alert of grouped.critical.slice(0, 3)) {\n lines.push(`• ${alert.title}`);\n if (alert.tokenId !== 'bitcoin') {\n lines.push(` _${alert.tokenId.toUpperCase()}_`);\n }\n }\n }\n\n // High priority alerts\n if (grouped.high.length > 0) {\n lines.push(\"\\n🟡 *HIGH*\");\n for (const alert of grouped.high.slice(0, 3)) {\n lines.push(`• ${alert.title}`);\n if (alert.tokenId !== 'bitcoin') {\n lines.push(` _${alert.tokenId.toUpperCase()}_`);\n }\n }\n }\n\n // Medium priority alerts\n if (grouped.medium.length > 0) {\n lines.push(\"\\n🔵 *MEDIUM*\");\n for (const alert of grouped.medium.slice(0, 2)) {\n lines.push(`• ${alert.title}`);\n }\n }\n\n // Low priority alerts (only show count)\n if (grouped.low.length > 0) {\n lines.push(`\\n⚫ ${grouped.low.length} low priority alerts`);\n }\n\n const result = lines.join(\"\\n\");\n\n // Telegram message limit is 4096 chars\n if (result.length > 4000) {\n return result.substring(0, 3900) + \"\\n\\n_...truncated_\";\n }\n\n return result;\n }\n\n /**\n * Format comprehensive scan summary with opportunities, top movers, and alerts.\n */\n static formatScanSummary(\n results: TokenAnalysis[],\n alerts: Alert[],\n regime?: MarketRegime\n ): string {\n const lines: string[] = [];\n\n // Header with timestamp\n lines.push(\"📊 *MARKET SCAN SUMMARY*\");\n lines.push(`_${new Date().toLocaleTimeString()}_`);\n\n // Market regime\n if (regime) {\n const regimeEmoji = regime === \"trending-up\" ? \"📈\" :\n regime === \"trending-down\" ? \"📉\" :\n regime === \"ranging\" ? \"↔️\" :\n regime === \"high-volatility\" ? \"🌋\" : \"🔄\";\n lines.push(`\\n${regimeEmoji} *Regime:* ${regime.toUpperCase().replace('-', ' ')}`);\n }\n\n // Check if all signals are HOLD (brief mode)\n const allHold = results.every(r => r.decision.action === \"HOLD\");\n\n if (allHold) {\n // Brief format for all HOLD scenario\n const strongest = results.reduce((max, r) =>\n r.decision.score > max.decision.score ? r : max\n );\n const weakest = results.reduce((min, r) =>\n r.decision.score < min.decision.score ? r : min\n );\n\n const fearGreed = results[0]?.data.fearAndGreed;\n const fgText = fearGreed ?\n (fearGreed < 25 ? \"Extreme Fear\" :\n fearGreed < 40 ? \"Fear\" :\n fearGreed < 60 ? \"Neutral\" :\n fearGreed < 75 ? \"Greed\" : \"Extreme Greed\") : \"Unknown\";\n\n lines.push(`\\n🔄 *All positions HOLD*`);\n lines.push(`💪 Strongest: ${strongest.token.toUpperCase()} (+${strongest.decision.score.toFixed(2)})`);\n lines.push(`💤 Weakest: ${weakest.token.toUpperCase()} (${weakest.decision.score.toFixed(2)})`);\n if (fearGreed) {\n lines.push(`😱 F&G: ${fearGreed} (${fgText})`);\n }\n } else {\n // Full format with opportunities and movers\n\n // Opportunities (score > 0.3)\n const opportunities = results.filter(r =>\n r.decision.score > 0.3 && (r.decision.action === \"BUY\" || r.decision.action === \"STRONG_BUY\")\n ).sort((a, b) => b.decision.score - a.decision.score);\n\n if (opportunities.length > 0) {\n lines.push(\"\\n💡 *OPPORTUNITIES*\");\n for (const opp of opportunities.slice(0, 3)) {\n const action = opp.decision.action === \"STRONG_BUY\" ? \"🚀 STRONG BUY\" : \"📈 BUY\";\n lines.push(`${action} ${opp.token.toUpperCase()} (${opp.decision.score.toFixed(2)})`);\n\n // Top signal\n const topSignal = opp.decision.signals\n .filter(s => Math.abs(s.value) > 0.1)\n .sort((a, b) => Math.abs(b.value) - Math.abs(a.value))[0];\n\n if (topSignal) {\n const signalEmoji = topSignal.value > 0 ? \"⬆️\" : \"⬇️\";\n lines.push(` ${signalEmoji} ${topSignal.source}: ${topSignal.value > 0 ? \"+\" : \"\"}${topSignal.value.toFixed(2)}`);\n }\n }\n }\n\n // Top movers (biggest absolute score changes)\n const topMovers = results\n .filter(r => Math.abs(r.decision.score) > 0.2)\n .sort((a, b) => Math.abs(b.decision.score) - Math.abs(a.decision.score))\n .slice(0, 3);\n\n if (topMovers.length > 0) {\n lines.push(\"\\n🎯 *TOP MOVERS*\");\n for (const mover of topMovers) {\n const scoreEmoji = mover.decision.score > 0 ? \"📈\" : \"📉\";\n lines.push(`${scoreEmoji} ${mover.token.toUpperCase()}: ${mover.decision.score > 0 ? \"+\" : \"\"}${mover.decision.score.toFixed(2)}`);\n }\n }\n }\n\n // Alerts section\n const alertText = this.formatForTelegram(alerts);\n if (alertText) {\n lines.push(`\\n${alertText}`);\n }\n\n const result = lines.join(\"\\n\");\n\n // Respect Telegram limit\n if (result.length > 4000) {\n return result.substring(0, 3900) + \"\\n\\n_...truncated_\";\n }\n\n return result;\n }\n}","/**\n * Execution pipeline - bridges scanner results to actionable trade proposals.\n * Converts high-confidence analysis into sized position recommendations.\n */\n\nimport type { TokenAnalysis } from \"./index.js\";\nimport type { TechnicalSignals } from \"./technical.js\";\nimport type { Candle } from \"./technical.js\";\nimport { calculateATR } from \"./technical.js\";\n\nexport type TradeAction = \"LONG\" | \"SHORT\";\n\nexport interface TradeProposal {\n tokenId: string;\n symbol: string;\n action: TradeAction;\n entryPrice: number;\n stopLoss: number;\n takeProfit: number;\n positionSizeUsd: number;\n leverage: number;\n confidence: number;\n signals: string[]; // Top 3 signal names\n regime: string;\n timestamp: number;\n}\n\n/**\n * Score-weighted position sizing config.\n *\n * When enabled, risk percentage scales linearly with score magnitude\n * across the [floor, ceiling] band. Score at the floor → minRiskPct.\n * Score at or above the ceiling → maxRiskPct.\n */\nexport interface ScaledSizingConfig {\n enabled: boolean;\n floor: number;\n ceiling: number;\n minRiskPct: number;\n maxRiskPct: number;\n}\n\nexport const DEFAULT_SCALED_SIZING: ScaledSizingConfig = {\n enabled: false,\n floor: 0.30,\n ceiling: 0.60,\n minRiskPct: 0.005,\n maxRiskPct: 0.02,\n};\n\nexport interface ProposalOptions {\n portfolioValueUsd?: number;\n scaledSizing?: ScaledSizingConfig;\n}\n\nexport class ExecutionPipeline {\n /**\n * Generate trade proposals from analysis results.\n * Only creates proposals for high-confidence opportunities — uses the\n * regime-conditional BUY/SELL threshold from the decision (defaults to\n * ±0.4 if the decision lacks regime context).\n *\n * Optional ProposalOptions.scaledSizing enables score-weighted sizing.\n * Previously this was a mutable static field — fixed per code review\n * to eliminate cross-invocation leakage in long-lived processes.\n */\n static generateProposals(\n analyses: TokenAnalysis[],\n portfolioValueOrOpts: number | ProposalOptions = 10000,\n optsArg?: ProposalOptions,\n ): TradeProposal[] {\n // Backward-compatible call shape: either generateProposals(analyses, 10000)\n // or generateProposals(analyses, { portfolioValueUsd, scaledSizing }).\n const opts: ProposalOptions =\n typeof portfolioValueOrOpts === 'number'\n ? { portfolioValueUsd: portfolioValueOrOpts, ...(optsArg ?? {}) }\n : (portfolioValueOrOpts ?? {});\n const portfolioValueUsd = opts.portfolioValueUsd ?? 10000;\n const scaledSizing = opts.scaledSizing ?? DEFAULT_SCALED_SIZING;\n const proposals: TradeProposal[] = [];\n\n for (const analysis of analyses) {\n const { decision, token, data } = analysis;\n\n // Skip HOLD signals\n if (decision.action === \"HOLD\") {\n continue;\n }\n\n // Filter: score must clear the regime-conditional action threshold\n // and confidence must exceed 50%. Falls back to ±0.4 for backward compat.\n // When scaledSizing is on, the floor drops to scaledSizing.floor — the\n // point is to take sub-threshold trades at proportionally smaller size,\n // not to filter them out before sizing kicks in.\n const baseBuyFloor = decision.thresholds?.buy ?? 0.4;\n const baseSellFloor = decision.thresholds?.sell ?? -0.4;\n const buyFloor = scaledSizing.enabled\n ? Math.min(baseBuyFloor, scaledSizing.floor)\n : baseBuyFloor;\n const sellFloor = scaledSizing.enabled\n ? Math.max(baseSellFloor, -scaledSizing.floor)\n : baseSellFloor;\n const clearsBuy = decision.score >= buyFloor;\n const clearsSell = decision.score <= sellFloor;\n if (!clearsBuy && !clearsSell) {\n continue;\n }\n if (decision.confidence <= 0.5) {\n continue;\n }\n\n // Determine trade direction\n const isLong = decision.score > 0;\n const action: TradeAction = isLong ? \"LONG\" : \"SHORT\";\n\n // Get current price (use entry price from technical data or mock)\n const entryPrice = this.getEntryPrice(data.technicalSignals);\n if (!entryPrice) continue;\n\n // Calculate ATR for stop loss\n const atr = this.getATR(data.technicalSignals);\n if (!atr) continue;\n\n // Calculate stop loss (entry +/- 2*ATR)\n const stopLoss = isLong\n ? entryPrice - (2 * atr)\n : entryPrice + (2 * atr);\n\n // Calculate take profit (2.5:1 R:R)\n const riskAmount = Math.abs(entryPrice - stopLoss);\n const takeProfit = isLong\n ? entryPrice + (2.5 * riskAmount)\n : entryPrice - (2.5 * riskAmount);\n\n // Position sizing — fixed 2% by default, score-scaled if enabled.\n // Score-scaled: bigger conviction → bigger size, smaller conviction → smaller size.\n // The point: take more sub-0.4 trades but with proportionally smaller exposure.\n let riskPercentage: number;\n if (scaledSizing.enabled) {\n const cfg = scaledSizing;\n const absScore = Math.abs(decision.score);\n const t = Math.min(1, Math.max(0, (absScore - cfg.floor) / (cfg.ceiling - cfg.floor)));\n riskPercentage = cfg.minRiskPct + t * (cfg.maxRiskPct - cfg.minRiskPct);\n } else {\n riskPercentage = 0.02;\n }\n const positionSizeUsd = (portfolioValueUsd * riskPercentage) / (Math.abs(entryPrice - stopLoss) / entryPrice);\n\n // Calculate leverage (size / max 10% portfolio allocation, capped at 5x)\n const maxAllocation = portfolioValueUsd * 0.10;\n const leverage = Math.min(5.0, positionSizeUsd / maxAllocation);\n\n // Get top 3 signals\n const topSignals = decision.signals\n .filter(s => Math.abs(s.value) > 0.05)\n .sort((a, b) => Math.abs(b.value) - Math.abs(a.value))\n .slice(0, 3)\n .map(s => s.source);\n\n // Get regime\n const regime = analysis.regime?.regime || \"unknown\";\n\n // Resolve token symbol (fallback to tokenId if not available)\n const symbol = this.resolveSymbol(token);\n\n const proposal: TradeProposal = {\n tokenId: token,\n symbol,\n action,\n entryPrice,\n stopLoss,\n takeProfit,\n positionSizeUsd,\n leverage,\n confidence: decision.confidence,\n signals: topSignals,\n regime,\n timestamp: Date.now(),\n };\n\n proposals.push(proposal);\n }\n\n return proposals.sort((a, b) => b.confidence - a.confidence);\n }\n\n /**\n * Format trade proposals for human review.\n */\n static formatProposals(proposals: TradeProposal[]): string {\n if (proposals.length === 0) {\n return \"📝 *TRADE PROPOSALS*\\n\\n_No actionable opportunities found._\\n_Criteria: Score > 0.4, Confidence > 50%_\";\n }\n\n const lines: string[] = [];\n lines.push(\"📝 *TRADE PROPOSALS*\");\n lines.push(\"_⚠️ For review only - NO auto-execution_\");\n\n for (const proposal of proposals.slice(0, 5)) {\n const directionEmoji = proposal.action === \"LONG\" ? \"🟢\" : \"🔴\";\n const confidenceBar = \"█\".repeat(Math.floor(proposal.confidence * 10));\n\n lines.push(`\\n${directionEmoji} *${proposal.action} ${proposal.symbol}*`);\n lines.push(`📈 Entry: $${proposal.entryPrice.toFixed(4)}`);\n lines.push(`🛑 Stop: $${proposal.stopLoss.toFixed(4)} (${this.calculatePctChange(proposal.entryPrice, proposal.stopLoss)}%)`);\n lines.push(`🎯 Target: $${proposal.takeProfit.toFixed(4)} (${this.calculatePctChange(proposal.entryPrice, proposal.takeProfit)}%)`);\n lines.push(`💰 Size: $${proposal.positionSizeUsd.toFixed(0)} (${proposal.leverage.toFixed(1)}x leverage)`);\n lines.push(`✅ Confidence: ${(proposal.confidence * 100).toFixed(0)}% ${confidenceBar}`);\n\n // Top signals\n if (proposal.signals.length > 0) {\n lines.push(`🔍 Signals: ${proposal.signals.slice(0, 2).join(\", \")}`);\n }\n\n // Market regime\n const regimeEmoji = proposal.regime === \"trending-up\" ? \"📈\" :\n proposal.regime === \"trending-down\" ? \"📉\" :\n proposal.regime === \"ranging\" ? \"↔️\" :\n proposal.regime === \"high-volatility\" ? \"🌋\" : \"🔄\";\n lines.push(`${regimeEmoji} Regime: ${proposal.regime}`);\n }\n\n if (proposals.length > 5) {\n lines.push(`\\n_...${proposals.length - 5} more proposals available_`);\n }\n\n lines.push(`\\n_Generated: ${new Date().toLocaleTimeString()}_`);\n\n const result = lines.join(\"\\n\");\n\n // Respect character limits\n if (result.length > 4000) {\n return result.substring(0, 3900) + \"\\n\\n_...truncated_\";\n }\n\n return result;\n }\n\n /**\n * Extract entry price from technical signals or use fallback.\n */\n private static getEntryPrice(technicalSignals?: TechnicalSignals): number | null {\n if (!technicalSignals) return null;\n\n // Use VWAP as entry price if available, fallback to EMA8\n if (technicalSignals.vwap > 0) {\n return technicalSignals.vwap;\n }\n\n if (technicalSignals.ema.ema8 > 0) {\n return technicalSignals.ema.ema8;\n }\n\n return null;\n }\n\n /**\n * Extract ATR from technical signals.\n */\n private static getATR(technicalSignals?: TechnicalSignals): number | null {\n if (!technicalSignals) return null;\n return technicalSignals.atr > 0 ? technicalSignals.atr : null;\n }\n\n /**\n * Resolve token symbol from token ID.\n */\n private static resolveSymbol(tokenId: string): string {\n const symbolMap: Record<string, string> = {\n \"bitcoin\": \"BTC\",\n \"ethereum\": \"ETH\",\n \"solana\": \"SOL\",\n \"aave\": \"AAVE\",\n \"uniswap\": \"UNI\",\n \"chainlink\": \"LINK\",\n \"polygon\": \"MATIC\",\n \"avalanche-2\": \"AVAX\",\n \"cardano\": \"ADA\",\n \"polkadot\": \"DOT\",\n };\n\n return symbolMap[tokenId] || tokenId.toUpperCase().slice(0, 6);\n }\n\n /**\n * Calculate percentage change between two prices.\n */\n private static calculatePctChange(from: number, to: number): string {\n const change = ((to - from) / from) * 100;\n return change >= 0 ? `+${change.toFixed(1)}` : change.toFixed(1);\n }\n}","/**\n * Signal activity audit — reads signal-history.jsonl and reports which\n * signal categories are actually firing, so dead weights can be pruned.\n *\n * A signal that contributes ~0 on every run isn't \"neutral information\" —\n * it's noise that compresses the aggregate score. Drop it from the weight\n * vector and renormalize so real signals aren't diluted.\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { getSignalFilePath } from \"./signal-logger.js\";\nimport type { SignalLogEntry } from \"./signal-logger.js\";\n\nconst FIRE_EPSILON = 0.05; // |value| below this counts as \"silent\"\n\n/** Category each strategy signal maps to (mirrors scoring.ts SIGNAL_CATEGORY_MAP). */\nconst SIGNAL_TO_CATEGORY: Record<string, string> = {\n // Direct categories\n technical: \"technical\",\n sentiment: \"sentiment\",\n onchain: \"onchain\",\n fundamental: \"fundamental\",\n event: \"event\",\n smartMoney: \"smartMoney\",\n\n // Active strategy signals\n breakoutOnChain: \"technical\",\n multiTimeframe: \"technical\",\n crossSectionalMomentum: \"technical\",\n tradingviewSignal: \"technical\",\n btcNetworkHealth: \"technical\",\n kronosVolForecast: \"technical\",\n dexFlow: \"onchain\",\n fundingRate: \"onchain\",\n hyperliquidFlow: \"onchain\",\n narrativeVacuum: \"onchain\",\n sentimentContrarian: \"sentiment\",\n socialVolume: \"sentiment\",\n predictionMarket: \"event\",\n whaleIntent: \"smartMoney\",\n flowIntelligence: \"smartMoney\",\n};\n\nexport interface SignalStats {\n name: string;\n category: string;\n observations: number;\n fireRate: number; // fraction of observations where |value| > epsilon\n meanAbsValue: number; // average |value|\n meanConfidence: number;\n directionalBias: number; // mean(value) — negative = mostly bearish, positive = bullish\n}\n\nexport interface CategoryStats {\n category: string;\n signalNames: string[];\n observations: number; // total signal-observations (multiple signals per category possible)\n fireRate: number;\n meanAbsValue: number;\n recommendation: \"keep\" | \"drop\" | \"review\";\n}\n\nexport interface AuditResult {\n filePath: string;\n totalEntries: number;\n dateRange: { from: string; to: string } | null;\n perSignal: SignalStats[];\n perCategory: CategoryStats[];\n}\n\n/**\n * Read the signal-history JSONL file and compute per-signal + per-category\n * activity statistics.\n */\nexport async function auditSignalHistory(\n filePath: string = getSignalFilePath(),\n): Promise<AuditResult> {\n let raw: string;\n try {\n raw = await readFile(filePath, \"utf-8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n return {\n filePath,\n totalEntries: 0,\n dateRange: null,\n perSignal: [],\n perCategory: [],\n };\n }\n throw err;\n }\n\n const lines = raw.split(\"\\n\").filter((l) => l.trim().length > 0);\n const entries: SignalLogEntry[] = [];\n for (const line of lines) {\n try {\n entries.push(JSON.parse(line) as SignalLogEntry);\n } catch {\n // skip malformed lines\n }\n }\n\n if (entries.length === 0) {\n return {\n filePath,\n totalEntries: 0,\n dateRange: null,\n perSignal: [],\n perCategory: [],\n };\n }\n\n // Per-signal aggregation\n const bySignal = new Map<\n string,\n { values: number[]; confidences: number[] }\n >();\n\n for (const entry of entries) {\n for (const sig of entry.signals) {\n if (!bySignal.has(sig.name)) {\n bySignal.set(sig.name, { values: [], confidences: [] });\n }\n const bucket = bySignal.get(sig.name)!;\n bucket.values.push(sig.value);\n bucket.confidences.push(sig.confidence);\n }\n }\n\n const perSignal: SignalStats[] = [];\n for (const [name, bucket] of bySignal) {\n const obs = bucket.values.length;\n const fires = bucket.values.filter((v) => Math.abs(v) > FIRE_EPSILON).length;\n const meanAbsValue =\n bucket.values.reduce((a, v) => a + Math.abs(v), 0) / obs;\n const meanConfidence =\n bucket.confidences.reduce((a, c) => a + c, 0) / obs;\n const directionalBias = bucket.values.reduce((a, v) => a + v, 0) / obs;\n\n perSignal.push({\n name,\n category: SIGNAL_TO_CATEGORY[name] ?? \"unknown\",\n observations: obs,\n fireRate: fires / obs,\n meanAbsValue,\n meanConfidence,\n directionalBias,\n });\n }\n\n perSignal.sort((a, b) => b.fireRate - a.fireRate);\n\n // Per-category aggregation\n const byCategory = new Map<\n string,\n { signals: Set<string>; obs: number; fires: number; absSum: number }\n >();\n\n for (const sig of perSignal) {\n if (!byCategory.has(sig.category)) {\n byCategory.set(sig.category, {\n signals: new Set(),\n obs: 0,\n fires: 0,\n absSum: 0,\n });\n }\n const bucket = byCategory.get(sig.category)!;\n bucket.signals.add(sig.name);\n bucket.obs += sig.observations;\n bucket.fires += sig.fireRate * sig.observations;\n bucket.absSum += sig.meanAbsValue * sig.observations;\n }\n\n const perCategory: CategoryStats[] = [];\n for (const [category, bucket] of byCategory) {\n const fireRate = bucket.obs > 0 ? bucket.fires / bucket.obs : 0;\n const meanAbsValue = bucket.obs > 0 ? bucket.absSum / bucket.obs : 0;\n\n let recommendation: \"keep\" | \"drop\" | \"review\";\n if (fireRate >= 0.4) recommendation = \"keep\";\n else if (fireRate < 0.1) recommendation = \"drop\";\n else recommendation = \"review\";\n\n perCategory.push({\n category,\n signalNames: [...bucket.signals],\n observations: bucket.obs,\n fireRate,\n meanAbsValue,\n recommendation,\n });\n }\n\n perCategory.sort((a, b) => b.fireRate - a.fireRate);\n\n const timestamps = entries.map((e) => e.timestamp).sort();\n\n return {\n filePath,\n totalEntries: entries.length,\n dateRange: {\n from: timestamps[0]!,\n to: timestamps[timestamps.length - 1]!,\n },\n perSignal,\n perCategory,\n };\n}\n\n// ── Diff against a saved baseline ──\n\nexport interface SignalDiffEntry {\n name: string;\n category: string;\n baseline: { fireRate: number; meanAbsValue: number; observations: number };\n current: { fireRate: number; meanAbsValue: number; observations: number };\n fireRateDelta: number;\n meanAbsValueDelta: number;\n status: \"improved\" | \"regressed\" | \"stable\" | \"new\" | \"removed\";\n}\n\nexport interface AuditDiff {\n baselineDateRange: { from: string; to: string } | null;\n currentDateRange: { from: string; to: string } | null;\n perSignal: SignalDiffEntry[];\n perCategory: SignalDiffEntry[];\n}\n\nconst DELTA_EPSILON = 0.05; // fire-rate deltas under 5pp are \"stable\"\n\nfunction classifyStatus(\n baselineRate: number | null,\n currentRate: number | null,\n): SignalDiffEntry[\"status\"] {\n if (baselineRate === null) return \"new\";\n if (currentRate === null) return \"removed\";\n const delta = currentRate - baselineRate;\n if (Math.abs(delta) < DELTA_EPSILON) return \"stable\";\n return delta > 0 ? \"improved\" : \"regressed\";\n}\n\n/** Compare a current audit result against a saved baseline. */\nexport function diffAudits(baseline: AuditResult, current: AuditResult): AuditDiff {\n const baseSignals = new Map(baseline.perSignal.map((s) => [s.name, s]));\n const currSignals = new Map(current.perSignal.map((s) => [s.name, s]));\n const allSignalNames = new Set([...baseSignals.keys(), ...currSignals.keys()]);\n\n const perSignal: SignalDiffEntry[] = [];\n for (const name of allSignalNames) {\n const b = baseSignals.get(name);\n const c = currSignals.get(name);\n perSignal.push({\n name,\n category: (c ?? b)?.category ?? \"unknown\",\n baseline: {\n fireRate: b?.fireRate ?? 0,\n meanAbsValue: b?.meanAbsValue ?? 0,\n observations: b?.observations ?? 0,\n },\n current: {\n fireRate: c?.fireRate ?? 0,\n meanAbsValue: c?.meanAbsValue ?? 0,\n observations: c?.observations ?? 0,\n },\n fireRateDelta: (c?.fireRate ?? 0) - (b?.fireRate ?? 0),\n meanAbsValueDelta: (c?.meanAbsValue ?? 0) - (b?.meanAbsValue ?? 0),\n status: classifyStatus(b?.fireRate ?? null, c?.fireRate ?? null),\n });\n }\n perSignal.sort((a, b) => Math.abs(b.fireRateDelta) - Math.abs(a.fireRateDelta));\n\n const baseCats = new Map(baseline.perCategory.map((c) => [c.category, c]));\n const currCats = new Map(current.perCategory.map((c) => [c.category, c]));\n const allCats = new Set([...baseCats.keys(), ...currCats.keys()]);\n\n const perCategory: SignalDiffEntry[] = [];\n for (const cat of allCats) {\n const b = baseCats.get(cat);\n const c = currCats.get(cat);\n perCategory.push({\n name: cat,\n category: cat,\n baseline: {\n fireRate: b?.fireRate ?? 0,\n meanAbsValue: b?.meanAbsValue ?? 0,\n observations: b?.observations ?? 0,\n },\n current: {\n fireRate: c?.fireRate ?? 0,\n meanAbsValue: c?.meanAbsValue ?? 0,\n observations: c?.observations ?? 0,\n },\n fireRateDelta: (c?.fireRate ?? 0) - (b?.fireRate ?? 0),\n meanAbsValueDelta: (c?.meanAbsValue ?? 0) - (b?.meanAbsValue ?? 0),\n status: classifyStatus(b?.fireRate ?? null, c?.fireRate ?? null),\n });\n }\n perCategory.sort((a, b) => Math.abs(b.fireRateDelta) - Math.abs(a.fireRateDelta));\n\n return {\n baselineDateRange: baseline.dateRange,\n currentDateRange: current.dateRange,\n perSignal,\n perCategory,\n };\n}\n\n/** Renormalize weights after dropping categories with fireRate below threshold. */\nexport function suggestRenormalizedWeights(\n currentWeights: Record<string, number>,\n perCategory: CategoryStats[],\n dropThreshold: number = 0.1,\n): Record<string, number> {\n const keepCategories = new Set(\n perCategory\n .filter((c) => c.fireRate >= dropThreshold && c.category !== \"unknown\")\n .map((c) => c.category),\n );\n\n // Sum retained weight\n let retainedSum = 0;\n for (const [cat, w] of Object.entries(currentWeights)) {\n if (keepCategories.has(cat)) retainedSum += w;\n }\n\n if (retainedSum === 0) return currentWeights; // nothing to renormalize\n\n const out: Record<string, number> = {};\n for (const [cat, w] of Object.entries(currentWeights)) {\n out[cat] = keepCategories.has(cat) ? w / retainedSum : 0;\n }\n return out;\n}\n"],"mappings":"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","names":["chalk","readFile","writeFile","mkdir","join","homedir","chalk","cacheKey","mkdir","readFile","writeFile","join","homedir","BASE_URL","sharedLastCallTime","cacheKey","mkdir","readFile","writeFile","join","homedir","join","cache","cacheKey","cache","join","CACHE_TTL","CACHE_TTL","execFile","join","existsSync","join","existsSync","CACHE_TTL","cache","cacheKey","execFile","CACHE_TTL","mkdir","join","homedir","createHash","readFile","writeFile","mkdir","join","homedir","join","homedir","createHash","readFile","mkdir","writeFile","mkdir","readFile","writeFile","dirname","cache","rename","cutoff","readFile","writeFile","mkdir","join","homedir","join","homedir","readFile","mkdir","cache","writeFile","readFile","writeFile","mkdir","join","homedir","join","homedir","readFile","mkdir","cache","writeFile","readFile","writeFile","mkdir","join","homedir","createHash","chalk","marketData","STOP_COOLDOWN_MS","mkdir","readFile","writeFile","join","homedir","join","homedir","readFile","mkdir","writeFile","chalk","mkdir","appendFile","join","homedir","chalk","chalk","mkdir","readFile","writeFile","rename","dirname","join","homedir","chalk","chalk","MARGIN_FRACTION","newTrailingStop","chalk","chalk","mkdir","readFile","writeFile","rename","join","homedir","join","homedir","mkdir","chalk","appendFile","chalk","chalk","STOP_LOSS_PCT","TAKE_PROFIT_PCT","TIME_STOP_HOURS","TRAIL_PCT","mkdir","writeFile","join","homedir","join","homedir","mkdir","writeFile","readFile","mkdir","writeFile","join","homedir","readFile","join","homedir","saveResults","mkdir","writeFile","fearGreed","readFile","readFile","spinner","alerts","chalk","join","homedir","readFile","mkdir","writeFile","tokens"]}