@sherwoodagent/cli 0.41.5 → 0.47.1

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 (69) hide show
  1. package/dist/{addresses-RQ7DTIBS.js → addresses-DOWLGXGE.js} +4 -4
  2. package/dist/{agent-SXEV65ME.js → agent-4HKHLQEJ.js} +3035 -2029
  3. package/dist/agent-4HKHLQEJ.js.map +1 -0
  4. package/dist/autoresearch-IA7HGSNW.js +267 -0
  5. package/dist/autoresearch-IA7HGSNW.js.map +1 -0
  6. package/dist/{chat-D5OF4OTL.js → chat-MRTPUZ3X.js} +8 -8
  7. package/dist/{chunk-2YOXRATG.js → chunk-2LRC7I33.js} +12 -2
  8. package/dist/{chunk-2YOXRATG.js.map → chunk-2LRC7I33.js.map} +1 -1
  9. package/dist/{chunk-4EHI4SKM.js → chunk-2W4KGGPU.js} +11 -1
  10. package/dist/chunk-2W4KGGPU.js.map +1 -0
  11. package/dist/chunk-7HJXZ4NM.js +189 -0
  12. package/dist/chunk-7HJXZ4NM.js.map +1 -0
  13. package/dist/{chunk-DG2K6CPY.js → chunk-E6LH2FJ6.js} +110 -68
  14. package/dist/chunk-E6LH2FJ6.js.map +1 -0
  15. package/dist/{chunk-MT3VS3GZ.js → chunk-FWC57PT7.js} +2 -2
  16. package/dist/{chunk-YRCJ3NUX.js → chunk-IS2CLLUT.js} +4 -4
  17. package/dist/{chunk-YYB4TF6D.js → chunk-KJ35EF7N.js} +4 -4
  18. package/dist/{chunk-D2UYGUV2.js → chunk-OHQOTSFG.js} +39 -3
  19. package/dist/chunk-OHQOTSFG.js.map +1 -0
  20. package/dist/chunk-R2P4C2I7.js +560 -0
  21. package/dist/chunk-R2P4C2I7.js.map +1 -0
  22. package/dist/{chunk-4QNPS5LI.js → chunk-R7X33D2S.js} +25 -6
  23. package/dist/{chunk-4QNPS5LI.js.map → chunk-R7X33D2S.js.map} +1 -1
  24. package/dist/{chunk-XZHIZVJ3.js → chunk-WMZEOR5W.js} +6 -127
  25. package/dist/chunk-WMZEOR5W.js.map +1 -0
  26. package/dist/{chunk-M2NJPATE.js → chunk-XDBRKW7I.js} +3 -3
  27. package/dist/chunk-YTYXBX7B.js +495 -0
  28. package/dist/chunk-YTYXBX7B.js.map +1 -0
  29. package/dist/{client-VSBQM2LU.js → client-UHHCB6EZ.js} +4 -4
  30. package/dist/{config-YRERDZSL.js → config-REASKDIK.js} +6 -2
  31. package/dist/{eas-QNZKCEP7.js → eas-PEEWGSAO.js} +16 -6
  32. package/dist/{governor-RE6IJMUJ.js → governor-MG5VVSU4.js} +6 -6
  33. package/dist/index.js +1137 -272
  34. package/dist/index.js.map +1 -1
  35. package/dist/{network-R5NFPIAF.js → network-3ZU7UBDU.js} +7 -5
  36. package/dist/{price-GY3JQ2OK.js → price-WCCXWPDM.js} +5 -5
  37. package/dist/{research-SVWNPHIN.js → research-QBKISW2M.js} +9 -9
  38. package/dist/research-YJWSQS2A.js +14 -0
  39. package/dist/serve-6MYBOAX6.js +222 -0
  40. package/dist/serve-6MYBOAX6.js.map +1 -0
  41. package/dist/{session-3Z6I2IM3.js → session-FPCPCVQX.js} +10 -10
  42. package/dist/summary-formatter-JIGWN4QS.js +180 -0
  43. package/dist/summary-formatter-JIGWN4QS.js.map +1 -0
  44. package/dist/{trade-E5HSZE44.js → trade-NO6CNWCA.js} +19 -17
  45. package/dist/{trade-E5HSZE44.js.map → trade-NO6CNWCA.js.map} +1 -1
  46. package/dist/{xmtp-VPGQE4IP.js → xmtp-ACB2W573.js} +6 -6
  47. package/package.json +5 -2
  48. package/dist/agent-SXEV65ME.js.map +0 -1
  49. package/dist/chunk-4EHI4SKM.js.map +0 -1
  50. package/dist/chunk-D2UYGUV2.js.map +0 -1
  51. package/dist/chunk-DG2K6CPY.js.map +0 -1
  52. package/dist/chunk-XZHIZVJ3.js.map +0 -1
  53. package/dist/research-PB22GPXC.js +0 -14
  54. /package/dist/{addresses-RQ7DTIBS.js.map → addresses-DOWLGXGE.js.map} +0 -0
  55. /package/dist/{chat-D5OF4OTL.js.map → chat-MRTPUZ3X.js.map} +0 -0
  56. /package/dist/{chunk-MT3VS3GZ.js.map → chunk-FWC57PT7.js.map} +0 -0
  57. /package/dist/{chunk-YRCJ3NUX.js.map → chunk-IS2CLLUT.js.map} +0 -0
  58. /package/dist/{chunk-YYB4TF6D.js.map → chunk-KJ35EF7N.js.map} +0 -0
  59. /package/dist/{chunk-M2NJPATE.js.map → chunk-XDBRKW7I.js.map} +0 -0
  60. /package/dist/{client-VSBQM2LU.js.map → client-UHHCB6EZ.js.map} +0 -0
  61. /package/dist/{config-YRERDZSL.js.map → config-REASKDIK.js.map} +0 -0
  62. /package/dist/{eas-QNZKCEP7.js.map → eas-PEEWGSAO.js.map} +0 -0
  63. /package/dist/{governor-RE6IJMUJ.js.map → governor-MG5VVSU4.js.map} +0 -0
  64. /package/dist/{network-R5NFPIAF.js.map → network-3ZU7UBDU.js.map} +0 -0
  65. /package/dist/{price-GY3JQ2OK.js.map → price-WCCXWPDM.js.map} +0 -0
  66. /package/dist/{research-SVWNPHIN.js.map → research-QBKISW2M.js.map} +0 -0
  67. /package/dist/{research-PB22GPXC.js.map → research-YJWSQS2A.js.map} +0 -0
  68. /package/dist/{session-3Z6I2IM3.js.map → session-FPCPCVQX.js.map} +0 -0
  69. /package/dist/{xmtp-VPGQE4IP.js.map → xmtp-ACB2W573.js.map} +0 -0
@@ -1 +0,0 @@
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/technical.ts","../src/agent/utils.ts","../src/agent/scoring.ts","../src/agent/strategies/sentiment-contrarian.ts","../src/agent/strategies/breakout-onchain.ts","../src/agent/strategies/funding-rate.ts","../src/providers/data/dexscreener.ts","../src/agent/strategies/dex-flow.ts","../src/agent/strategies/hyperliquid-flow.ts","../src/agent/strategies/multi-timeframe.ts","../src/agent/strategies/index.ts","../src/providers/data/funding-rate.ts","../src/providers/data/token-unlocks.ts","../src/providers/data/twitter.ts","../src/agent/signal-logger.ts","../src/agent/signal-smoother.ts","../src/providers/data/hyperliquid.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/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: 3, // Only run x402 on top 3 tokens by free-signal score — saves ~$1.12/run\n smoothFastSignals: true, // Rolling-window smoothing for noisy signals — prevents single-scan flicker\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 .action(async (tokens: string[], options: { all?: boolean; auto?: boolean; json?: boolean; x402: boolean; telegram?: boolean; proposals?: boolean; weightProfile?: string; scaledSizing?: boolean; smooth?: boolean }) => {\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({ tokens: tokenList, useX402: options.x402, weightProfile: options.weightProfile, smoothFastSignals: options.smooth });\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 .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 }) => {\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({ tokens: tokenList, cycle, dryRun: !isLive, useX402: options.x402, weightProfile: options.weightProfile, smoothFastSignals: options.smooth }),\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 // ── 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 // ── 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 // ── 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 scoreOnChain,\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 { logSignal } from \"./signal-logger.js\";\nimport { SignalSmoother, FileSmootherStorage, DEFAULT_SMOOTHER_CONFIG } from \"./signal-smoother.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 { 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}\n\nexport interface TokenAnalysis {\n token: string;\n decision: TradeDecision;\n data: {\n technicalSignals?: TechnicalSignals;\n fearAndGreed?: number;\n tvl?: number;\n };\n regime?: RegimeAnalysis;\n correlation?: CorrelationCheck;\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\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 }): 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 (OHLC, Fear & Greed, TVL, Hyperliquid)\n const [ohlcResult, fearGreedResult, tvlResult, hyperliquidResult] = await Promise.allSettled([\n // 1. Fetch OHLC data from CoinGecko\n this.coingecko.getOHLC(tokenId, 30).then(async (ohlcRaw) => {\n if (!ohlcRaw || ohlcRaw.length <= 10) return null;\n\n const rawCandles = 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]!, // CoinGecko OHLC: [ts, o, h, l, c]\n volume: 0, // OHLC endpoint doesn't include volume\n }));\n\n // Fetch volume data in parallel\n try {\n const marketData = await this.coingecko.getMarketData(tokenId, 30);\n if (marketData?.total_volumes) {\n // Map volumes to nearest candle by timestamp\n for (const candle of rawCandles) {\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 // Volume data optional\n }\n\n return rawCandles;\n }),\n\n // 2. Fetch Fear & Greed\n this.sentiment.getFearAndGreed(),\n\n // 3. Fetch TVL data (if DeFi protocol)\n this.defillama.getProtocolTvl(tokenId).then(async (tvlValue) => {\n if (typeof tvlValue === \"number\" && tvlValue > 0) {\n const priceData = await this.coingecko.getPrice([tokenId], [\"usd\"]);\n const mcap = priceData?.[tokenId]?.usd_market_cap;\n return { tvl: tvlValue, mcapToTvl: mcap && tvlValue > 0 ? mcap / tvlValue : undefined };\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 OHLC results\n if (ohlcResult.status === 'fulfilled' && ohlcResult.value) {\n candles = ohlcResult.value;\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 — use Hyperliquid price if available instead of calling CoinGecko\n if (tvlResult.status === 'fulfilled' && tvlResult.value) {\n const { tvl: tvlValue } = tvlResult.value;\n tvl = tvlValue;\n\n // Calculate mcapToTvl ratio using Hyperliquid price if available\n let mcapToTvl: number | undefined;\n if (hyperliquidData?.markPrice) {\n // Use Hyperliquid mark price instead of CoinGecko\n try {\n const coinDetails = await this.coingecko.getCoinDetails(tokenId);\n const circSupply = coinDetails?.market_data?.circulating_supply;\n if (circSupply) {\n const mcap = hyperliquidData.markPrice * circSupply;\n mcapToTvl = tvlValue > 0 ? mcap / tvlValue : undefined;\n }\n } catch {\n // Fall back to original CoinGecko approach if coin details fail\n const priceData = await this.coingecko.getPrice([tokenId], [\"usd\"]);\n const mcap = priceData?.[tokenId]?.usd_market_cap;\n mcapToTvl = mcap && tvlValue > 0 ? mcap / tvlValue : undefined;\n }\n } else {\n // Fall back to CoinGecko price\n const priceData = await this.coingecko.getPrice([tokenId], [\"usd\"]);\n const mcap = priceData?.[tokenId]?.usd_market_cap;\n mcapToTvl = mcap && tvlValue > 0 ? mcap / tvlValue : undefined;\n }\n\n signals.push(scoreFundamental({ mcapToTvl }));\n } else {\n // Not all tokens have TVL data, that's fine\n signals.push(scoreFundamental({}));\n }\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\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 x402 research data (Messari dropped — low value for majors)\n //\n // Two Nansen calls in parallel:\n // 1. Smart-money netflows (multi-chain: ethereum, solana, base, L2s)\n // — was previously Base-only, returning empty for BTC/ETH/SOL\n // 2. Hyperliquid smart-money perp trades — same venue we trade on,\n // shows what Funds/Smart Traders are doing right now\n if (x402Enabled && x402Available) {\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 // Nansen netflow dropped — returns 422 for token_symbol filter.\n // HL perp-trades is the higher-value signal (same venue we trade).\n const [hlPerpResult] = await Promise.allSettled([\n nansenProvider.queryHyperliquidSmartMoney(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(` x402 Nansen HL perps: ${trades.length} trades, ${(longRatio * 100).toFixed(0)}% long bias, cost ${hlResult.costUsdc} USDC`));\n } else {\n console.error(chalk.dim(` x402 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 // 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 3: Parallel strategy data fetching (symbol, funding rate, unlocks)\n let marketData: any = undefined;\n\n try {\n // Twitter sentiment fetch removed — API returns 402 (paid tier required)\n // for most token queries and was spamming logs without producing usable\n // signal. TwitterSentimentStrategy is also disabled in DEFAULT_STRATEGIES.\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 };\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 if (tokenId !== \"bitcoin\" && (!candles || candles.length < 100)) {\n const btcOhlc = await this.coingecko.getOHLC(\"bitcoin\", 200);\n if (btcOhlc && btcOhlc.length > 100) {\n btcCandles = btcOhlc.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, // Volume not needed for regime detection\n }));\n }\n }\n\n if (btcCandles.length > 100) {\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 result: TokenAnalysis = {\n token: tokenId,\n decision,\n data: { technicalSignals, fearAndGreed: fearAndGreedValue, tvl },\n regime: regimeAnalysis,\n correlation: correlationCheck,\n };\n\n // Persist analysis to ~/.sherwood/agent/signal-history.jsonl so the\n // signal-audit tool can measure fire rates over time. Fire-and-forget —\n // never blocks scoring, never crashes on disk failure.\n const currentPrice = hyperliquidData?.markPrice\n ?? (candles && candles.length > 0 ? candles[candles.length - 1]!.close : 0);\n logSignal(result, currentPrice, resolvedWeights);\n\n return result;\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 /** 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 if (!shouldTwoPass) {\n // Original single-pass: analyze all tokens with whatever x402 config says\n const results: TokenAnalysis[] = [];\n for (const token of this.config.tokens) {\n const result = await this.analyzeToken(token);\n results.push(result);\n }\n return results;\n }\n\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, { skipX402: true });\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 enrichedMap.set(token, enriched);\n }\n\n // Merge: use enriched results for top N, free results for the rest\n return freeResults.map((r) => enrichedMap.get(r.token) ?? r);\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// 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 // Cache miss — make API call with rate limiting\n const job = requestQueue.then(async () => {\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 — exponential backoff: 10s, 30s, 60s\n let retryDelay = 10_000;\n for (let attempt = 0; attempt < 3; attempt++) {\n await new Promise((resolve) => setTimeout(resolve, retryDelay));\n sharedLastCallTime = Date.now();\n\n const retry = await fetch(url, { headers });\n if (retry.ok) {\n const data = await retry.json();\n await this.writeCache(cacheKey, data);\n return data;\n }\n\n retryDelay *= 3; // 10s -> 30s -> 90s (but we cap at 60s)\n if (retryDelay > 60_000) retryDelay = 60_000;\n }\n throw new Error(`CoinGecko rate limit exceeded after retries — ${url}`);\n }\n if (!res.ok) throw new Error(`CoinGecko error: ${res.status} ${res.statusText} — ${url}`);\n\n const data = await res.json();\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 * Technical analysis module — pure calculation functions.\n * All functions use standard financial formulas.\n */\n\nexport interface Candle {\n timestamp: number;\n open: number;\n high: number;\n low: number;\n close: number;\n volume: number;\n}\n\nexport interface TechnicalSignals {\n rsi: number;\n macd: { value: number; signal: number; histogram: number };\n bb: { upper: number; middle: number; lower: number; width: number; squeeze: boolean };\n ema: { ema8: number; ema21: number; ema50: number; ema200: number };\n atr: number;\n vwap: number;\n volume: { current: number; avg20: number; ratio: number };\n}\n\n// ── Simple Moving Average ──\n\nexport function calculateSMA(values: number[], period: number): number[] {\n if (values.length === 0) return [];\n\n const result: number[] = [];\n for (let i = 0; i < values.length; i++) {\n if (i < period - 1) {\n result.push(NaN);\n } else {\n let sum = 0;\n for (let j = i - period + 1; j <= i; j++) {\n sum += values[j]!;\n }\n result.push(sum / period);\n }\n }\n return result;\n}\n\n// ── Exponential Moving Average ──\n\nexport function calculateEMA(values: number[], period: number): number[] {\n if (values.length === 0) return [];\n\n const result: number[] = [];\n const k = 2 / (period + 1);\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 // Seed with SMA of first `period` valid values\n let sum = 0;\n let count = 0;\n for (let j = 0; j < period; j++) {\n if (!isNaN(values[j]!)) {\n sum += values[j]!;\n count++;\n }\n }\n if (count > 0) {\n result.push(sum / count);\n } else {\n result.push(NaN);\n }\n } else {\n const prev = result[i - 1]!;\n const current = values[i]!;\n if (!isNaN(current) && !isNaN(prev)) {\n result.push(current * k + prev * (1 - k));\n } else {\n result.push(NaN);\n }\n }\n }\n return result;\n}\n\n// ── RSI (Relative Strength Index) ──\n\nexport function calculateRSI(candles: Candle[], period: number = 14): number[] {\n if (candles.length < 2) return [];\n\n const closes = candles.map((c) => c.close);\n const result: number[] = [];\n\n // Calculate price changes\n const changes: number[] = [];\n for (let i = 0; i < closes.length; i++) {\n if (i === 0) {\n changes.push(0);\n } else {\n changes.push(closes[i]! - closes[i - 1]!);\n }\n }\n\n let avgGain = 0;\n let avgLoss = 0;\n\n for (let i = 0; i < closes.length; i++) {\n if (i < period) {\n result.push(NaN);\n continue;\n }\n\n if (i === period) {\n // Sum gains and losses for changes[1..period]\n let sumGain = 0;\n let sumLoss = 0;\n for (let j = 1; j <= period; j++) {\n const change = changes[j]!;\n sumGain += Math.max(change, 0);\n sumLoss += Math.max(-change, 0);\n }\n avgGain = sumGain / period;\n avgLoss = sumLoss / period;\n } else {\n // Use Wilder's smoothing (exponential smoothing with alpha = 1/period)\n const change = changes[i]!;\n const alpha = 1 / period;\n avgGain = (1 - alpha) * avgGain + alpha * Math.max(change, 0);\n avgLoss = (1 - alpha) * avgLoss + alpha * Math.max(-change, 0);\n }\n\n if (avgLoss === 0) {\n result.push(100);\n } else {\n const rs = avgGain / avgLoss;\n result.push(100 - 100 / (1 + rs));\n }\n }\n\n return result;\n}\n\n// ── MACD ──\n\nexport function calculateMACD(\n candles: Candle[],\n fast: number = 12,\n slow: number = 26,\n signalPeriod: number = 9,\n): { macd: number[]; signal: number[]; histogram: number[] } {\n const closes = candles.map((c) => c.close);\n const emaFast = calculateEMA(closes, fast);\n const emaSlow = calculateEMA(closes, slow);\n\n const macdLine: number[] = [];\n for (let i = 0; i < closes.length; i++) {\n if (isNaN(emaFast[i]!) || isNaN(emaSlow[i]!)) {\n macdLine.push(NaN);\n } else {\n macdLine.push(emaFast[i]! - emaSlow[i]!);\n }\n }\n\n // Signal line = EMA of MACD line\n // Calculate EMA directly on the MACD line, which will handle NaN values internally\n const signalLine = calculateEMA(macdLine, signalPeriod);\n\n const histogram: number[] = [];\n for (let i = 0; i < macdLine.length; i++) {\n if (isNaN(macdLine[i]!) || isNaN(signalLine[i]!)) {\n histogram.push(NaN);\n } else {\n histogram.push(macdLine[i]! - signalLine[i]!);\n }\n }\n\n return { macd: macdLine, signal: signalLine, histogram };\n}\n\n// ── Bollinger Bands ──\n\nexport function calculateBollingerBands(\n candles: Candle[],\n period: number = 20,\n stdDevMult: number = 2.0,\n): { upper: number[]; middle: number[]; lower: number[]; width: number[] } {\n if (candles.length === 0) {\n return { upper: [], middle: [], lower: [], width: [] };\n }\n\n const closes = candles.map((c) => c.close);\n const middle = calculateSMA(closes, period);\n\n const upper: number[] = [];\n const lower: number[] = [];\n const width: number[] = [];\n\n for (let i = 0; i < closes.length; i++) {\n if (isNaN(middle[i]!)) {\n upper.push(NaN);\n lower.push(NaN);\n width.push(NaN);\n } else {\n let sumSq = 0;\n for (let j = i - period + 1; j <= i; j++) {\n sumSq += (closes[j]! - middle[i]!) ** 2;\n }\n const sd = Math.sqrt(sumSq / period);\n upper.push(middle[i]! + stdDevMult * sd);\n lower.push(middle[i]! - stdDevMult * sd);\n width.push(middle[i]! !== 0 ? (stdDevMult * 2 * sd) / middle[i]! : 0);\n }\n }\n\n return { upper, middle, lower, width };\n}\n\n// ── Average True Range ──\n\nexport function calculateATR(candles: Candle[], period: number = 14): number[] {\n if (candles.length === 0) return [];\n\n const trueRanges: number[] = [];\n for (let i = 0; i < candles.length; i++) {\n const c = candles[i]!;\n if (i === 0) {\n trueRanges.push(c.high - c.low);\n } else {\n const prev = candles[i - 1]!;\n const tr = Math.max(c.high - c.low, Math.abs(c.high - prev.close), Math.abs(c.low - prev.close));\n trueRanges.push(tr);\n }\n }\n\n // ATR = smoothed average of true ranges (Wilder's smoothing)\n const result: number[] = [];\n for (let i = 0; i < trueRanges.length; i++) {\n if (i < period - 1) {\n result.push(NaN);\n } else if (i === period - 1) {\n let sum = 0;\n for (let j = 0; j < period; j++) sum += trueRanges[j]!;\n result.push(sum / period);\n } else {\n result.push((result[i - 1]! * (period - 1) + trueRanges[i]!) / period);\n }\n }\n return result;\n}\n\n// ── VWAP (Volume Weighted Average Price) ──\n\nexport function calculateVWAP(candles: Candle[]): number[] {\n const result: number[] = [];\n let cumulativeTPV = 0;\n let cumulativeVolume = 0;\n\n for (const c of candles) {\n const typicalPrice = (c.high + c.low + c.close) / 3;\n cumulativeTPV += typicalPrice * c.volume;\n cumulativeVolume += c.volume;\n result.push(cumulativeVolume > 0 ? cumulativeTPV / cumulativeVolume : typicalPrice);\n }\n return result;\n}\n\n// ── Bollinger Band Squeeze Detection ──\n\nexport function detectBBSqueeze(candles: Candle[]): boolean {\n const bb = calculateBollingerBands(candles, 20, 2.0);\n const validWidths = bb.width.filter((w) => !isNaN(w));\n if (validWidths.length < 20) return false;\n\n const current = validWidths[validWidths.length - 1]!;\n const recent20 = validWidths.slice(-20);\n const minWidth = Math.min(...recent20);\n return current <= minWidth * 1.01; // within 1% of 20-period low\n}\n\n// ── Get Latest Signals (all indicators at current candle) ──\n\nexport function getLatestSignals(candles: Candle[]): TechnicalSignals {\n if (candles.length < 2) {\n throw new Error(\"Need at least 2 candles for technical analysis\");\n }\n\n const last = (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 const rsiArr = calculateRSI(candles, 14);\n const macdResult = calculateMACD(candles, 12, 26, 9);\n const bbResult = calculateBollingerBands(candles, 20, 2.0);\n const closes = candles.map((c) => c.close);\n const ema8 = calculateEMA(closes, 8);\n const ema21 = calculateEMA(closes, 21);\n const ema50 = calculateEMA(closes, 50);\n const ema200 = calculateEMA(closes, 200);\n const atrArr = calculateATR(candles, 14);\n const vwapArr = calculateVWAP(candles);\n\n // Volume stats\n const volumes = candles.map((c) => c.volume);\n const currentVol = volumes[volumes.length - 1] ?? 0;\n const recent20Vol = volumes.slice(-20);\n const avg20Vol = recent20Vol.length > 0 ? recent20Vol.reduce((a, b) => a + b, 0) / recent20Vol.length : 0;\n\n return {\n rsi: last(rsiArr),\n macd: {\n value: last(macdResult.macd),\n signal: last(macdResult.signal),\n histogram: last(macdResult.histogram),\n },\n bb: {\n upper: last(bbResult.upper),\n middle: last(bbResult.middle),\n lower: last(bbResult.lower),\n width: last(bbResult.width),\n squeeze: detectBBSqueeze(candles),\n },\n ema: {\n ema8: last(ema8),\n ema21: last(ema21),\n ema50: last(ema50),\n ema200: last(ema200),\n },\n atr: last(atrArr),\n vwap: last(vwapArr),\n volume: {\n current: currentVol,\n avg20: avg20Vol,\n ratio: avg20Vol > 0 ? currentVol / avg20Vol : 0,\n },\n };\n}\n","/** Clamp a number between min and max (default [-1, 1]). */\nexport function clamp(v: number, min: number = -1, max: number = 1): number {\n return Math.min(max, Math.max(min, v));\n}\n","/**\n * Signal scoring engine — converts raw data into trade decisions.\n */\n\nimport type { TechnicalSignals } from \"./technical.js\";\nimport { clamp } from \"./utils.js\";\nimport type { CorrelationCheck } from \"./correlation.js\";\nimport type { MarketRegime } from \"./regime.js\";\n\nexport interface Signal {\n name: string;\n value: number; // -1.0 to +1.0\n confidence: number; // 0.0 to 1.0\n source: string;\n details: string;\n _weightOverride?: number;\n}\n\nexport interface ScoringWeights {\n smartMoney: number;\n technical: number;\n sentiment: number;\n onchain: number;\n fundamental: number;\n event: number;\n}\n\n// Replay calibration over 5 days × 15 tokens / 1817 production observations\n// ranked the `contrarian` profile (sentiment 0.40) #1 with Sharpe 1.354,\n// while the prior `default` (sentiment 0.20) ranked 21st with Sharpe 0.296.\n// Rebalanced to lift sentiment, cut the most-lagging signals (technical),\n// and reduce smartMoney since x402 Nansen is often unfunded in practice.\n// `onchain` slightly boosted — HL flow + fundingRate are the highest-firing\n// live categories. Sums to 1.00.\nexport const DEFAULT_WEIGHTS: ScoringWeights = {\n smartMoney: 0.15,\n technical: 0.10,\n sentiment: 0.40,\n onchain: 0.20,\n fundamental: 0.10,\n event: 0.05,\n};\n\n/**\n * Named weight profiles for `--weight-profile <name>` and per-asset auto-selection.\n *\n * - default: balanced — used when no profile selected\n * - majors: BTC/ETH/SOL — drops fundamental (no meaningful TVL on BTC),\n * drops event (token unlocks rare on majors). Mass redistributed\n * to technical + sentiment + smartMoney + onchain (the 4 that\n * actually fire on majors per signal-audit data).\n * - altcoin: smaller caps — keeps fundamental + event because TVL deltas\n * and unlocks meaningfully drive price action on altcoins.\n * - sentHeavy: contrarian setups (extreme F&G), bias toward sentiment signal.\n * - techHeavy: trend continuation setups, bias toward technical confirmation.\n */\nexport const WEIGHT_PROFILES: Record<string, ScoringWeights> = {\n default: DEFAULT_WEIGHTS,\n // Majors (BTC/ETH/SOL): no TVL/event data. 4 active categories.\n // Same rebalance rationale as DEFAULT_WEIGHTS — replay calibration showed\n // sentiment-heavy profiles dominating production-stack data; this profile\n // concentrates the freed-up weight into sentiment + onchain (the two\n // categories that produce directional opinions on majors). Technical and\n // smartMoney shrink because both are lagging or unfunded in practice.\n majors: { smartMoney: 0.15, technical: 0.10, sentiment: 0.40, onchain: 0.35, fundamental: 0.00, event: 0.00 },\n // Altcoins: keep fundamental (TVL data exists) and event. Sentiment lifted\n // here too but less aggressively — altcoins respond more to TVL/flow than\n // majors, and the sentimentContrarian extremes-only fix means sentiment\n // weight is dormant most of the time.\n altcoin: { smartMoney: 0.15, technical: 0.15, sentiment: 0.30, onchain: 0.20, fundamental: 0.10, event: 0.10 },\n // sentHeavy/techHeavy removed — unvalidated, added parameter complexity without evidence of benefit\n};\n\n/** Tokens that get the \"majors\" profile when auto-selection is enabled. */\nconst MAJORS_TOKEN_IDS = new Set([\"bitcoin\", \"ethereum\", \"solana\"]);\n\n/**\n * Pick a weight profile for a token. Returns user-specified profile if given,\n * otherwise auto-selects \"majors\" for BTC/ETH/SOL and \"default\" for everything else.\n */\nexport function profileForToken(tokenId: string, userProfile?: string): ScoringWeights {\n if (userProfile && WEIGHT_PROFILES[userProfile]) {\n return WEIGHT_PROFILES[userProfile];\n }\n if (MAJORS_TOKEN_IDS.has(tokenId.toLowerCase())) {\n return WEIGHT_PROFILES[\"majors\"]!;\n }\n return DEFAULT_WEIGHTS;\n}\n\nexport interface TradeDecision {\n action: \"STRONG_BUY\" | \"BUY\" | \"HOLD\" | \"SELL\" | \"STRONG_SELL\";\n score: number;\n signals: Signal[];\n reasoning: string;\n confidence: number;\n timestamp: number;\n /** Thresholds actually used to decide action — for audit + backtest replay. */\n thresholds?: ActionThresholds;\n}\n\n/** BUY/SELL action thresholds. All values are score cutoffs in [-1, 1]. */\nexport interface ActionThresholds {\n strongBuy: number; // score >= strongBuy → STRONG_BUY\n buy: number; // score >= buy → BUY\n sell: number; // score <= sell → SELL (negative)\n strongSell: number; // score <= strongSell → STRONG_SELL (more negative)\n}\n\n/** Default thresholds — symmetric, used when no regime info is provided. */\nexport const DEFAULT_THRESHOLDS: ActionThresholds = {\n strongBuy: 0.6,\n buy: 0.3,\n sell: -0.3,\n strongSell: -0.6,\n};\n\n/**\n * Regime-conditional thresholds.\n *\n * Trending regimes: asymmetric — easier to enter WITH the trend, harder to fade it.\n * Ranging: symmetric but tighter — whipsaws are expensive, demand more conviction.\n * High-volatility: tighter — wide moves without direction mean more false signals.\n * Low-volatility: looser — cleaner signal environment, less noise to filter.\n */\nexport const REGIME_THRESHOLDS: Record<MarketRegime, ActionThresholds> = {\n \"trending-up\": { strongBuy: 0.55, buy: 0.25, sell: -0.40, strongSell: -0.70 },\n \"trending-down\": { strongBuy: 0.70, buy: 0.40, sell: -0.25, strongSell: -0.55 },\n // Ranging BUY lowered 0.30 → 0.25 to match trending-up. Production cycles\n // showed scores capping at 0.27 for hours at a time during ranging regimes,\n // missing real entries (BTC 0.272, DOGE 0.266, AAVE 0.266 all HOLD'd).\n // Symmetric SELL lowered to -0.25 to keep the band balanced.\n \"ranging\": { strongBuy: 0.55, buy: 0.25, sell: -0.25, strongSell: -0.55 },\n \"high-volatility\":{ strongBuy: 0.70, buy: 0.45, sell: -0.45, strongSell: -0.70 },\n \"low-volatility\": { strongBuy: 0.60, buy: 0.30, sell: -0.30, strongSell: -0.60 },\n};\n\nexport function thresholdsForRegime(regime?: MarketRegime): ActionThresholds {\n if (!regime) return DEFAULT_THRESHOLDS;\n return REGIME_THRESHOLDS[regime] ?? DEFAULT_THRESHOLDS;\n}\n\n// ── Score Technical Signals ──\n\nexport function scoreTechnical(signals: TechnicalSignals): Signal {\n let value = 0;\n const details: string[] = [];\n let confidence = 0.5;\n\n // If most indicators are NaN, reduce confidence significantly\n const nanCount = [signals.rsi, signals.macd.histogram, signals.bb.width, signals.ema.ema50].filter(v => isNaN(v)).length;\n if (nanCount >= 3) {\n confidence = 0.15;\n details.push(\"Insufficient data for most indicators\");\n }\n\n // RSI scoring\n const rsi = signals.rsi;\n if (!isNaN(rsi)) {\n if (rsi < 30) {\n const rsiScore = 0.5 + 0.3 * ((30 - rsi) / 30); // 0.5 at RSI=30, 0.8 at RSI=0\n value += rsiScore;\n details.push(`RSI ${rsi.toFixed(1)} oversold (+${rsiScore.toFixed(2)})`);\n confidence += 0.1;\n } else if (rsi > 70) {\n const rsiScore = -(0.5 + 0.3 * ((rsi - 70) / 30)); // -0.5 at RSI=70, -0.8 at RSI=100\n value += rsiScore;\n details.push(`RSI ${rsi.toFixed(1)} overbought (${rsiScore.toFixed(2)})`);\n confidence += 0.1;\n } else {\n // Scale linearly: RSI 50 → 0, RSI 30 → +0.5, RSI 70 → -0.5\n const rsiScore = -((rsi - 50) / 20) * 0.5;\n value += rsiScore;\n details.push(`RSI ${rsi.toFixed(1)} (${rsiScore >= 0 ? \"+\" : \"\"}${rsiScore.toFixed(2)})`);\n }\n }\n\n // MACD histogram scoring\n const hist = signals.macd.histogram;\n if (!isNaN(hist)) {\n if (hist > 0) {\n value += 0.3;\n details.push(`MACD histogram positive (+0.30)`);\n } else if (hist < 0) {\n value -= 0.3;\n details.push(`MACD histogram negative (-0.30)`);\n }\n }\n\n // Bollinger Bands scoring\n if (signals.bb.squeeze) {\n // Squeeze = neutral, wait for breakout\n details.push(\"BB squeeze detected (neutral, awaiting breakout)\");\n } else {\n const lastClose = signals.ema.ema8; // Use ema8 as proxy for current price\n if (!isNaN(signals.bb.lower) && !isNaN(lastClose)) {\n const volSpike = signals.volume.ratio > 1.5;\n if (lastClose < signals.bb.lower && volSpike) {\n value += 0.5;\n details.push(\"Price below lower BB with volume spike (+0.50)\");\n confidence += 0.1;\n } else if (lastClose > signals.bb.upper && volSpike) {\n value -= 0.5;\n details.push(\"Price above upper BB with volume spike (-0.50)\");\n confidence += 0.1;\n }\n }\n }\n\n // EMA alignment scoring\n const { ema8, ema21, ema50, ema200 } = signals.ema;\n if (!isNaN(ema8) && !isNaN(ema21) && !isNaN(ema50) && !isNaN(ema200)) {\n if (ema8 > ema21 && ema21 > ema50 && ema50 > ema200) {\n value += 0.4;\n details.push(\"Bullish EMA alignment (8>21>50>200) (+0.40)\");\n confidence += 0.1;\n } else if (ema8 < ema21 && ema21 < ema50 && ema50 < ema200) {\n value -= 0.4;\n details.push(\"Bearish EMA alignment (8<21<50<200) (-0.40)\");\n confidence += 0.1;\n }\n }\n\n return {\n name: \"technical\",\n value: clamp(value),\n confidence: Math.min(confidence, 1.0),\n source: \"Technical Analysis\",\n details: details.join(\"; \"),\n };\n}\n\n// ── Score Sentiment ──\n\nexport function scoreSentiment(fearAndGreed: number, sentimentZScore?: number): Signal {\n let value = 0;\n const details: string[] = [];\n let confidence = 0.6;\n\n if (fearAndGreed < 15) {\n value = 1.0;\n details.push(`Extreme fear (F&G=${fearAndGreed}) → max contrarian buy`);\n confidence = 0.8;\n } else if (fearAndGreed < 25) {\n // Linear interpolation: F&G 15→+0.8, F&G 25→+0.5\n value = 0.8 - 0.3 * ((fearAndGreed - 15) / 10);\n details.push(`Fear (F&G=${fearAndGreed}) → contrarian buy (+${value.toFixed(2)})`);\n confidence = 0.7;\n } else if (fearAndGreed < 40) {\n // F&G 25→+0.3, F&G 40→+0.1\n value = 0.3 - 0.2 * ((fearAndGreed - 25) / 15);\n details.push(`Mild fear (F&G=${fearAndGreed}) → slight buy (+${value.toFixed(2)})`);\n } else if (fearAndGreed <= 60) {\n value = 0;\n details.push(`Neutral sentiment (F&G=${fearAndGreed})`);\n confidence = 0.4;\n } else if (fearAndGreed <= 75) {\n // F&G 60→-0.1, F&G 75→-0.5\n value = -0.1 - 0.4 * ((fearAndGreed - 60) / 15);\n details.push(`Greed (F&G=${fearAndGreed}) → contrarian sell (${value.toFixed(2)})`);\n } else {\n // F&G 75→-0.5, F&G 100→-1.0\n value = -0.5 - 0.5 * ((fearAndGreed - 75) / 25);\n details.push(`Extreme greed (F&G=${fearAndGreed}) → max contrarian sell (${value.toFixed(2)})`);\n confidence = 0.8;\n }\n\n // Z-score adjustment\n if (sentimentZScore !== undefined) {\n const adjustment = clamp(sentimentZScore * 0.1, -0.2, 0.2);\n value = clamp(value + adjustment);\n if (Math.abs(adjustment) > 0.05) {\n details.push(`Z-score adjustment: ${adjustment >= 0 ? \"+\" : \"\"}${adjustment.toFixed(2)}`);\n }\n }\n\n return {\n name: \"sentiment\",\n value: clamp(value),\n confidence,\n source: \"Market Sentiment\",\n details: details.join(\"; \"),\n };\n}\n\n// ── Score On-Chain ──\n\nexport function scoreOnChain(data: {\n exchangeNetFlow?: number;\n whaleAccumulating?: boolean;\n activeAddressesGrowth?: number;\n}): Signal {\n let value = 0;\n const details: string[] = [];\n let confidence = 0.4;\n\n if (data.exchangeNetFlow !== undefined) {\n if (data.exchangeNetFlow < 0) {\n // Negative net flow = tokens leaving exchanges = bullish\n value += 0.4;\n details.push(\"Negative exchange net flow (bullish)\");\n confidence += 0.1;\n } else if (data.exchangeNetFlow > 0) {\n value -= 0.4;\n details.push(\"Positive exchange net flow (bearish)\");\n confidence += 0.1;\n }\n }\n\n if (data.whaleAccumulating !== undefined) {\n if (data.whaleAccumulating) {\n value += 0.3;\n details.push(\"Whale accumulation detected\");\n confidence += 0.15;\n } else {\n value -= 0.3;\n details.push(\"Whale distribution detected\");\n confidence += 0.15;\n }\n }\n\n if (data.activeAddressesGrowth !== undefined) {\n const growth = data.activeAddressesGrowth;\n if (growth > 0.1) {\n value += 0.2;\n details.push(`Active addresses growing (${(growth * 100).toFixed(1)}%)`);\n } else if (growth < -0.1) {\n value -= 0.2;\n details.push(`Active addresses declining (${(growth * 100).toFixed(1)}%)`);\n }\n }\n\n if (details.length === 0) {\n details.push(\"No on-chain data available\");\n confidence = 0.1;\n }\n\n return {\n name: \"onchain\",\n value: clamp(value),\n confidence: Math.min(confidence, 1.0),\n source: \"On-Chain Analysis\",\n details: details.join(\"; \"),\n };\n}\n\n// ── Score Fundamentals ──\n\nexport function scoreFundamental(data: {\n tvlGrowthWeekly?: number;\n revenueGrowth?: number;\n mcapToTvl?: number;\n}): Signal {\n let value = 0;\n const details: string[] = [];\n let confidence = 0.4;\n\n if (data.tvlGrowthWeekly !== undefined) {\n const g = data.tvlGrowthWeekly;\n if (g > 0.1) {\n value += 0.4;\n details.push(`TVL growing ${(g * 100).toFixed(1)}% weekly`);\n confidence += 0.1;\n } else if (g < -0.1) {\n value -= 0.4;\n details.push(`TVL declining ${(g * 100).toFixed(1)}% weekly`);\n confidence += 0.1;\n }\n }\n\n if (data.revenueGrowth !== undefined) {\n const g = data.revenueGrowth;\n if (g > 0.1) {\n value += 0.3;\n details.push(`Revenue growing ${(g * 100).toFixed(1)}%`);\n } else if (g < -0.1) {\n value -= 0.3;\n details.push(`Revenue declining ${(g * 100).toFixed(1)}%`);\n }\n }\n\n if (data.mcapToTvl !== undefined) {\n const ratio = data.mcapToTvl;\n if (ratio < 1.0) {\n value += 0.3;\n details.push(`Mcap/TVL ratio ${ratio.toFixed(2)} (undervalued)`);\n confidence += 0.1;\n } else if (ratio > 5.0) {\n value -= 0.3;\n details.push(`Mcap/TVL ratio ${ratio.toFixed(2)} (overvalued)`);\n confidence += 0.1;\n }\n }\n\n if (details.length === 0) {\n details.push(\"No fundamental data available\");\n confidence = 0.1;\n }\n\n return {\n name: \"fundamental\",\n value: clamp(value),\n confidence: Math.min(confidence, 1.0),\n source: \"Fundamental Analysis\",\n details: details.join(\"; \"),\n };\n}\n\n// ── Score Events ──\n\nexport function scoreEvent(data: {\n unlockPercent?: number;\n daysUntilUnlock?: number;\n positiveEvent?: boolean;\n}): Signal {\n let value = 0;\n const details: string[] = [];\n let confidence = 0.3;\n\n if (data.unlockPercent !== undefined && data.daysUntilUnlock !== undefined) {\n if (data.unlockPercent > 5 && data.daysUntilUnlock < 30) {\n // Large upcoming unlock = bearish\n const severity = Math.min(data.unlockPercent / 20, 1.0);\n value -= severity * 0.8;\n details.push(\n `Token unlock: ${data.unlockPercent.toFixed(1)}% in ${data.daysUntilUnlock} days (bearish)`,\n );\n confidence = 0.7;\n } else if (data.unlockPercent < 2) {\n details.push(\"Minor unlock, negligible impact\");\n }\n }\n\n if (data.positiveEvent !== undefined) {\n if (data.positiveEvent) {\n value += 0.4;\n details.push(\"Positive catalyst event\");\n confidence += 0.2;\n } else {\n value -= 0.4;\n details.push(\"Negative catalyst event\");\n confidence += 0.2;\n }\n }\n\n if (details.length === 0) {\n details.push(\"No event data available\");\n confidence = 0.1;\n }\n\n return {\n name: \"event\",\n value: clamp(value),\n confidence: Math.min(confidence, 1.0),\n source: \"Event Analysis\",\n details: details.join(\"; \"),\n };\n}\n\n// ── Combine All Signals Into Trade Decision ──\n\n/** Lagging technical indicators whose weight is dampened during momentum moves. */\nconst LAGGING_TECHNICAL_SIGNALS = new Set(['technical', 'meanReversion']);\n\n/** Signal name → weight category mapping. */\nconst SIGNAL_CATEGORY_MAP: Record<string, keyof ScoringWeights> = {\n technical: \"technical\",\n sentiment: \"sentiment\",\n onchain: \"onchain\",\n fundamental: \"fundamental\",\n event: \"event\",\n smartMoney: \"smartMoney\",\n\n // Strategy signal mappings to categories\n // momentum intentionally has no key in getStrategyAdjustments() — it keeps\n // full weight during momentum overrides (not dampened like lagging indicators).\n momentum: \"technical\",\n breakoutOnChain: \"technical\",\n meanReversion: \"technical\",\n multiTimeframe: \"technical\",\n dexFlow: \"onchain\",\n fundingRate: \"onchain\", // FIX 2: was \"fundamental\" — wasted on majors (0.00 weight)\n tvlMomentum: \"fundamental\",\n sentimentContrarian: \"sentiment\",\n twitterSentiment: \"sentiment\",\n tokenUnlock: \"event\",\n hyperliquidFlow: \"onchain\",\n};\n\n/** Categories that rely on x402 paid data (Nansen, Messari). */\nconst X402_CATEGORIES: Set<keyof ScoringWeights> = new Set([\"smartMoney\", \"event\"]);\n\nexport function computeTradeDecision(\n signals: Signal[],\n weights?: ScoringWeights,\n regimeAdjustments?: Record<string, number>,\n correlationCheck?: CorrelationCheck,\n regime?: MarketRegime,\n x402Available?: boolean,\n): TradeDecision {\n const w = weights ?? DEFAULT_WEIGHTS;\n const thresholds = thresholdsForRegime(regime);\n\n // FIX 1: When x402 was configured but wallet is unfunded, exclude\n // x402-dependent categories (smartMoney, event) so their zero-value signals\n // don't dilute the aggregate. Only when x402Available is explicitly false\n // (meaning x402 was intended but failed) — undefined means x402 was never\n // configured, so no exclusion (the agent may have those signals from other\n // sources, e.g. backtest or manual injection).\n const excludedCategories = new Set<keyof ScoringWeights>();\n if (x402Available === false) {\n for (const cat of X402_CATEGORIES) {\n if (w[cat] > 0) excludedCategories.add(cat);\n }\n }\n\n // FIX 3: Normalize per-category weights.\n // Count how many signals fire per category, then split category weight among them.\n const categoryCounts: Record<string, number> = {};\n for (const signal of signals) {\n if (signal._weightOverride !== undefined) continue; // manual overrides skip normalization\n const category = SIGNAL_CATEGORY_MAP[signal.name];\n if (!category || excludedCategories.has(category)) continue;\n categoryCounts[category] = (categoryCounts[category] ?? 0) + 1;\n }\n\n // Weighted sum\n let totalWeight = 0;\n let weightedSum = 0;\n let weightedConfidence = 0;\n\n for (const signal of signals) {\n // Determine signal weight\n let signalWeight: number;\n\n if (signal._weightOverride !== undefined) {\n signalWeight = signal._weightOverride;\n } else {\n const category = SIGNAL_CATEGORY_MAP[signal.name];\n\n // FIX 1: skip signals in excluded x402 categories\n if (category && excludedCategories.has(category)) continue;\n\n const categoryWeight = category ? w[category] : 0.1;\n const count = category ? (categoryCounts[category] ?? 1) : 1;\n // FIX 3: split category weight among all signals in that category\n signalWeight = categoryWeight / count;\n }\n\n // Apply regime adjustment if available\n let adjustedValue = signal.value;\n let adjustedConfidence = signal.confidence;\n\n if (regimeAdjustments && signal.name in regimeAdjustments) {\n const adjustment = regimeAdjustments[signal.name]!;\n adjustedValue *= adjustment;\n adjustedConfidence *= adjustment;\n }\n\n // Dampen lagging technical indicators during momentum moves.\n // RSI/MACD/EMA read bearish alignment during the first hours of a pump\n // because they lag by construction (14-period RSI, 26-period MACD, 50/200 EMA).\n // This cancels real-time bullish signals — ETH scored 0.00 during a +7.8% rally\n // because scoreTechnical() was reading stale bearish MACD/EMA.\n //\n // When the regime is a momentum override (trending-up/down), cut the weight\n // of lagging technical signals by 50%. The momentum signal (which is NOT lagged)\n // and breakoutOnChain (which uses recent price action) keep their full weight.\n // LAGGING_TECHNICAL_SIGNALS hoisted to module level for GC efficiency\n if (regime && (regime === 'trending-up' || regime === 'trending-down')\n && LAGGING_TECHNICAL_SIGNALS.has(signal.name)) {\n signalWeight *= 0.5;\n }\n\n weightedSum += adjustedValue * signalWeight;\n weightedConfidence += adjustedConfidence * signalWeight;\n totalWeight += signalWeight;\n }\n\n let score = totalWeight > 0 ? weightedSum / totalWeight : 0;\n const confidence = totalWeight > 0 ? weightedConfidence / totalWeight : 0;\n\n // ── Convergence bonus ──\n // When multiple independent categories agree on direction, amplify the score.\n // The weighted average treats disagreement and agreement symmetrically — a\n // 5/6 bullish consensus scores the same as if those 5 categories had smaller\n // values. In reality, broad agreement across uncorrelated sources (technical +\n // sentiment + on-chain + funding) is stronger evidence than any single source\n // at high conviction.\n //\n // On the Apr 13-14 BTC pump: 4-5 categories were bullish but the score\n // capped at 0.27. With convergence bonus: 0.27 × 1.15 = 0.31 → fires BUY.\n if (Math.abs(score) > 0.01) {\n const scoreSign = Math.sign(score);\n // Compute net direction per active category.\n // NOTE: uses raw signal.value (not dampened). This means a dampened\n // technical signal at -0.30 still votes \"bearish\" in convergence even\n // though its weighted contribution is halved. This is intentional:\n // dampening reduces a lagging signal's INFLUENCE on the score, but\n // its DIRECTIONAL OPINION still counts for convergence. If we used\n // dampened values, a 50% weight cut would make a -0.30 signal appear\n // as -0.15, potentially flipping the category to \"neutral\" and\n // artificially inflating the convergence count.\n const categoryNetDirection = new Map<string, number>();\n for (const signal of signals) {\n const category = SIGNAL_CATEGORY_MAP[signal.name];\n if (!category || excludedCategories.has(category)) continue;\n if (signal._weightOverride !== undefined) continue;\n const current = categoryNetDirection.get(category) ?? 0;\n categoryNetDirection.set(category, current + signal.value);\n }\n // Count categories that agree with the aggregate direction\n let agreeing = 0;\n let totalActive = 0;\n for (const [, netValue] of categoryNetDirection) {\n totalActive++;\n if (Math.sign(netValue) === scoreSign) agreeing++;\n }\n // Bonus kicks in at 4+ agreeing categories out of at least 4 active.\n // Scale: 4/N → 1.15x, 5/N → 1.30x, 6/N → 1.45x. Capped at 1.45x.\n if (agreeing >= 4 && totalActive >= 4) {\n const bonus = 1.0 + 0.15 * (agreeing - 3);\n score *= Math.min(bonus, 1.45);\n }\n }\n\n // Apply correlation-aware adjustment if provided\n // BTC bearish → suppress longs, boost shorts\n // BTC bullish → boost longs, suppress shorts\n if (correlationCheck) {\n if (correlationCheck.btcBias === \"bearish\" && score > 0) {\n // Suppress long signals when BTC is bearish (factor < 1)\n score *= correlationCheck.suppressionFactor;\n } else if (correlationCheck.btcBias === \"bearish\" && score < 0) {\n // Boost short signals when BTC is bearish\n score *= (1 + Math.abs(correlationCheck.btcScore) * 0.3);\n } else if (correlationCheck.btcBias === \"bullish\" && score > 0) {\n // Boost long signals when BTC is bullish\n score *= (1 + Math.abs(correlationCheck.btcScore) * 0.3);\n } else if (correlationCheck.btcBias === \"bullish\" && score < 0) {\n // Suppress short signals when BTC is bullish (factor < 1)\n score *= correlationCheck.suppressionFactor;\n }\n\n score = Math.max(-1, Math.min(1, score));\n }\n\n // Unconditional clamp — convergence bonus and correlation boost can both\n // push score beyond [-1, 1]. Clamp was previously only inside the\n // correlationCheck block; scores flowed unclamped when no correlation\n // data was available.\n score = Math.max(-1, Math.min(1, score));\n\n // Determine action using regime-conditional thresholds\n let action: TradeDecision[\"action\"];\n // Symmetric >= / <= on both sides — exactly hitting a threshold counts as\n // crossing it. Score == buy → BUY. Score == sell → SELL. Score ==\n // strongSell → STRONG_SELL. This replaces the old mixed > / >= scheme\n // flagged in code review as asymmetric.\n if (score >= thresholds.strongBuy) action = \"STRONG_BUY\";\n else if (score >= thresholds.buy) action = \"BUY\";\n else if (score <= thresholds.strongSell) action = \"STRONG_SELL\";\n else if (score <= thresholds.sell) action = \"SELL\";\n else action = \"HOLD\";\n\n const reasoning = signals.map((s) => `[${s.source}] ${s.details}`).join(\"\\n\");\n\n return {\n action,\n score,\n signals,\n reasoning,\n confidence,\n timestamp: Date.now(),\n thresholds,\n };\n}\n","/**\n * Contrarian Sentiment Strategy\n * Uses Fear & Greed index and social sentiment Z-score to trade against the crowd.\n */\n\nimport type { Signal } from '../scoring.js';\nimport type { Strategy, StrategyContext } from './types.js';\nimport { clamp } from '../utils.js';\n\nexport class SentimentContrarianStrategy implements Strategy {\n name = 'sentimentContrarian';\n description = 'Contrarian strategy: buys extreme fear, sells extreme greed, with social Z-score adjustment';\n requiredData = ['fearAndGreed'];\n\n async analyze(ctx: StrategyContext): Promise<Signal> {\n if (!ctx.fearAndGreed) {\n return {\n name: this.name,\n value: 0.0,\n confidence: 0.1,\n source: 'Contrarian Sentiment',\n details: 'No Fear & Greed data available',\n };\n }\n\n const fg = ctx.fearAndGreed.value;\n const details: string[] = [];\n let value = 0;\n let confidence = 0.3;\n\n // Only fire on EXTREMES — moderate fear/greed (20–35, 65–80) used to add a\n // persistent +0.2 to +0.5 contrarian bias during prolonged sentiment\n // regimes. Audit data showed sentimentContrarian held a constant +0.68\n // mean across 458 observations, dominating the sentiment category and\n // pushing every score upward regardless of price action. Restricting\n // contributions to extremes preserves the high-conviction edge while\n // killing the structural bias.\n if (fg < 20) {\n // Extreme fear: strong buy. Linear: fg=0 → +1.0, fg=20 → +0.6\n value = 1.0 - (fg / 20) * 0.4;\n confidence = 0.9;\n details.push(`Extreme fear (F&G=${fg}): strong contrarian buy signal`);\n } else if (fg > 80) {\n // Extreme greed: strong sell. Linear: fg=80 → -0.6, fg=100 → -1.0\n value = -(0.6 + ((fg - 80) / 20) * 0.4);\n confidence = 0.9;\n details.push(`Extreme greed (F&G=${fg}): strong contrarian sell signal`);\n } else {\n // Moderate / neutral zone: no contrarian edge — emit a near-zero\n // confidence signal so the scoring layer effectively ignores it.\n value = 0.0;\n confidence = 0.1;\n const label = fg < 35 ? 'mild fear' : fg > 65 ? 'mild greed' : 'neutral';\n details.push(`${label} (F&G=${fg}): below extremes, no edge`);\n }\n\n // Z-score adjustment: extreme social sentiment reinforces the signal\n if (ctx.sentimentZScore !== undefined) {\n const z = ctx.sentimentZScore;\n // If Z-score agrees with F&G direction, strengthen; if contradicts, weaken\n let adjustment = 0;\n if (z < -1.5 && value > 0) {\n // Extreme negative social sentiment + fear = stronger buy\n adjustment = 0.2;\n details.push(`Social Z-score ${z.toFixed(2)} reinforces buy (+0.20)`);\n } else if (z > 1.5 && value < 0) {\n // Extreme positive social sentiment + greed = stronger sell\n adjustment = -0.2;\n details.push(`Social Z-score ${z.toFixed(2)} reinforces sell (-0.20)`);\n } else if (Math.abs(z) > 1.0) {\n // Moderate Z-score adjustment\n adjustment = z > 0 ? -0.1 : 0.1;\n if (Math.abs(adjustment) > 0.05) {\n details.push(`Social Z-score ${z.toFixed(2)} adjustment (${adjustment > 0 ? '+' : ''}${adjustment.toFixed(2)})`);\n }\n }\n value = clamp(value + adjustment);\n }\n\n return {\n name: this.name,\n value: clamp(value),\n confidence: Math.min(confidence, 1.0),\n source: 'Contrarian Sentiment',\n details: details.join('; '),\n };\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 * DEX Flow Analysis Strategy\n * Uses buy/sell transaction ratios from DEXScreener to gauge on-chain momentum.\n *\n * If buy txns significantly > sell txns (ratio > 1.5): bullish +0.3 to +0.6\n * If sell txns > buy txns (ratio > 1.5): bearish -0.3 to -0.6\n * Volume spike (24h volume > 3x liquidity): signal amplified\n * Combines 1h (reactive) and 24h (trend confirmation) data\n */\n\nimport type { Signal } from '../scoring.js';\nimport type { Strategy, StrategyContext } from './types.js';\nimport { DexScreenerProvider } from '../../providers/data/dexscreener.js';\nimport type { DexPair } from '../../providers/data/dexscreener.js';\nimport { clamp } from '../utils.js';\n\n/** Well-known CoinGecko ID → ticker symbol mapping. */\nconst TOKEN_SYMBOL_MAP: Record<string, string> = {\n ethereum: 'ETH',\n bitcoin: 'BTC',\n solana: 'SOL',\n uniswap: 'UNI',\n chainlink: 'LINK',\n aave: 'AAVE',\n ripple: 'XRP',\n cardano: 'ADA',\n polkadot: 'DOT',\n avalanche: 'AVAX',\n polygon: 'MATIC',\n arbitrum: 'ARB',\n optimism: 'OP',\n litecoin: 'LTC',\n dogecoin: 'DOGE',\n 'shiba-inu': 'SHIB',\n pepe: 'PEPE',\n celestia: 'TIA',\n sui: 'SUI',\n aptos: 'APT',\n sei: 'SEI',\n injective: 'INJ',\n jupiter: 'JUP',\n render: 'RENDER',\n maker: 'MKR',\n compound: 'COMP',\n lido: 'LDO',\n 'wrapped-bitcoin': 'WBTC',\n 'staked-ether': 'STETH',\n};\n\n/** Allowed chains — only EVM mainnet + major L2s. */\nconst ALLOWED_CHAINS = new Set([\n 'ethereum', 'base', 'arbitrum', 'optimism', 'polygon',\n]);\n\nconst MIN_LIQUIDITY_USD = 100_000;\n\n/** Well-known token addresses so we can search by address instead of name. */\nconst KNOWN_TOKENS: Record<string, { address: string; chain: string }> = {\n ETH: { address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', chain: 'ethereum' },\n BTC: { address: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', chain: 'ethereum' },\n WBTC: { address: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', chain: 'ethereum' },\n USDC: { address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', chain: 'ethereum' },\n USDT: { address: '0xdAC17F958D2ee523a2206206994597C13D831ec7', chain: 'ethereum' },\n UNI: { address: '0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984', chain: 'ethereum' },\n AAVE: { address: '0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9', chain: 'ethereum' },\n LINK: { address: '0x514910771AF9Ca656af840dff83E8264EcF986CA', chain: 'ethereum' },\n SOL: { address: '0xD31a59c85aE9D8edEFec411186437e05Ce3a1d82', chain: 'ethereum' },\n MKR: { address: '0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2', chain: 'ethereum' },\n CRV: { address: '0xD533a949740bb3306d119CC777fa900bA034cd52', chain: 'ethereum' },\n LDO: { address: '0x5A98FcBEA516Cf06857215779Fd812CA3beF1B32', chain: 'ethereum' },\n PENDLE: { address: '0x808507121B80c02388fAd14726482e061B8da827', chain: 'ethereum' },\n ARB: { address: '0xB50721BCf8d664c30412Cfbc6cf7a15145234ad1', chain: 'arbitrum' },\n OP: { address: '0x4200000000000000000000000000000000000042', chain: 'optimism' },\n};\n\n/**\n * Resolve a CoinGecko tokenId to a ticker symbol for DEXScreener search.\n */\nfunction resolveSymbol(tokenId: string, ctx: StrategyContext): string {\n if (TOKEN_SYMBOL_MAP[tokenId]) return TOKEN_SYMBOL_MAP[tokenId]!;\n if (ctx.tokenSymbol) return ctx.tokenSymbol.toUpperCase();\n return tokenId.toUpperCase();\n}\n\n/**\n * Filter and rank DEXScreener pairs:\n * 1. ONLY allowed EVM chains\n * 2. Require liquidity >= $100K\n * 3. Sort by liquidity descending\n */\nfunction pickBestPair(pairs: DexPair[]): DexPair | undefined {\n const viable = pairs\n .filter((p) => ALLOWED_CHAINS.has(p.chainId))\n .filter((p) => (p.liquidity?.usd ?? 0) >= MIN_LIQUIDITY_USD)\n .filter((p) => p.volume?.h24 > 0 && p.txns);\n\n if (viable.length === 0) return undefined;\n\n // Sort by liquidity descending\n viable.sort((a, b) => (b.liquidity?.usd ?? 0) - (a.liquidity?.usd ?? 0));\n return viable[0];\n}\n\nexport class DexFlowStrategy implements Strategy {\n name = 'dexFlow';\n description = 'Analyzes DEX buy/sell transaction ratios and volume for on-chain momentum';\n requiredData = ['marketData'];\n\n private dex = new DexScreenerProvider();\n\n async analyze(ctx: StrategyContext): Promise<Signal> {\n const details: string[] = [];\n let value = 0;\n let confidence = 0.3;\n\n // Try to find DEX pairs for the token\n let pairs: DexPair[] = [];\n try {\n if (ctx.dexData && Array.isArray(ctx.dexData) && ctx.dexData.length > 0) {\n pairs = ctx.dexData as DexPair[];\n } else {\n const symbol = resolveSymbol(ctx.tokenId, ctx);\n const known = KNOWN_TOKENS[symbol];\n if (known) {\n // Use address-based lookup for known tokens — much more precise\n const allPairs = await this.dex.getTokenPairs(known.address);\n pairs = allPairs.filter((p) => p.chainId === known.chain);\n } else {\n // Fallback: search by symbol\n pairs = await this.dex.searchPairs(symbol);\n }\n }\n } catch (err) {\n return {\n name: this.name,\n value: 0,\n confidence: 0.1,\n source: 'DEX Flow Analysis',\n details: `Failed to fetch DEX data: ${(err as Error).message}`,\n };\n }\n\n if (pairs.length === 0) {\n return {\n name: this.name,\n value: 0,\n confidence: 0.1,\n source: 'DEX Flow Analysis',\n details: `No DEX pairs found for ${ctx.tokenId}`,\n };\n }\n\n // Pick best pair: filter by liquidity, prefer EVM chains, sort by liquidity\n const pair = pickBestPair(pairs);\n\n if (!pair || !pair.txns) {\n return {\n name: this.name,\n value: 0,\n confidence: 0.1,\n source: 'DEX Flow Analysis',\n details: 'No DEX pairs with transaction data',\n };\n }\n\n details.push(`Pair: ${pair.baseToken.symbol}/${pair.quoteToken.symbol} on ${pair.dexId}`);\n\n // 1h transaction ratio analysis (more reactive, weighted higher)\n const h1Buys = pair.txns.h1?.buys ?? 0;\n const h1Sells = pair.txns.h1?.sells ?? 0;\n const h1Total = h1Buys + h1Sells;\n\n let h1Signal = 0;\n if (h1Total > 10) { // need minimum activity\n const h1Ratio = h1Buys / Math.max(h1Sells, 1);\n if (h1Ratio > 1.5) {\n // Bullish: scale from +0.3 (ratio=1.5) to +0.6 (ratio=3.0+)\n h1Signal = 0.3 + Math.min((h1Ratio - 1.5) / 1.5, 1.0) * 0.3;\n details.push(`1h: ${h1Buys} buys / ${h1Sells} sells (ratio ${h1Ratio.toFixed(2)}, bullish)`);\n } else if (h1Ratio < 1 / 1.5) {\n // Bearish: inverse ratio\n const invRatio = h1Sells / Math.max(h1Buys, 1);\n h1Signal = -(0.3 + Math.min((invRatio - 1.5) / 1.5, 1.0) * 0.3);\n details.push(`1h: ${h1Buys} buys / ${h1Sells} sells (ratio ${h1Ratio.toFixed(2)}, bearish)`);\n } else {\n details.push(`1h: ${h1Buys} buys / ${h1Sells} sells (balanced)`);\n }\n confidence += 0.1;\n }\n\n // 24h transaction ratio analysis (trend confirmation)\n const h24Buys = pair.txns.h24?.buys ?? 0;\n const h24Sells = pair.txns.h24?.sells ?? 0;\n const h24Total = h24Buys + h24Sells;\n\n let h24Signal = 0;\n if (h24Total > 50) { // need minimum activity\n const h24Ratio = h24Buys / Math.max(h24Sells, 1);\n if (h24Ratio > 1.5) {\n h24Signal = 0.3 + Math.min((h24Ratio - 1.5) / 1.5, 1.0) * 0.3;\n details.push(`24h: ${h24Buys} buys / ${h24Sells} sells (ratio ${h24Ratio.toFixed(2)}, bullish)`);\n } else if (h24Ratio < 1 / 1.5) {\n const invRatio = h24Sells / Math.max(h24Buys, 1);\n h24Signal = -(0.3 + Math.min((invRatio - 1.5) / 1.5, 1.0) * 0.3);\n details.push(`24h: ${h24Buys} buys / ${h24Sells} sells (ratio ${h24Ratio.toFixed(2)}, bearish)`);\n } else {\n details.push(`24h: ${h24Buys} buys / ${h24Sells} sells (balanced)`);\n }\n confidence += 0.1;\n }\n\n // Combine: 60% weight on 1h (reactive), 40% on 24h (trend)\n value = h1Signal * 0.6 + h24Signal * 0.4;\n\n // Volume spike amplifier: if 24h volume > 3x liquidity, signal is amplified\n const volume24h = pair.volume?.h24 ?? 0;\n const liquidity = pair.liquidity?.usd ?? 0;\n if (liquidity > 0 && volume24h > liquidity * 3) {\n const amplifier = Math.min(volume24h / liquidity / 3, 2.0); // cap at 2x\n value = clamp(value * amplifier);\n confidence += 0.1;\n details.push(`Volume spike: $${(volume24h / 1e6).toFixed(1)}M vol vs $${(liquidity / 1e6).toFixed(1)}M liq (${(volume24h / liquidity).toFixed(1)}x)`);\n }\n\n // Agreement bonus: if 1h and 24h agree in direction, boost confidence\n if ((h1Signal > 0 && h24Signal > 0) || (h1Signal < 0 && h24Signal < 0)) {\n confidence += 0.1;\n details.push('1h and 24h signals agree');\n } else if ((h1Signal > 0 && h24Signal < 0) || (h1Signal < 0 && h24Signal > 0)) {\n confidence -= 0.1;\n details.push('1h and 24h signals diverge');\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: 'DEX Flow Analysis',\n details: details.join('; '),\n };\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 * 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 { SmartMoneyStrategy } from './smart-money.js';\nimport { TokenUnlockStrategy } from './token-unlock.js';\nimport { SentimentContrarianStrategy } from './sentiment-contrarian.js';\nimport { BreakoutOnChainStrategy } from './breakout-onchain.js';\nimport { TvlMomentumStrategy } from './tvl-momentum.js';\nimport { FundingRateStrategy } from './funding-rate.js';\nimport { DexFlowStrategy } from './dex-flow.js';\nimport { MeanReversionStrategy } from './mean-reversion.js';\nimport { TwitterSentimentStrategy } from './twitter-sentiment.js';\nimport { HyperliquidFlowStrategy } from './hyperliquid-flow.js';\nimport { MultiTimeframeStrategy } from './multi-timeframe.js';\n\nexport type { Strategy, StrategyContext, StrategyConfig };\nexport { SmartMoneyStrategy, TokenUnlockStrategy, SentimentContrarianStrategy };\nexport { BreakoutOnChainStrategy, TvlMomentumStrategy, FundingRateStrategy };\nexport { DexFlowStrategy, MeanReversionStrategy, TwitterSentimentStrategy, HyperliquidFlowStrategy };\nexport { MultiTimeframeStrategy };\n\nexport const DEFAULT_STRATEGIES: Strategy[] = [\n // ── Active signals (fire rate >25% in production) ──\n new SentimentContrarianStrategy(), // 100% fire rate — F&G based\n new BreakoutOnChainStrategy(), // 95% fire rate\n new FundingRateStrategy(), // 52% fire rate — HL native\n new DexFlowStrategy(), // 18% fire rate\n new HyperliquidFlowStrategy(), // 57% fire rate\n new MultiTimeframeStrategy(), // 78% fire rate\n\n // ── Disabled: zero or near-zero fire rate in production ──\n // SmartMoneyStrategy — replaced by Nansen HL perp-trades in index.ts\n // TwitterSentimentStrategy — Twitter API returns 402\n // TokenUnlockStrategy — 0% fire rate, no data source\n // TvlMomentumStrategy — 0% fire rate for majors (no TVL on BTC/SOL)\n // MeanReversionStrategy — 0% fire rate, BB conditions never met\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 // 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 * 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';\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}\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): void {\n // Fire-and-forget — do not await\n _writeSignal(analysis, price, weights).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): 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 };\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 * 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 * Hyperliquid data provider — fetches native exchange data from Hyperliquid's free REST API.\n * Provides funding rates, open interest, order book, and trade flow data.\n * Base URL: https://api.hyperliquid.xyz/info (POST requests, JSON body)\n * Rate limit: reasonable usage, cached for 2 minutes.\n */\n\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\n\nconst HYPERLIQUID_BASE = 'https://api.hyperliquid.xyz/info';\n\n// Map CoinGecko token IDs to Hyperliquid coin names\nconst TOKEN_TO_COIN: Record<string, string> = {\n bitcoin: 'BTC',\n ethereum: 'ETH',\n solana: 'SOL',\n arbitrum: 'ARB',\n chainlink: 'LINK',\n aave: 'AAVE',\n uniswap: 'UNI',\n dogecoin: 'DOGE',\n avalanche: 'AVAX',\n near: 'NEAR',\n sui: 'SUI',\n aptos: 'APT',\n injective: 'INJ',\n pendle: 'PENDLE',\n pepe: 'PEPE',\n polygon: 'MATIC',\n optimism: 'OP',\n litecoin: 'LTC',\n cosmos: 'ATOM',\n filecoin: 'FIL',\n maker: 'MKR',\n cardano: 'ADA',\n polkadot: 'DOT',\n render: 'RENDER',\n jupiter: 'JUP',\n};\n\nexport interface HyperliquidData {\n fundingRate: number; // current hourly funding rate (Hyperliquid funds every hour)\n openInterest: number; // USD value\n oiChangePct: number; // OI change since last fetch (percentage, NOT 24h)\n volume24h: number; // 24h notional volume\n markPrice: number;\n oraclePrice: number;\n prevDayPrice: number;\n orderBookImbalance: number; // (bid_depth - ask_depth) / (bid_depth + ask_depth) for top 10 levels\n largeTradesBias: number; // net direction of trades > $50K in last hour (-1 to +1)\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 L2BookLevel {\n px: string;\n sz: string;\n n: number;\n}\n\ninterface L2Book {\n levels: [L2BookLevel[], L2BookLevel[]]; // [bids, asks]\n}\n\ninterface RecentTrade {\n coin: string;\n side: string;\n px: string;\n sz: string;\n time: number;\n hash: string;\n}\n\nexport class HyperliquidProvider {\n private cacheDir: string;\n private cacheTTL = 2 * 60 * 1000; // 2 minutes\n private oiCache = new Map<string, number>(); // For tracking OI changes\n\n constructor() {\n this.cacheDir = join(homedir(), '.sherwood', 'agent', 'cache');\n }\n\n /** Get comprehensive Hyperliquid data for a token. Returns null if token not supported or API failure. */\n async getHyperliquidData(tokenId: string): Promise<HyperliquidData | null> {\n const coin = TOKEN_TO_COIN[tokenId];\n if (!coin) return null;\n\n // Check cache first\n const cached = await this.readCache(tokenId);\n if (cached) return cached;\n\n try {\n // Fetch all data in parallel\n const [metaResult, bookResult, tradesResult] = await Promise.allSettled([\n this.fetchMetaAndAssetCtxs(),\n this.fetchL2Book(coin),\n this.fetchRecentTrades(coin)\n ]);\n\n // Process meta and asset contexts (funding, OI, volume, prices)\n if (metaResult.status !== 'fulfilled' || !metaResult.value) {\n return null;\n }\n\n const [meta, assetCtxs] = metaResult.value;\n const assetIdx = meta.universe.findIndex(asset => asset.name === coin);\n if (assetIdx === -1 || !assetCtxs[assetIdx]) {\n return null;\n }\n\n const assetData = assetCtxs[assetIdx]!;\n const fundingRate = parseFloat(assetData.funding);\n const openInterest = parseFloat(assetData.openInterest);\n const volume24h = parseFloat(assetData.dayNtlVlm);\n const markPrice = parseFloat(assetData.markPx);\n const oraclePrice = parseFloat(assetData.oraclePx);\n const prevDayPrice = parseFloat(assetData.prevDayPx);\n\n // OI change since last fetch (NOT 24h — in-memory cache resets on restart).\n // For true 24h change, would need historical snapshots on disk.\n const prevOI = this.oiCache.get(coin) ?? openInterest;\n const oiChangePct = prevOI > 0 ? ((openInterest - prevOI) / prevOI) * 100 : 0;\n this.oiCache.set(coin, openInterest);\n\n // Process order book imbalance\n let orderBookImbalance = 0;\n if (bookResult.status === 'fulfilled' && bookResult.value) {\n orderBookImbalance = this.calculateOrderBookImbalance(bookResult.value);\n }\n\n // Process large trades bias\n let largeTradesBias = 0;\n if (tradesResult.status === 'fulfilled' && tradesResult.value) {\n largeTradesBias = this.calculateLargeTradesBias(tradesResult.value);\n }\n\n const data: HyperliquidData = {\n fundingRate,\n openInterest,\n oiChangePct,\n volume24h,\n markPrice,\n oraclePrice,\n prevDayPrice,\n orderBookImbalance,\n largeTradesBias,\n };\n\n // Cache results\n await this.writeCache(tokenId, data);\n\n return data;\n } catch (err) {\n console.error(`Hyperliquid API error for ${tokenId}: ${(err as Error).message}`);\n return null;\n }\n }\n\n /** Fetch metadata and asset contexts (funding, OI, volume, prices). */\n private async fetchMetaAndAssetCtxs(): Promise<[MetaAndAssetCtxs, AssetCtx[]] | null> {\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 const data = await response.json() as [MetaAndAssetCtxs, AssetCtx[]];\n return data;\n }\n\n /** Fetch L2 order book for a coin. */\n private async fetchL2Book(coin: string): Promise<L2Book | null> {\n const response = await fetch(HYPERLIQUID_BASE, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ type: 'l2Book', coin }),\n });\n\n if (!response.ok) return null;\n return await response.json() as L2Book;\n }\n\n /** Fetch recent trades for a coin. */\n private async fetchRecentTrades(coin: string): Promise<RecentTrade[] | null> {\n const response = await fetch(HYPERLIQUID_BASE, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ type: 'recentTrades', coin }),\n });\n\n if (!response.ok) return null;\n return await response.json() as RecentTrade[];\n }\n\n /** Calculate order book imbalance from L2 book. */\n private calculateOrderBookImbalance(book: L2Book): number {\n const [bids, asks] = book.levels;\n\n // Sum top 10 levels for both sides\n const bidDepth = bids.slice(0, 10).reduce((sum, level) => sum + parseFloat(level.sz), 0);\n const askDepth = asks.slice(0, 10).reduce((sum, level) => sum + parseFloat(level.sz), 0);\n\n const totalDepth = bidDepth + askDepth;\n if (totalDepth === 0) return 0;\n\n return (bidDepth - askDepth) / totalDepth;\n }\n\n /** Calculate large trades bias from recent trades (>$50K in last hour). */\n private calculateLargeTradesBias(trades: RecentTrade[]): number {\n const oneHourAgo = Date.now() - 60 * 60 * 1000;\n const recentTrades = trades.filter(t => t.time > oneHourAgo);\n\n let buyVolume = 0;\n let sellVolume = 0;\n\n for (const trade of recentTrades) {\n const size = parseFloat(trade.sz);\n const price = parseFloat(trade.px);\n const notional = size * price;\n\n // Only count trades >$50K\n if (notional > 50000) {\n if (trade.side === 'B') {\n buyVolume += notional;\n } else if (trade.side === 'A') {\n sellVolume += notional;\n }\n }\n }\n\n const totalVolume = buyVolume + sellVolume;\n if (totalVolume === 0) return 0;\n\n return (buyVolume - sellVolume) / totalVolume;\n }\n\n /** Read cached Hyperliquid data. */\n private async readCache(tokenId: string): Promise<HyperliquidData | null> {\n try {\n const cacheFile = join(this.cacheDir, `hl-${tokenId}.json`);\n const raw = await readFile(cacheFile, 'utf-8');\n const cached = JSON.parse(raw) as { ts: number; data: HyperliquidData };\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 Hyperliquid data to cache. */\n private async writeCache(tokenId: string, data: HyperliquidData): Promise<void> {\n try {\n await mkdir(this.cacheDir, { recursive: true });\n const cacheFile = join(this.cacheDir, `hl-${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 * 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\ninterface BtcDominanceData {\n market_cap_percentage: {\n btc: number;\n };\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 < 200) {\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 const ema50 = calculateEMA(closes, 50);\n const ema200 = calculateEMA(closes, 200);\n const currentEma50 = this.getLastValidValue(ema50);\n const currentEma200 = this.getLastValidValue(ema200);\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, 14);\n const currentAdx = this.getLastValidValue(adx);\n\n let btcTrend: RegimeAnalysis[\"btcTrend\"] = \"neutral\";\n if (!isNaN(currentEma50) && !isNaN(currentEma200)) {\n if (currentPrice > currentEma50 && currentEma50 > currentEma200) {\n btcTrend = \"up\";\n } else if (currentPrice < currentEma50 && currentEma50 < currentEma200) {\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 < 20) {\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 > 25 && btcTrend !== \"neutral\") {\n if (btcTrend === \"up\") {\n regime = \"trending-up\";\n confidence = Math.min(0.9, 0.6 + (currentAdx - 25) * 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 - 25) * 0.01);\n details = `Strong downtrend (ADX ${currentAdx.toFixed(1)})`;\n }\n } else {\n regime = \"ranging\";\n confidence = currentAdx < 20 ? 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 if (candles.length < 200) {\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 classifySync ──\n const momentumResult = this.checkMomentumOverride(candles, currentPrice);\n if (momentumResult) return momentumResult;\n\n // ── Standard EMA/ADX regime classification ──\n // Calculate EMAs for trend detection\n const ema50 = calculateEMA(closes, 50);\n const ema200 = calculateEMA(closes, 200);\n const currentEma50 = this.getLastValidValue(ema50);\n const currentEma200 = this.getLastValidValue(ema200);\n\n // Calculate Bollinger Bands for volatility\n const bb = calculateBollingerBands(candles, 20, 2.0);\n const currentBbWidth = this.getLastValidValue(bb.width);\n\n // Calculate ATR for additional volatility measure\n const atr = calculateATR(candles, 14);\n const currentAtr = this.getLastValidValue(atr);\n const atrRatio = currentAtr / currentPrice;\n\n // Calculate ADX for trend strength\n const adx = this.calculateADX(candles, 14);\n const currentAdx = this.getLastValidValue(adx);\n\n // Fetch BTC dominance (async, with fallback)\n let btcDominanceTrend: \"rising\" | \"falling\" | \"neutral\" = \"neutral\";\n try {\n btcDominanceTrend = await this.getBtcDominanceTrend();\n } catch {\n // Use default if fetch fails\n }\n\n // Determine BTC trend\n let btcTrend: RegimeAnalysis[\"btcTrend\"] = \"neutral\";\n if (!isNaN(currentEma50) && !isNaN(currentEma200)) {\n if (currentPrice > currentEma50 && currentEma50 > currentEma200) {\n btcTrend = \"up\";\n } else if (currentPrice < currentEma50 && currentEma50 < currentEma200) {\n btcTrend = \"down\";\n }\n }\n\n // Determine volatility level\n let volatilityLevel: RegimeAnalysis[\"volatilityLevel\"] = \"normal\";\n if (!isNaN(currentBbWidth)) {\n if (currentBbWidth < 0.04) {\n volatilityLevel = \"low\";\n } else if (currentBbWidth > 0.20) {\n volatilityLevel = \"extreme\";\n } else if (currentBbWidth > 0.10) {\n volatilityLevel = \"high\";\n }\n }\n\n // Determine primary regime and confidence\n let regime: MarketRegime;\n let confidence: number;\n let details: string;\n\n // Volatility regimes take precedence if extreme\n if (volatilityLevel === \"extreme\") {\n regime = \"high-volatility\";\n confidence = 0.9;\n details = `Extreme volatility detected (BB width: ${currentBbWidth.toFixed(3)}, ATR: ${atrRatio.toFixed(3)})`;\n } else if (volatilityLevel === \"low\" && currentAdx < 20) {\n regime = \"low-volatility\";\n confidence = 0.8;\n details = `Low volatility detected (BB width: ${currentBbWidth.toFixed(3)}, ADX: ${currentAdx.toFixed(1)})`;\n } else {\n // Trend-based regimes\n if (currentAdx > 25 && btcTrend !== \"neutral\") {\n if (btcTrend === \"up\") {\n regime = \"trending-up\";\n confidence = Math.min(0.9, 0.6 + (currentAdx - 25) * 0.01);\n details = `Strong uptrend (ADX: ${currentAdx.toFixed(1)}, EMAs aligned bullish)`;\n } else {\n regime = \"trending-down\";\n confidence = Math.min(0.9, 0.6 + (currentAdx - 25) * 0.01);\n details = `Strong downtrend (ADX: ${currentAdx.toFixed(1)}, EMAs aligned bearish)`;\n }\n } else {\n regime = \"ranging\";\n confidence = currentAdx < 20 ? 0.7 : 0.5;\n details = `Ranging market (ADX: ${currentAdx.toFixed(1)}, no clear trend)`;\n }\n }\n\n // Add BTC dominance context to details\n if (btcDominanceTrend !== \"neutral\") {\n details += `, BTC dominance ${btcDominanceTrend}`;\n }\n\n // Generate strategy adjustments based on regime\n const strategyAdjustments = this.getStrategyAdjustments(regime);\n\n return {\n regime,\n confidence,\n btcTrend,\n volatilityLevel,\n details,\n strategyAdjustments,\n };\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 private async getBtcDominanceTrend(): Promise<\"rising\" | \"falling\" | \"neutral\"> {\n try {\n const response = await fetch(\"https://api.coingecko.com/api/v3/global\");\n if (!response.ok) throw new Error(\"Failed to fetch global data\");\n\n const data = await response.json() as BtcDominanceData;\n const currentDominance = data.market_cap_percentage.btc;\n\n // This is a simplified trend detection - in production you'd want historical data\n // For now, just use the current value as a neutral baseline\n if (currentDominance > 50) return \"rising\";\n if (currentDominance < 40) return \"falling\";\n return \"neutral\";\n } catch {\n return \"neutral\";\n }\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 const baseMultipliers: Record<MarketRegime, Record<string, number>> = {\n \"trending-up\": {\n breakoutOnChain: 1.3,\n meanReversion: 0.3,\n fundingRate: 1.0,\n dexFlow: 1.2,\n twitterSentiment: 1.0,\n sentimentContrarian: 0.7,\n tvlMomentum: 1.2,\n hyperliquidFlow: 1.2,\n },\n \"trending-down\": {\n breakoutOnChain: 1.3,\n meanReversion: 0.3,\n fundingRate: 1.2,\n dexFlow: 1.2,\n twitterSentiment: 1.0,\n sentimentContrarian: 0.8,\n tvlMomentum: 0.5,\n hyperliquidFlow: 1.3,\n },\n \"ranging\": {\n breakoutOnChain: 0.5,\n meanReversion: 1.5,\n fundingRate: 1.0,\n dexFlow: 0.8,\n twitterSentiment: 0.8,\n sentimentContrarian: 1.3,\n tvlMomentum: 1.0,\n hyperliquidFlow: 1.0,\n },\n \"high-volatility\": {\n breakoutOnChain: 1.0,\n meanReversion: 0.3,\n fundingRate: 1.0,\n dexFlow: 0.7,\n twitterSentiment: 0.7,\n sentimentContrarian: 0.7,\n tvlMomentum: 0.7,\n hyperliquidFlow: 0.7,\n },\n \"low-volatility\": {\n breakoutOnChain: 1.3,\n meanReversion: 1.2,\n fundingRate: 0.8,\n dexFlow: 0.8,\n twitterSentiment: 0.8,\n sentimentContrarian: 0.8,\n tvlMomentum: 0.8,\n hyperliquidFlow: 0.8,\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 meanReversion: 1.0,\n fundingRate: 1.0,\n dexFlow: 1.0,\n twitterSentiment: 1.0,\n sentimentContrarian: 1.0,\n tvlMomentum: 1.0,\n hyperliquidFlow: 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\";\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\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 }\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\n try {\n const cached = await this.loadCache();\n const now = Date.now();\n const cacheAge = now - cached.timestamp;\n const CACHE_DURATION = 10 * 60 * 1000; // 10 minutes\n\n if (cacheAge < CACHE_DURATION) {\n return cached.btcStructure;\n }\n } catch {\n // Cache miss or invalid, continue to fresh analysis\n }\n\n // Fetch fresh BTC data\n const btcStructure = await this.analyzeBtcStructure();\n\n // Save to cache\n try {\n await this.saveCache(btcStructure);\n } catch {\n // Non-critical, continue without caching\n }\n\n return btcStructure;\n }\n\n private async analyzeBtcStructure(): Promise<BtcStructure> {\n try {\n // Fetch BTC candles (30 days for EMAs)\n const ohlcData = await this.coingecko.getOHLC(\"bitcoin\", 30);\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';\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 const volume24h = parseFloat(ctx.dayNtlVlm);\n const funding = parseFloat(ctx.funding);\n const openInterest = parseFloat(ctx.openInterest);\n const markPrice = parseFloat(ctx.markPx);\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 } from './risk.js';\nimport { CoinGeckoProvider } from '../providers/data/coingecko.js';\nimport { Reporter } from './reporter.js';\nimport { DynamicTokenSelector } from './token-selector.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 dailyPnl: 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\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 }\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 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 if (!this.running) break;\n\n const ms = parseCycleInterval(this.config.agent.cycle);\n console.log(chalk.dim(` Next cycle in ${this.config.agent.cycle}. Sleeping...`));\n await this.sleepInterruptible(ms);\n }\n\n console.log(chalk.bold('\\n Agent loop stopped.\\n'));\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 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 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 dailyPnl: state.dailyPnl,\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') {\n currentPrices[id] = price;\n }\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 signals.push({ token: result.token, score, action, regime: result.regime?.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 for the token\n const priceData = await this.coingecko.getPrice([result.token], ['usd']);\n const currentPrice = priceData?.[result.token]?.usd;\n\n if (typeof currentPrice === 'number' && currentPrice > 0) {\n const execResult = await this.executor.execute(result.decision, result.token, currentPrice);\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 } else {\n errors.push(`No price data for ${result.token}`);\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 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 dailyPnl: updatedState.dailyPnl,\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 */\nfunction parseCycleInterval(cycle: string): number {\n const match = cycle.match(/^(\\d+)(m|h)$/);\n if (!match) return 4 * 60 * 60 * 1000; // default 4h\n\n const value = parseInt(match[1]!, 10);\n const unit = match[2];\n\n if (unit === 'm') return value * 60 * 1000;\n if (unit === 'h') return value * 60 * 60 * 1000;\n return 4 * 60 * 60 * 1000;\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 { createWalletClient, http, encodeFunctionData, encodeAbiParameters } from 'viem';\nimport { privateKeyToAccount } from 'viem/accounts';\nimport type { TradeDecision } from './scoring.js';\nimport type { Position } from './risk.js';\nimport { RiskManager } from './risk.js';\nimport { PortfolioTracker } from './portfolio.js';\nimport { BASE_STRATEGY_ABI } from '../lib/abis.js';\nimport { hyperevm, hyperevmTestnet } from '../lib/network.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\nexport class TradeExecutor {\n private config: ExecutionConfig;\n private riskManager: RiskManager;\n private portfolio: PortfolioTracker;\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 async execute(\n decision: TradeDecision,\n tokenId: string,\n currentPrice: number,\n ): Promise<{\n success: boolean;\n position?: Position;\n error?: string;\n dryRun: boolean;\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 // 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):\n // Stop: 3% — tight enough that a failed trade exits quickly\n // Take profit: 6% (2:1 R:R) — achievable in 1-2 days on crypto vol\n // Time stop: 48h (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 STOP_LOSS_PCT = 0.03; // 3% stop\n const RR_RATIO = 2.0; // 2:1 reward/risk → 6% take profit\n const stopLossDistance = currentPrice * STOP_LOSS_PCT;\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 - STOP_LOSS_PCT * RR_RATIO) // profit below for shorts\n : currentPrice * (1 + STOP_LOSS_PCT * 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 const sizeMultiplier = isPyramid\n ? 0.5 ** ((existingSameSide!.addCount ?? 0) + 1)\n : 1.0;\n\n // Size the position using risk management\n const sizing = this.riskManager.calculatePositionSize(\n currentPrice,\n stopLossPrice,\n state.totalValue,\n );\n\n // Apply the pyramid haircut after sizing — the calculator picks a\n // risk-appropriate base size, then we shrink it for each subsequent add.\n const pyramidQuantity = sizing.quantity * sizeMultiplier;\n const pyramidSizeUsd = sizing.sizeUsd * sizeMultiplier;\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 });\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 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)}`));\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 });\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 HyperEVM via the HyperliquidPerpStrategy contract */\n private async executeHyperliquidPerp(order: OrderParams, currentPrice?: number): Promise<{ txHash: string; executedPrice: number }> {\n if (!this.config.strategyClone) throw new Error('--strategy-clone is required for hyperliquid-perp mode');\n if (!this.config.proposerPrivateKey) throw new Error('SHERWOOD_PROPOSER_KEY env var is required for live execution');\n if (!currentPrice || currentPrice <= 0) throw new Error('currentPrice is required for live execution');\n\n const account = privateKeyToAccount(this.config.proposerPrivateKey);\n const chain = this.config.chain === 'hyperevm-testnet' ? hyperevmTestnet : hyperevm;\n const client = createWalletClient({\n account,\n chain,\n transport: http(),\n });\n\n // Resolve token → HL asset index for multi-asset execution\n const assetIndex = TradeExecutor.resolveAssetIndex(order.tokenId)\n ?? this.config.assetIndex ?? 3; // fallback to config or ETH\n\n // limitPx: slightly above/below market for IOC (1% slippage buffer)\n const isShort = order.side === 'sell';\n const limitPx = isShort\n ? priceToUint64(currentPrice * 0.99) // below market for sell IOC\n : priceToUint64(currentPrice * 1.01); // above market for buy IOC\n // sz: token quantity — fetch asset-specific szDecimals from Hyperliquid meta API\n const quantity = order.amountUsd / currentPrice;\n const szDec = await getSzDecimals(assetIndex);\n const sz = sizeToUint64(quantity, szDec);\n const stopLossPx = priceToUint64(order.stopLoss);\n const stopLossSz = sz;\n\n // Use multi-asset actions (6/7) that include assetIndex in calldata.\n // One strategy clone can now trade any HL perp asset.\n const action = isShort ? 7 : 6; // ACTION_OPEN_SHORT_MULTI / ACTION_OPEN_LONG_MULTI\n const actionData = encodeAbiParameters(\n [{ type: 'uint8' }, { type: 'uint32' }, { type: 'uint64' }, { type: 'uint64' }, { type: 'uint64' }, { type: 'uint64' }],\n [action, assetIndex, limitPx, sz, stopLossPx, stopLossSz],\n );\n\n const txData = encodeFunctionData({\n abi: BASE_STRATEGY_ABI,\n functionName: 'updateParams',\n args: [actionData],\n });\n\n const txHash = await client.sendTransaction({\n to: this.config.strategyClone,\n data: txData,\n });\n\n console.error(chalk.green(`[LIVE] Transaction sent: ${txHash}`));\n\n return {\n txHash,\n executedPrice: currentPrice,\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// ── HyperCore conversion helpers ──\n\n/** Convert a USD price (e.g. 3000.50) to HyperCore uint64 format (6-decimal fixed point). */\nfunction priceToUint64(priceUsd: number): bigint {\n return BigInt(Math.round(priceUsd * 1e6));\n}\n\n/** Convert a token quantity to HyperCore uint64 format using the asset's szDecimals.\n * HyperCore szDecimals varies per asset — MUST be fetched from the meta API. */\nfunction sizeToUint64(quantity: number, szDecimals: number): bigint {\n return BigInt(Math.round(quantity * 10 ** szDecimals));\n}\n\n/** Fetch szDecimals for an asset index from Hyperliquid's meta API.\n * Caches per session to avoid repeated calls. */\nconst szDecimalsCache = new Map<number, number>();\n\nasync function getSzDecimals(assetIndex: number): Promise<number> {\n if (szDecimalsCache.has(assetIndex)) return szDecimalsCache.get(assetIndex)!;\n\n try {\n const res = await fetch('https://api.hyperliquid.xyz/info', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ type: 'meta' }),\n });\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n const meta = await res.json() as { universe: Array<{ szDecimals: number }> };\n // Cache all assets from the response\n for (let i = 0; i < meta.universe.length; i++) {\n szDecimalsCache.set(i, meta.universe[i]!.szDecimals);\n }\n } catch (err) {\n console.error(`Failed to fetch szDecimals from Hyperliquid meta API: ${err}`);\n }\n\n const dec = szDecimalsCache.get(assetIndex);\n if (dec === undefined) {\n throw new Error(`Unknown asset index ${assetIndex} — cannot determine szDecimals. Check Hyperliquid meta API.`);\n }\n return dec;\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/** 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 // 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};\n\nexport class PortfolioTracker {\n private statePath: string;\n private historyPath: string;\n private state: PortfolioState;\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 = { ...DEFAULT_PORTFOLIO };\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 ...DEFAULT_PORTFOLIO,\n totalValue: vaultValueUsd,\n cash: vaultValueUsd,\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 = { ...DEFAULT_PORTFOLIO };\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 = { ...DEFAULT_PORTFOLIO };\n return this.state;\n }\n }\n\n this.state = parsed;\n } catch {\n // File doesn't exist or is invalid — start fresh\n this.state = { ...DEFAULT_PORTFOLIO };\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 await this.load();\n\n const fullPosition: Position = {\n ...position,\n addCount: position.addCount ?? 0,\n lastAddTimestamp: position.lastAddTimestamp ?? position.entryTimestamp,\n pnlPercent: 0,\n pnlUsd: 0,\n };\n\n this.state.positions.push(fullPosition);\n this.state.cash -= position.quantity * position.entryPrice;\n this.state.totalValue = this.state.cash + this.state.positions.reduce(\n (sum, p) => sum + p.quantity * p.currentPrice,\n 0,\n );\n\n await this.save(this.state);\n return fullPosition;\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 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 // Paper-trading simplification: debit full notional on cash regardless\n // of direction. For longs this is correct (you buy tokens). For shorts\n // on a perp venue the correct model is margin (a fraction of notional),\n // not full notional. This module does not yet model leverage / margin —\n // when real perp accounting lands, branch on `side` here and in\n // openPosition / closePosition together.\n this.state.cash -= addPrice * addQuantity;\n this.state.totalValue = this.state.cash + this.state.positions.reduce(\n (sum, p) => sum + p.quantity * p.currentPrice,\n 0,\n );\n\n await this.save(this.state);\n return updated;\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 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 this.state.positions.splice(idx, 1);\n this.state.cash += pos.quantity * exitPrice;\n this.state.dailyPnl += pnlUsd;\n this.state.weeklyPnl += pnlUsd;\n this.state.monthlyPnl += pnlUsd;\n this.state.totalValue = this.state.cash + this.state.positions.reduce(\n (sum, p) => sum + p.quantity * p.currentPrice,\n 0,\n );\n\n await this.save(this.state);\n\n return { pnl: pnlUsd, pnlPercent, duration };\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 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 = this.state.cash + this.state.positions.reduce(\n (sum, p) => sum + p.quantity * p.currentPrice,\n 0,\n );\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}\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}\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 /** Stepped profit-lock table: each [trigger, lock] pair means \"if PnL% crosses\n * trigger, ratchet stop to lock in at least lockPct of gain\".\n * Entries applied in order; last matching entry wins per cycle. */\n profitLockSteps: Array<{ trigger: number; lock: number }>;\n dailyLossLimit: number;\n weeklyLossLimit: number;\n monthlyLossLimit: number;\n maxSlippage: Record<string, number>;\n riskPerTrade: number;\n}\n\n/**\n * Default risk config.\n *\n * `trailingStopPct` / `breakevenTriggerPct` / `profitLockSteps` default to\n * 0 / 0 / [] — existing users upgrading the CLI keep prior behavior\n * (SELL signal + static stop-loss + take-profit + time-stop only).\n *\n * To enable active trailing, users explicitly opt in via config:\n * sherwood agent config --set trailingStopPct=0.05\n * sherwood agent config --set breakevenTriggerPct=0.02\n *\n * Recommended aggressive defaults are preserved as RECOMMENDED_TRAILING_CONFIG\n * below for one-shot enablement.\n */\nexport const DEFAULT_RISK_CONFIG: RiskConfig = {\n maxPortfolioRisk: 0.15,\n maxSinglePosition: 0.10,\n maxCorrelatedExposure: 0.20,\n maxConcurrentTrades: 8,\n hardStopPercent: 0.05, // 5% hard stop — short-term trades shouldn't bleed past this\n trailingStopAtr: 1.5,\n trailingStopPct: 0, // OFF — opt in via config\n breakevenTriggerPct: 0, // OFF — opt in via config\n profitLockSteps: [], // OFF — opt in via config\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.025, // 2.5% trail — tighter for short-term\n breakevenTriggerPct: 0.015, // move to breakeven after +1.5% gain\n profitLockSteps: [\n { trigger: 0.02, lock: 0.005 }, // after +2%, lock in +0.5%\n { trigger: 0.04, lock: 0.02 }, // after +4%, lock in +2%\n { trigger: 0.06, lock: 0.04 }, // after +6%, lock in +4% (near TP)\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\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 /** 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 // 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 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\n if (sizeUsd > this.portfolio.cash) {\n return { allowed: false, reason: `Insufficient cash: need $${sizeUsd.toFixed(2)}, 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 upward each cycle based on current prices.\n * Applies three independent mechanisms, whichever gives the highest 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. Profit-lock ratchet: stepped table (e.g. after +5% gain, lock in +2%).\n * Each triggered step moves stop to entryPrice × (1 + lock).\n * Prevents giving back earned profit on mean reversion.\n *\n * 3. Percent-trail: stop = max(currentStop, currentPrice × (1 - trailingStopPct)).\n * Continuous trail — tracks new highs as price runs.\n *\n * All mechanisms respect the \"stops never move down\" invariant.\n * Returns an array of updated positions (pure — does not mutate input).\n *\n * No ATR needed — works from price alone, so it can run every loop cycle\n * without re-computing technicals. Use updateStopLosses() when ATR is\n * already available (keeps tighter trails on volatile assets).\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 // Direction-aware PnL\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 move DOWN (tighter = lower price).\n // \"Never loosen\" = never move stop UP (higher) for shorts.\n\n // 1. Breakeven: move stop down to entry (from above)\n if (this.config.breakevenTriggerPct > 0 && pnlPct >= this.config.breakevenTriggerPct) {\n newStop = Math.min(newStop, pos.entryPrice);\n }\n\n // 2. Profit-lock: lock in gains by moving stop further down\n for (const step of this.config.profitLockSteps) {\n if (pnlPct >= step.trigger) {\n const lockedStop = pos.entryPrice * (1 - step.lock);\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) {\n return { ...pos, stopLoss: newStop, trailingStop: newStop };\n }\n } else {\n // For LONGS: stops move UP (tighter = higher price).\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. Profit-lock steps\n for (const step of this.config.profitLockSteps) {\n if (pnlPct >= step.trigger) {\n const lockedStop = pos.entryPrice * (1 + step.lock);\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) {\n return { ...pos, stopLoss: newStop, trailingStop: newStop };\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 newTrailingStop = pos.currentPrice - trailingDistance;\n\n // Only move trailing stop up, never down\n const currentTrailing = pos.trailingStop ?? pos.stopLoss;\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 if (holdingHours > 48 && 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\n const pvColor = result.dailyPnl >= 0 ? chalk.green : chalk.red;\n const pnlStr = result.dailyPnl >= 0\n ? `+$${result.dailyPnl.toFixed(2)}`\n : `-$${Math.abs(result.dailyPnl).toFixed(2)}`;\n lines.push(` │ Portfolio: $${result.portfolioValue.toFixed(2)} Daily PnL: ${pvColor(pnlStr)}`);\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 * 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 } 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 const STOP_LOSS_PCT = 0.03; // 3% stop — matches executor.ts\n const TAKE_PROFIT_PCT = 0.06; // 6% TP (2:1 R:R)\n const TIME_STOP_HOURS = 48; // 48h dead-money exit\n const TRAIL_PCT = this.config.trailingStopPct ?? 0.025; // 2.5% trail\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 weightMap). */\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 // Strategy-level signals mapped to their category\n breakoutOnChain: \"technical\",\n meanReversion: \"technical\",\n multiTimeframe: \"technical\",\n dexFlow: \"onchain\",\n fundingRate: \"fundamental\",\n tvlMomentum: \"fundamental\",\n sentimentContrarian: \"sentiment\",\n twitterSentiment: \"sentiment\",\n tokenUnlock: \"event\",\n hyperliquidFlow: \"onchain\",\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","data","mkdir","readFile","writeFile","join","homedir","BASE_URL","sharedLastCallTime","mkdir","readFile","writeFile","join","homedir","mkdir","join","homedir","mkdir","readFile","writeFile","rename","cutoff","mkdir","readFile","writeFile","join","homedir","readFile","writeFile","mkdir","join","homedir","join","homedir","readFile","mkdir","writeFile","readFile","writeFile","mkdir","join","homedir","join","homedir","readFile","mkdir","writeFile","readFile","writeFile","mkdir","join","homedir","createHash","marketData","mkdir","readFile","writeFile","join","homedir","HYPERLIQUID_BASE","chalk","mkdir","appendFile","join","homedir","chalk","chalk","STOP_LOSS_PCT","mkdir","readFile","writeFile","rename","dirname","join","homedir","chalk","http","hyperevm","hyperevmTestnet","BASE_STRATEGY_ABI","chalk","chalk","chalk","join","homedir","mkdir","chalk","appendFile","chalk","chalk","STOP_LOSS_PCT","TAKE_PROFIT_PCT","TIME_STOP_HOURS","TRAIL_PCT","mkdir","writeFile","join","homedir","WEIGHT_PROFILES","join","homedir","mkdir","writeFile","readFile","mkdir","writeFile","join","homedir","readFile","join","homedir","WEIGHT_PROFILES","saveResults","mkdir","writeFile","fearGreed","readFile","readFile","spinner","alerts","chalk","join","homedir","readFile","mkdir","writeFile","tokens"]}