@elizaos/plugin-social-alpha 2.0.3-beta.5 → 2.0.3-beta.7

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 (151) hide show
  1. package/dist/clients.d.ts +354 -0
  2. package/dist/clients.d.ts.map +1 -0
  3. package/dist/clients.js +670 -0
  4. package/dist/clients.js.map +1 -0
  5. package/dist/config.d.ts +144 -0
  6. package/dist/config.d.ts.map +1 -0
  7. package/dist/config.js +122 -0
  8. package/dist/config.js.map +1 -0
  9. package/dist/events.d.ts +5 -0
  10. package/dist/events.d.ts.map +1 -0
  11. package/dist/events.js +426 -0
  12. package/dist/events.js.map +1 -0
  13. package/dist/frontend/LeaderboardView.helpers.d.ts +6 -0
  14. package/dist/frontend/LeaderboardView.helpers.d.ts.map +1 -0
  15. package/dist/frontend/LeaderboardView.helpers.js +59 -0
  16. package/dist/frontend/LeaderboardView.helpers.js.map +1 -0
  17. package/dist/frontend/SocialAlphaSpatialView.d.ts +52 -0
  18. package/dist/frontend/SocialAlphaSpatialView.d.ts.map +1 -0
  19. package/dist/frontend/SocialAlphaSpatialView.js +72 -0
  20. package/dist/frontend/SocialAlphaSpatialView.js.map +1 -0
  21. package/dist/frontend/SocialAlphaView.d.ts +35 -0
  22. package/dist/frontend/SocialAlphaView.d.ts.map +1 -0
  23. package/dist/frontend/SocialAlphaView.js +125 -0
  24. package/dist/frontend/SocialAlphaView.js.map +1 -0
  25. package/dist/index.d.ts +24 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +73 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/mockPriceService.d.ts +22 -0
  30. package/dist/mockPriceService.d.ts.map +1 -0
  31. package/dist/mockPriceService.js +21 -0
  32. package/dist/mockPriceService.js.map +1 -0
  33. package/dist/providers/socialAlphaProvider.d.ts +15 -0
  34. package/dist/providers/socialAlphaProvider.d.ts.map +1 -0
  35. package/dist/providers/socialAlphaProvider.js +261 -0
  36. package/dist/providers/socialAlphaProvider.js.map +1 -0
  37. package/dist/register-terminal-view.d.ts +15 -0
  38. package/dist/register-terminal-view.d.ts.map +1 -0
  39. package/dist/register-terminal-view.js +21 -0
  40. package/dist/register-terminal-view.js.map +1 -0
  41. package/dist/register.d.ts +10 -0
  42. package/dist/register.d.ts.map +1 -0
  43. package/dist/register.js +5 -0
  44. package/dist/register.js.map +1 -0
  45. package/dist/reports.d.ts +57 -0
  46. package/dist/reports.d.ts.map +1 -0
  47. package/dist/reports.js +455 -0
  48. package/dist/reports.js.map +1 -0
  49. package/dist/routes.d.ts +3 -0
  50. package/dist/routes.d.ts.map +1 -0
  51. package/dist/routes.js +59 -0
  52. package/dist/routes.js.map +1 -0
  53. package/dist/schemas.d.ts +151 -0
  54. package/dist/schemas.d.ts.map +1 -0
  55. package/dist/schemas.js +258 -0
  56. package/dist/schemas.js.map +1 -0
  57. package/dist/service.d.ts +306 -0
  58. package/dist/service.d.ts.map +1 -0
  59. package/dist/service.js +3078 -0
  60. package/dist/service.js.map +1 -0
  61. package/dist/services/balancedTrustScoreCalculator.d.ts +61 -0
  62. package/dist/services/balancedTrustScoreCalculator.d.ts.map +1 -0
  63. package/dist/services/balancedTrustScoreCalculator.js +207 -0
  64. package/dist/services/balancedTrustScoreCalculator.js.map +1 -0
  65. package/dist/services/historicalPriceService.d.ts +59 -0
  66. package/dist/services/historicalPriceService.d.ts.map +1 -0
  67. package/dist/services/historicalPriceService.js +291 -0
  68. package/dist/services/historicalPriceService.js.map +1 -0
  69. package/dist/services/index.d.ts +12 -0
  70. package/dist/services/index.d.ts.map +1 -0
  71. package/dist/services/index.js +17 -0
  72. package/dist/services/index.js.map +1 -0
  73. package/dist/services/priceEnrichmentService.d.ts +109 -0
  74. package/dist/services/priceEnrichmentService.d.ts.map +1 -0
  75. package/dist/services/priceEnrichmentService.js +780 -0
  76. package/dist/services/priceEnrichmentService.js.map +1 -0
  77. package/dist/services/simulationActorsV2.d.ts +54 -0
  78. package/dist/services/simulationActorsV2.d.ts.map +1 -0
  79. package/dist/services/simulationActorsV2.js +362 -0
  80. package/dist/services/simulationActorsV2.js.map +1 -0
  81. package/dist/services/simulationRunner.d.ts +113 -0
  82. package/dist/services/simulationRunner.d.ts.map +1 -0
  83. package/dist/services/simulationRunner.js +771 -0
  84. package/dist/services/simulationRunner.js.map +1 -0
  85. package/dist/services/tokenSimulationService.d.ts +34 -0
  86. package/dist/services/tokenSimulationService.d.ts.map +1 -0
  87. package/dist/services/tokenSimulationService.js +297 -0
  88. package/dist/services/tokenSimulationService.js.map +1 -0
  89. package/dist/services/trustScoreOptimizer.d.ts +110 -0
  90. package/dist/services/trustScoreOptimizer.d.ts.map +1 -0
  91. package/dist/services/trustScoreOptimizer.js +635 -0
  92. package/dist/services/trustScoreOptimizer.js.map +1 -0
  93. package/dist/simulationActors.d.ts +35 -0
  94. package/dist/simulationActors.d.ts.map +1 -0
  95. package/dist/simulationActors.js +160 -0
  96. package/dist/simulationActors.js.map +1 -0
  97. package/dist/social-alpha-view-bundle.d.ts +2 -0
  98. package/dist/social-alpha-view-bundle.d.ts.map +1 -0
  99. package/dist/social-alpha-view-bundle.js +5 -0
  100. package/dist/social-alpha-view-bundle.js.map +1 -0
  101. package/dist/types.d.ts +937 -0
  102. package/dist/types.d.ts.map +1 -0
  103. package/dist/types.js +46 -0
  104. package/dist/types.js.map +1 -0
  105. package/dist/views/brand/background/clouds_background.jpg +0 -0
  106. package/dist/views/brand/banners/eliza_banner.svg +20 -0
  107. package/dist/views/brand/banners/elizacloud_banner.svg +20 -0
  108. package/dist/views/brand/banners/elizaos_banner.svg +20 -0
  109. package/dist/views/brand/concepts/billboard_concept_1200.jpg +0 -0
  110. package/dist/views/brand/concepts/chibi_usb_concept_900.jpg +0 -0
  111. package/dist/views/brand/concepts/concept_minipc_900.jpg +0 -0
  112. package/dist/views/brand/concepts/concept_phone_800.jpg +0 -0
  113. package/dist/views/brand/concepts/concept_usbdrive_900.jpg +0 -0
  114. package/dist/views/brand/favicons/android-chrome-192x192.png +0 -0
  115. package/dist/views/brand/favicons/android-chrome-512x512.png +0 -0
  116. package/dist/views/brand/favicons/apple-touch-icon.png +0 -0
  117. package/dist/views/brand/favicons/favicon-16x16.png +0 -0
  118. package/dist/views/brand/favicons/favicon-32x32.png +0 -0
  119. package/dist/views/brand/favicons/favicon.ico +0 -0
  120. package/dist/views/brand/favicons/favicon.svg +17 -0
  121. package/dist/views/brand/logos/elizaOS_text_black.svg +3 -0
  122. package/dist/views/brand/logos/elizaOS_text_white.svg +3 -0
  123. package/dist/views/brand/logos/eliza_logotext.svg +26 -0
  124. package/dist/views/brand/logos/eliza_logotext_black.svg +26 -0
  125. package/dist/views/brand/logos/eliza_text_black.svg +3 -0
  126. package/dist/views/brand/logos/eliza_text_white.svg +3 -0
  127. package/dist/views/brand/logos/elizacloud_logotext.svg +26 -0
  128. package/dist/views/brand/logos/elizacloud_logotext_black.svg +26 -0
  129. package/dist/views/brand/logos/elizacloud_text_black.svg +3 -0
  130. package/dist/views/brand/logos/elizacloud_text_white.svg +3 -0
  131. package/dist/views/brand/logos/elizaos_logotext.svg +26 -0
  132. package/dist/views/brand/logos/elizaos_logotext_black.svg +26 -0
  133. package/dist/views/brand/logos/logo_blue_blackbg.svg +18 -0
  134. package/dist/views/brand/logos/logo_blue_nobg.svg +17 -0
  135. package/dist/views/brand/logos/logo_orange_blackbg.svg +18 -0
  136. package/dist/views/brand/logos/logo_orange_nobg.svg +17 -0
  137. package/dist/views/brand/logos/logo_white_blackbg.svg +25 -0
  138. package/dist/views/brand/logos/logo_white_bluebg.svg +25 -0
  139. package/dist/views/brand/logos/logo_white_graybg.svg +18 -0
  140. package/dist/views/brand/logos/logo_white_nobg.svg +24 -0
  141. package/dist/views/brand/logos/logo_white_orangebg.svg +25 -0
  142. package/dist/views/brand/ogembeds/eliza_ogembed.png +0 -0
  143. package/dist/views/brand/ogembeds/eliza_ogembed.svg +20 -0
  144. package/dist/views/brand/ogembeds/elizacloud_ogembed.png +0 -0
  145. package/dist/views/brand/ogembeds/elizacloud_ogembed.svg +20 -0
  146. package/dist/views/brand/ogembeds/elizaos_ogembed.png +0 -0
  147. package/dist/views/brand/ogembeds/elizaos_ogembed.svg +20 -0
  148. package/dist/views/bundle.js +268 -0
  149. package/dist/views/bundle.js.map +1 -0
  150. package/dist/views/site.webmanifest +19 -0
  151. package/package.json +5 -5
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/services/priceEnrichmentService.ts"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { IAgentRuntime } from \"@elizaos/core\";\nimport { BirdeyeClient, DexscreenerClient } from \"../clients.js\";\nimport type { TokenAPIData } from \"../types.js\";\nimport { SupportedChain } from \"../types.js\";\nimport {\n\ttype HistoricalPriceData,\n\tHistoricalPriceService,\n} from \"./historicalPriceService.js\";\n\nexport interface TradingCall {\n\tcallId: string;\n\toriginalMessageId: string;\n\tuserId: string;\n\tusername: string;\n\ttimestamp: number;\n\tcontent: string;\n\ttokenMentioned?: string;\n\tcaMentioned?: string;\n\tchain: string;\n\tsentiment: \"positive\" | \"negative\" | \"neutral\";\n\tconviction: \"NONE\" | \"LOW\" | \"MEDIUM\" | \"HIGH\";\n\tllmReasoning: string;\n\tcertainty: \"low\" | \"medium\" | \"high\";\n\tfileSource: string;\n}\n\nexport interface EnrichedTradingCall extends TradingCall {\n\tresolvedToken?: {\n\t\taddress: string;\n\t\tsymbol: string;\n\t\tname: string;\n\t\tchain: SupportedChain;\n\t};\n\tpriceData?: {\n\t\tcalledPrice: number;\n\t\tcalledPriceTimestamp: number;\n\t\tbestPrice: number;\n\t\tbestPriceTimestamp: number;\n\t\tworstPrice: number;\n\t\tworstPriceTimestamp: number;\n\t\tidealProfitLoss: number;\n\t\tidealProfitLossPercent: number;\n\t\twindowDays: number;\n\t};\n\tenrichmentStatus: \"pending\" | \"success\" | \"failed\";\n\tenrichmentError?: string;\n\tenrichedAt?: number;\n}\n\nexport interface TrustScore {\n\tuserId: string;\n\tusername: string;\n\ttotalCalls: number;\n\tsuccessfulCalls: number;\n\tfailedCalls: number;\n\taverageProfitLoss: number;\n\taverageProfitLossPercent: number;\n\ttrustScore: number;\n\tconsistency: number;\n\trecencyWeight: number;\n\tconvictionAccuracy: number;\n\tlastUpdated: number;\n}\n\nexport class PriceEnrichmentService {\n\tprivate birdeyeClient: BirdeyeClient;\n\tprivate dexscreenerClient: DexscreenerClient;\n\tprivate historicalPriceService: HistoricalPriceService;\n\n\tconstructor(runtime: IAgentRuntime) {\n\t\tthis.birdeyeClient = BirdeyeClient.createFromRuntime(runtime);\n\t\tthis.dexscreenerClient = DexscreenerClient.createFromRuntime(runtime);\n\t\tthis.historicalPriceService = new HistoricalPriceService(runtime);\n\t}\n\n\t/**\n\t * Load batch files from the cache directory\n\t */\n\tasync loadBatchFiles(batchCacheDir: string): Promise<TradingCall[]> {\n\t\tconst allCalls: TradingCall[] = [];\n\n\t\ttry {\n\t\t\tconst files = await fs.readdir(batchCacheDir);\n\t\t\tconst batchFiles = files.filter(\n\t\t\t\t(file) => file.startsWith(\"chat_\") && file.endsWith(\".json\"),\n\t\t\t);\n\n\t\t\tfor (const file of batchFiles) {\n\t\t\t\ttry {\n\t\t\t\t\tconst filePath = path.join(batchCacheDir, file);\n\t\t\t\t\tconst content = await fs.readFile(filePath, \"utf-8\");\n\t\t\t\t\tconst batchData = JSON.parse(content) as TradingCall[];\n\t\t\t\t\tallCalls.push(...batchData);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconsole.error(`Error loading batch file ${file}:`, error);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconsole.log(\n\t\t\t\t`Loaded ${allCalls.length} trading calls from ${batchFiles.length} batch files`,\n\t\t\t);\n\t\t\treturn allCalls;\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Error loading batch files:\", error);\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Resolve contract address or token mention to standardized token info\n\t */\n\tasync resolveToken(call: TradingCall): Promise<{\n\t\taddress: string;\n\t\tsymbol: string;\n\t\tname: string;\n\t\tchain: SupportedChain;\n\t} | null> {\n\t\ttry {\n\t\t\t// If we have a contract address, use it directly\n\t\t\tif (call.caMentioned) {\n\t\t\t\tconst tokenData = await this.getTokenInfo(\n\t\t\t\t\tcall.caMentioned,\n\t\t\t\t\tthis.chainStringToEnum(call.chain),\n\t\t\t\t);\n\t\t\t\tif (tokenData) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\taddress: call.caMentioned,\n\t\t\t\t\t\tsymbol: tokenData.symbol || \"UNKNOWN\",\n\t\t\t\t\t\tname: tokenData.name || \"Unknown Token\",\n\t\t\t\t\t\tchain: this.chainStringToEnum(call.chain),\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If we have a token mention, try to resolve it\n\t\t\tif (call.tokenMentioned) {\n\t\t\t\tconst resolved = await this.searchTokenBySymbol(\n\t\t\t\t\tcall.tokenMentioned,\n\t\t\t\t\tthis.chainStringToEnum(call.chain),\n\t\t\t\t);\n\t\t\t\treturn resolved;\n\t\t\t}\n\n\t\t\treturn null;\n\t\t} catch (error) {\n\t\t\tconsole.error(`Error resolving token for call ${call.callId}:`, error);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Get historical price data for a token within a time window\n\t */\n\tasync getPriceDataInWindow(\n\t\ttokenAddress: string,\n\t\tchain: SupportedChain,\n\t\tcallTimestamp: number,\n\t\twindowDays: number = 30,\n\t): Promise<{\n\t\tcalledPrice: number;\n\t\tbestPrice: number;\n\t\tbestPriceTimestamp: number;\n\t\tworstPrice: number;\n\t\tworstPriceTimestamp: number;\n\t} | null> {\n\t\ttry {\n\t\t\tconst windowEnd = callTimestamp + windowDays * 24 * 60 * 60 * 1000;\n\t\t\tconst historicalData =\n\t\t\t\tchain === SupportedChain.SOLANA\n\t\t\t\t\t? await this.historicalPriceService.fetchBirdeyeHistoricalPrices(\n\t\t\t\t\t\t\ttokenAddress,\n\t\t\t\t\t\t\tcallTimestamp,\n\t\t\t\t\t\t\twindowEnd,\n\t\t\t\t\t\t)\n\t\t\t\t\t: await this.historicalPriceService.fetchDexscreenerHistoricalPrices(\n\t\t\t\t\t\t\ttokenAddress,\n\t\t\t\t\t\t\tchain,\n\t\t\t\t\t\t\tcallTimestamp,\n\t\t\t\t\t\t\twindowEnd,\n\t\t\t\t\t\t);\n\n\t\t\tif (!historicalData) return null;\n\n\t\t\tconst calledPrice =\n\t\t\t\tthis.historicalPriceService.getPriceAtTimestamp(\n\t\t\t\t\thistoricalData,\n\t\t\t\t\tcallTimestamp,\n\t\t\t\t) ??\n\t\t\t\thistoricalData.firstPrice ??\n\t\t\t\thistoricalData.lastPrice;\n\t\t\tconst bestPrice = this.historicalPriceService.getMaxPriceInWindow(\n\t\t\t\thistoricalData,\n\t\t\t\tcallTimestamp,\n\t\t\t\twindowEnd,\n\t\t\t);\n\t\t\tconst worstPrice = this.getMinPriceInWindow(\n\t\t\t\thistoricalData,\n\t\t\t\tcallTimestamp,\n\t\t\t\twindowEnd,\n\t\t\t);\n\n\t\t\tif (calledPrice === undefined || !bestPrice || !worstPrice) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tcalledPrice,\n\t\t\t\tbestPrice: bestPrice.price,\n\t\t\t\tbestPriceTimestamp: bestPrice.timestamp,\n\t\t\t\tworstPrice: worstPrice.price,\n\t\t\t\tworstPriceTimestamp: worstPrice.timestamp,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tconsole.error(`Error getting price data for ${tokenAddress}:`, error);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Enrich a single trading call with price data\n\t */\n\tasync enrichCall(call: TradingCall): Promise<EnrichedTradingCall> {\n\t\tconst enrichedCall: EnrichedTradingCall = {\n\t\t\t...call,\n\t\t\tenrichmentStatus: \"pending\",\n\t\t};\n\n\t\ttry {\n\t\t\t// Step 1: Resolve token information\n\t\t\tconst resolvedToken = await this.resolveToken(call);\n\t\t\tif (!resolvedToken) {\n\t\t\t\tenrichedCall.enrichmentStatus = \"failed\";\n\t\t\t\tenrichedCall.enrichmentError = \"Could not resolve token\";\n\t\t\t\treturn enrichedCall;\n\t\t\t}\n\n\t\t\tenrichedCall.resolvedToken = resolvedToken;\n\n\t\t\t// Step 2: Get price data\n\t\t\tconst windowDays = call.sentiment === \"negative\" ? 14 : 30; // Shorter window for FUD\n\t\t\tconst priceData = await this.getPriceDataInWindow(\n\t\t\t\tresolvedToken.address,\n\t\t\t\tresolvedToken.chain,\n\t\t\t\tcall.timestamp,\n\t\t\t\twindowDays,\n\t\t\t);\n\n\t\t\tif (!priceData) {\n\t\t\t\tenrichedCall.enrichmentStatus = \"failed\";\n\t\t\t\tenrichedCall.enrichmentError = \"Could not get price data\";\n\t\t\t\treturn enrichedCall;\n\t\t\t}\n\n\t\t\t// Step 3: Calculate profit/loss metrics\n\t\t\tconst idealProfitLoss =\n\t\t\t\tcall.sentiment === \"positive\"\n\t\t\t\t\t? priceData.bestPrice - priceData.calledPrice\n\t\t\t\t\t: priceData.calledPrice - priceData.worstPrice; // For negative sentiment, profit is avoiding loss\n\n\t\t\tconst idealProfitLossPercent =\n\t\t\t\t(idealProfitLoss / priceData.calledPrice) * 100;\n\n\t\t\tenrichedCall.priceData = {\n\t\t\t\tcalledPrice: priceData.calledPrice,\n\t\t\t\tcalledPriceTimestamp: call.timestamp,\n\t\t\t\tbestPrice: priceData.bestPrice,\n\t\t\t\tbestPriceTimestamp: priceData.bestPriceTimestamp,\n\t\t\t\tworstPrice: priceData.worstPrice,\n\t\t\t\tworstPriceTimestamp: priceData.worstPriceTimestamp,\n\t\t\t\tidealProfitLoss,\n\t\t\t\tidealProfitLossPercent,\n\t\t\t\twindowDays,\n\t\t\t};\n\n\t\t\tenrichedCall.enrichmentStatus = \"success\";\n\t\t\tenrichedCall.enrichedAt = Date.now();\n\t\t} catch (error) {\n\t\t\tconsole.error(`Error enriching call ${call.callId}:`, error);\n\t\t\tenrichedCall.enrichmentStatus = \"failed\";\n\t\t\tenrichedCall.enrichmentError =\n\t\t\t\terror instanceof Error ? error.message : \"Unknown error\";\n\t\t}\n\n\t\treturn enrichedCall;\n\t}\n\n\t/**\n\t * Process all calls in batches and save enriched data\n\t */\n\tasync enrichAllCalls(\n\t\tbatchCacheDir: string,\n\t\toutputDir: string,\n\t\tbatchSize: number = 10,\n\t): Promise<void> {\n\t\tconst calls = await this.loadBatchFiles(batchCacheDir);\n\t\tconst enrichedCalls: EnrichedTradingCall[] = [];\n\n\t\tlet successCount = 0;\n\t\tlet failureCount = 0;\n\t\tlet resolvedTokenCount = 0;\n\n\t\tconsole.log(\n\t\t\t`Starting enrichment of ${calls.length} calls in batches of ${batchSize}`,\n\t\t);\n\n\t\tconst startTime = Date.now();\n\n\t\t// Process in batches to avoid rate limits\n\t\tfor (let i = 0; i < calls.length; i += batchSize) {\n\t\t\tconst batch = calls.slice(i, i + batchSize);\n\t\t\tconst batchNumber = Math.floor(i / batchSize) + 1;\n\t\t\tconst totalBatches = Math.ceil(calls.length / batchSize);\n\n\t\t\tconsole.log(\n\t\t\t\t`Processing batch ${batchNumber}/${totalBatches} (${((batchNumber / totalBatches) * 100).toFixed(1)}%)`,\n\t\t\t);\n\n\t\t\tconst batchPromises = batch.map((call) => this.enrichCall(call));\n\t\t\tconst enrichedBatch = await Promise.all(batchPromises);\n\t\t\tenrichedCalls.push(...enrichedBatch);\n\n\t\t\t// Count successes and failures in this batch\n\t\t\tenrichedBatch.forEach((call) => {\n\t\t\t\tif (call.enrichmentStatus === \"success\") {\n\t\t\t\t\tsuccessCount++;\n\t\t\t\t\tif (call.resolvedToken) resolvedTokenCount++;\n\t\t\t\t} else {\n\t\t\t\t\tfailureCount++;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Save intermediate results every 100 batches or at the end\n\t\t\tif (batchNumber % 100 === 0 || batchNumber === totalBatches) {\n\t\t\t\tconst outputFile = path.join(\n\t\t\t\t\toutputDir,\n\t\t\t\t\t`enriched_batch_${Math.floor(i / batchSize)}.json`,\n\t\t\t\t);\n\t\t\t\tawait fs.writeFile(outputFile, JSON.stringify(enrichedBatch, null, 2));\n\n\t\t\t\t// Show progress stats\n\t\t\t\tconst elapsed = (Date.now() - startTime) / 1000 / 60; // minutes\n\t\t\t\tconst rate = batchNumber / elapsed; // batches per minute\n\t\t\t\tconst remaining = totalBatches - batchNumber;\n\t\t\t\tconst eta = remaining / rate; // minutes\n\n\t\t\t\tconsole.log(\n\t\t\t\t\t`📊 Progress: ${successCount} success, ${failureCount} failed, ${resolvedTokenCount} tokens resolved`,\n\t\t\t\t);\n\t\t\t\tconsole.log(\n\t\t\t\t\t`⏱️ Time: ${elapsed.toFixed(1)}min elapsed, ~${eta.toFixed(1)}min remaining`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Rate limiting delay - shorter for better progress\n\t\t\tawait new Promise((resolve) => setTimeout(resolve, 500));\n\t\t}\n\n\t\t// Save complete enriched dataset\n\t\tconst completeOutputFile = path.join(\n\t\t\toutputDir,\n\t\t\t\"enriched_calls_complete.json\",\n\t\t);\n\t\tawait fs.writeFile(\n\t\t\tcompleteOutputFile,\n\t\t\tJSON.stringify(enrichedCalls, null, 2),\n\t\t);\n\n\t\tconst totalTime = (Date.now() - startTime) / 1000 / 60;\n\t\tconsole.log(`\\n✅ Enrichment complete in ${totalTime.toFixed(2)} minutes!`);\n\t\tconsole.log(\n\t\t\t`📈 Results: ${successCount} success (${((successCount / calls.length) * 100).toFixed(1)}%), ${failureCount} failed`,\n\t\t);\n\t\tconsole.log(\n\t\t\t`🎯 Tokens resolved: ${resolvedTokenCount} (${((resolvedTokenCount / calls.length) * 100).toFixed(1)}%)`,\n\t\t);\n\t\tconsole.log(\n\t\t\t`💾 Saved ${enrichedCalls.length} enriched calls to ${completeOutputFile}`,\n\t\t);\n\t}\n\n\t/**\n\t * Calculate trust scores for all users\n\t */\n\tasync calculateTrustScores(\n\t\tenrichedCalls: EnrichedTradingCall[],\n\t): Promise<TrustScore[]> {\n\t\tconst userStats = new Map<\n\t\t\tstring,\n\t\t\t{\n\t\t\t\tcalls: EnrichedTradingCall[];\n\t\t\t\ttotalProfitLoss: number;\n\t\t\t\ttotalProfitLossPercent: number;\n\t\t\t\tsuccessfulCalls: number;\n\t\t\t\tfailedCalls: number;\n\t\t\t}\n\t\t>();\n\n\t\t// Group calls by user\n\t\tfor (const call of enrichedCalls) {\n\t\t\tif (call.enrichmentStatus !== \"success\" || !call.priceData) continue;\n\n\t\t\tif (!userStats.has(call.userId)) {\n\t\t\t\tuserStats.set(call.userId, {\n\t\t\t\t\tcalls: [],\n\t\t\t\t\ttotalProfitLoss: 0,\n\t\t\t\t\ttotalProfitLossPercent: 0,\n\t\t\t\t\tsuccessfulCalls: 0,\n\t\t\t\t\tfailedCalls: 0,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst stats = userStats.get(call.userId);\n\t\t\tif (!stats) continue;\n\t\t\tstats.calls.push(call);\n\t\t\tstats.totalProfitLoss += call.priceData.idealProfitLoss;\n\t\t\tstats.totalProfitLossPercent += call.priceData.idealProfitLossPercent;\n\n\t\t\t// Consider a call successful if it would have been profitable\n\t\t\tif (call.priceData.idealProfitLossPercent > 0) {\n\t\t\t\tstats.successfulCalls++;\n\t\t\t} else {\n\t\t\t\tstats.failedCalls++;\n\t\t\t}\n\t\t}\n\n\t\t// Calculate trust scores\n\t\tconst trustScores: TrustScore[] = [];\n\n\t\tfor (const [userId, stats] of userStats) {\n\t\t\tconst totalCalls = stats.successfulCalls + stats.failedCalls;\n\t\t\tif (totalCalls === 0) continue;\n\n\t\t\tconst averageProfitLoss = stats.totalProfitLoss / totalCalls;\n\t\t\tconst averageProfitLossPercent =\n\t\t\t\tstats.totalProfitLossPercent / totalCalls;\n\t\t\tconst successRate = stats.successfulCalls / totalCalls;\n\n\t\t\t// Calculate consistency (lower standard deviation = higher consistency)\n\t\t\tconst profitLossValues = stats.calls\n\t\t\t\t.map((call) => call.priceData?.idealProfitLossPercent)\n\t\t\t\t.filter((v): v is number => v !== undefined);\n\t\t\tconst variance =\n\t\t\t\tprofitLossValues.reduce(\n\t\t\t\t\t(sum, val) => sum + (val - averageProfitLossPercent) ** 2,\n\t\t\t\t\t0,\n\t\t\t\t) / totalCalls;\n\t\t\tconst consistency = Math.max(0, 100 - Math.sqrt(variance));\n\n\t\t\t// Calculate recency weight (more recent calls weighted higher)\n\t\t\tconst now = Date.now();\n\t\t\tconst recencyWeight =\n\t\t\t\tstats.calls.reduce((sum, call) => {\n\t\t\t\t\tconst daysSince = (now - call.timestamp) / (1000 * 60 * 60 * 24);\n\t\t\t\t\treturn sum + Math.exp(-daysSince / 30); // Exponential decay over 30 days\n\t\t\t\t}, 0) / totalCalls;\n\n\t\t\t// Calculate conviction accuracy\n\t\t\tconst convictionAccuracy = this.calculateConvictionAccuracy(stats.calls);\n\n\t\t\t// Final trust score combining multiple factors\n\t\t\tconst trustScore =\n\t\t\t\tsuccessRate * 40 + // 40% weight on success rate\n\t\t\t\tMath.max(0, Math.min(100, averageProfitLossPercent * 2)) * 30 + // 30% weight on profit %\n\t\t\t\tconsistency * 20 + // 20% weight on consistency\n\t\t\t\tconvictionAccuracy * 10; // 10% weight on conviction accuracy\n\n\t\t\tconst username = stats.calls[0]?.username || \"Unknown\";\n\n\t\t\ttrustScores.push({\n\t\t\t\tuserId,\n\t\t\t\tusername,\n\t\t\t\ttotalCalls,\n\t\t\t\tsuccessfulCalls: stats.successfulCalls,\n\t\t\t\tfailedCalls: stats.failedCalls,\n\t\t\t\taverageProfitLoss,\n\t\t\t\taverageProfitLossPercent,\n\t\t\t\ttrustScore: Math.round(trustScore * 100) / 100,\n\t\t\t\tconsistency: Math.round(consistency * 100) / 100,\n\t\t\t\trecencyWeight: Math.round(recencyWeight * 100) / 100,\n\t\t\t\tconvictionAccuracy: Math.round(convictionAccuracy * 100) / 100,\n\t\t\t\tlastUpdated: Date.now(),\n\t\t\t});\n\t\t}\n\n\t\treturn trustScores.sort((a, b) => b.trustScore - a.trustScore);\n\t}\n\n\t// Helper methods\n\n\tprivate chainStringToEnum(chain: string): SupportedChain {\n\t\tswitch (chain.toUpperCase()) {\n\t\t\tcase \"SOLANA\":\n\t\t\t\treturn SupportedChain.SOLANA;\n\t\t\tcase \"ETHEREUM\":\n\t\t\t\treturn SupportedChain.ETHEREUM;\n\t\t\tcase \"BASE\":\n\t\t\t\treturn SupportedChain.BASE;\n\t\t\tdefault:\n\t\t\t\treturn SupportedChain.UNKNOWN;\n\t\t}\n\t}\n\n\tprivate async getTokenInfo(\n\t\taddress: string,\n\t\tchain: SupportedChain,\n\t): Promise<TokenAPIData | null> {\n\t\ttry {\n\t\t\tif (chain === SupportedChain.SOLANA) {\n\t\t\t\tconst overview = await this.birdeyeClient.fetchTokenOverview(address);\n\t\t\t\treturn {\n\t\t\t\t\tname: overview.name,\n\t\t\t\t\tsymbol: overview.symbol,\n\t\t\t\t\tcurrentPrice: 0, // Will be fetched separately\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// For other chains, use dexscreener\n\t\t\tconst dexData =\n\t\t\t\tawait this.dexscreenerClient.searchForHighestLiquidityPair(address);\n\t\t\tif (dexData) {\n\t\t\t\treturn {\n\t\t\t\t\tname: dexData.baseToken.name,\n\t\t\t\t\tsymbol: dexData.baseToken.symbol,\n\t\t\t\t\tcurrentPrice: parseFloat(dexData.priceUsd),\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn null;\n\t\t} catch (error) {\n\t\t\tconsole.error(`Error getting token info for ${address}:`, error);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate async searchTokenBySymbol(\n\t\tsymbol: string,\n\t\tchain: SupportedChain,\n\t): Promise<{\n\t\taddress: string;\n\t\tsymbol: string;\n\t\tname: string;\n\t\tchain: SupportedChain;\n\t} | null> {\n\t\ttry {\n\t\t\t// First check our static token mappings\n\t\t\tconst staticMapping = this.getStaticTokenMapping(symbol, chain);\n\t\t\tif (staticMapping) {\n\t\t\t\treturn staticMapping;\n\t\t\t}\n\n\t\t\t// Use DexScreener for symbol search across supported chains.\n\t\t\tconst dexscreenerResult = await this.searchTokenOnDexscreener(\n\t\t\t\tsymbol,\n\t\t\t\tchain,\n\t\t\t);\n\t\t\tif (dexscreenerResult) {\n\t\t\t\treturn dexscreenerResult;\n\t\t\t}\n\n\t\t\tconsole.warn(`Could not resolve token symbol: ${symbol} on ${chain}`);\n\t\t\treturn null;\n\t\t} catch (error) {\n\t\t\tconsole.error(`Error searching for token ${symbol}:`, error);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate getMinPriceInWindow(\n\t\thistoricalData: HistoricalPriceData,\n\t\tfromTimestamp: number,\n\t\ttoTimestamp: number,\n\t): { price: number; timestamp: number } | null {\n\t\tconst pricesInWindow = historicalData.priceHistory.filter(\n\t\t\t(point) =>\n\t\t\t\tpoint.timestamp >= fromTimestamp && point.timestamp <= toTimestamp,\n\t\t);\n\n\t\tif (pricesInWindow.length === 0) {\n\t\t\tconst priceAtStart = this.historicalPriceService.getPriceAtTimestamp(\n\t\t\t\thistoricalData,\n\t\t\t\tfromTimestamp,\n\t\t\t);\n\t\t\tconst priceAtEnd = this.historicalPriceService.getPriceAtTimestamp(\n\t\t\t\thistoricalData,\n\t\t\t\ttoTimestamp,\n\t\t\t);\n\n\t\t\tif (priceAtStart !== null && priceAtEnd !== null) {\n\t\t\t\treturn priceAtStart <= priceAtEnd\n\t\t\t\t\t? { price: priceAtStart, timestamp: fromTimestamp }\n\t\t\t\t\t: { price: priceAtEnd, timestamp: toTimestamp };\n\t\t\t}\n\n\t\t\treturn null;\n\t\t}\n\n\t\tlet minPrice = pricesInWindow[0];\n\t\tfor (const point of pricesInWindow) {\n\t\t\tif (point.price < minPrice.price) {\n\t\t\t\tminPrice = point;\n\t\t\t}\n\t\t}\n\n\t\treturn { price: minPrice.price, timestamp: minPrice.timestamp };\n\t}\n\n\tprivate calculateConvictionAccuracy(calls: EnrichedTradingCall[]): number {\n\t\tconst convictionWeights = { NONE: 0, LOW: 1, MEDIUM: 2, HIGH: 3 };\n\t\tlet totalWeightedAccuracy = 0;\n\t\tlet totalWeight = 0;\n\n\t\tfor (const call of calls) {\n\t\t\tif (!call.priceData) continue;\n\n\t\t\tconst weight =\n\t\t\t\tconvictionWeights[call.conviction as keyof typeof convictionWeights] ||\n\t\t\t\t0;\n\t\t\tconst isAccurate = call.priceData.idealProfitLossPercent > 0 ? 1 : 0;\n\n\t\t\ttotalWeightedAccuracy += weight * isAccurate;\n\t\t\ttotalWeight += weight;\n\t\t}\n\n\t\treturn totalWeight > 0 ? (totalWeightedAccuracy / totalWeight) * 100 : 0;\n\t}\n\n\t/**\n\t * Get static token mappings for well-known tokens\n\t */\n\tprivate getStaticTokenMapping(\n\t\tsymbol: string,\n\t\tchain: SupportedChain,\n\t): {\n\t\taddress: string;\n\t\tsymbol: string;\n\t\tname: string;\n\t\tchain: SupportedChain;\n\t} | null {\n\t\tconst cleanSymbol = symbol.toUpperCase();\n\n\t\t// Known tokens on Solana\n\t\tif (chain === SupportedChain.SOLANA) {\n\t\t\tconst knownSolanaTokens: Record<\n\t\t\t\tstring,\n\t\t\t\t{ address: string; name: string }\n\t\t\t> = {\n\t\t\t\tSOL: {\n\t\t\t\t\taddress: \"So11111111111111111111111111111111111111112\",\n\t\t\t\t\tname: \"Solana\",\n\t\t\t\t},\n\t\t\t\tUSDC: {\n\t\t\t\t\taddress: \"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v\",\n\t\t\t\t\tname: \"USD Coin\",\n\t\t\t\t},\n\t\t\t\tUSDT: {\n\t\t\t\t\taddress: \"Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB\",\n\t\t\t\t\tname: \"Tether USD\",\n\t\t\t\t},\n\t\t\t\tWIF: {\n\t\t\t\t\taddress: \"EKpQGSJtjMFqKZ9KQanSqYXRcF8fBopzL7WDb43cuQu2\",\n\t\t\t\t\tname: \"dogwifhat\",\n\t\t\t\t},\n\t\t\t\tBONK: {\n\t\t\t\t\taddress: \"DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263\",\n\t\t\t\t\tname: \"Bonk\",\n\t\t\t\t},\n\t\t\t\tJUP: {\n\t\t\t\t\taddress: \"JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN\",\n\t\t\t\t\tname: \"Jupiter\",\n\t\t\t\t},\n\t\t\t\tRAY: {\n\t\t\t\t\taddress: \"4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R\",\n\t\t\t\t\tname: \"Raydium\",\n\t\t\t\t},\n\t\t\t\tORCA: {\n\t\t\t\t\taddress: \"orcaEKTdK7LKz57vaAYr9QeNsVEPfiu6QeMU1kektZE\",\n\t\t\t\t\tname: \"Orca\",\n\t\t\t\t},\n\t\t\t\tPNUT: {\n\t\t\t\t\taddress: \"2qEHjDLDLbuBgRYvsxhc5D6uDWAivNFZGan56P1tpump\",\n\t\t\t\t\tname: \"Peanut the Squirrel\",\n\t\t\t\t},\n\t\t\t\tGOAT: {\n\t\t\t\t\taddress: \"CzLSujWBLFsSjncfkh59rUFqvafWcY5tzedWJSuypump\",\n\t\t\t\t\tname: \"Goatseus Maximus\",\n\t\t\t\t},\n\t\t\t\tAI16Z: {\n\t\t\t\t\taddress: \"HeLp6NuQkmYB4pYWo2zYs22mESHXPQYzXbB8n4V98jwC\",\n\t\t\t\t\tname: \"ai16z\",\n\t\t\t\t},\n\t\t\t\tZEREBRO: {\n\t\t\t\t\taddress: \"HeLp6NuQkmYB4pYWo2zYs22mESHXPQYzXbB8n4V98jwC\",\n\t\t\t\t\tname: \"Zerebro\",\n\t\t\t\t},\n\t\t\t\tFARTCOIN: {\n\t\t\t\t\taddress: \"9BB6NFEcjBCtnNLFko2FqVQBq8HHM13kCyYcdQbgpump\",\n\t\t\t\t\tname: \"Fartcoin\",\n\t\t\t\t},\n\t\t\t\tPOPCAT: {\n\t\t\t\t\taddress: \"7GCihgDB8fe6KNjn2MYtkzZcRjQy3t9GHdC8uHYmW2hr\",\n\t\t\t\t\tname: \"Popcat\",\n\t\t\t\t},\n\t\t\t\tDOGE: {\n\t\t\t\t\taddress: \"3NZ9JMVBmGAqocybic2c7LQCJScmgsAZ6vQqTDzcqmJh\",\n\t\t\t\t\tname: \"Dogecoin (Wormhole)\",\n\t\t\t\t},\n\t\t\t\tSHIB: {\n\t\t\t\t\taddress: \"CiKu4eHsVrc1eueVQeHn7qhXTcVu95gSQmBpX4utjL9z\",\n\t\t\t\t\tname: \"Shiba Inu (Wormhole)\",\n\t\t\t\t},\n\t\t\t\tPEPE: {\n\t\t\t\t\taddress: \"BxnFDLpgvhQqhwjwQDNx3RgVQeJbk2ReNGdpE4F1pump\",\n\t\t\t\t\tname: \"Pepe\",\n\t\t\t\t},\n\t\t\t\t// Popular AI tokens from your data\n\t\t\t\tDEGENAI: {\n\t\t\t\t\taddress: \"Gu3LDkn7Vx3bmCzLafYNKcDxv2mqcDvZLhbiewCaAp1M\",\n\t\t\t\t\tname: \"DEGENAI\",\n\t\t\t\t},\n\t\t\t\tCOBIE: {\n\t\t\t\t\taddress: \"6og9y7SuLDZ5wJXtvJTXFJECaFmCj3gKcSSoydG39Dxu\",\n\t\t\t\t\tname: \"Cobie\",\n\t\t\t\t},\n\t\t\t\tSHAW: {\n\t\t\t\t\taddress: \"9Bb6Nf8cNmMSvQT71xFSjGCvGbJ7SQmHwQEE2D9h5R68\",\n\t\t\t\t\tname: \"Shaw\",\n\t\t\t\t},\n\t\t\t\tAILON: {\n\t\t\t\t\taddress: \"EhLXiPhqgAhSt4bdaFfN6b3vWckjV2Sg9mwpLFZUEWb3\",\n\t\t\t\t\tname: \"Ailon\",\n\t\t\t\t},\n\t\t\t\tNAVAL: {\n\t\t\t\t\taddress: \"8P5rj3RRyMEKEzAT8iY1t6WgY0sNdwjBRZVcjY2BwM7h\",\n\t\t\t\t\tname: \"Naval\",\n\t\t\t\t},\n\t\t\t\tAROK: {\n\t\t\t\t\taddress: \"GKJ5Tf7n2Hs9Mg8TkGJT2s6dA1xf1Fw8C3N7b2kK6mPa\",\n\t\t\t\t\tname: \"Arok\",\n\t\t\t\t},\n\t\t\t\tHONEY: {\n\t\t\t\t\taddress: \"4vMsoUT2BWatFweudnQM1xedRLfJgJ7hswhcpz4xgBTy\",\n\t\t\t\t\tname: \"Honey\",\n\t\t\t\t},\n\t\t\t\tBOSSU: {\n\t\t\t\t\taddress: \"7GvK8XPzFwHdQfcrJZ9mCgA8jN9R2Nw9gX4tS1xP7oEq\",\n\t\t\t\t\tname: \"Bossu\",\n\t\t\t\t},\n\t\t\t\tMCAIFEE: {\n\t\t\t\t\taddress: \"9y7K1nBzpVwX7F8yNfG6Ldc2aQ5rN9xV8uTe3CpM6iYx\",\n\t\t\t\t\tname: \"McAifee\",\n\t\t\t\t},\n\t\t\t\tSCHIFF: {\n\t\t\t\t\taddress: \"2A7yHGqN5xL4zP1wE9sF8vQqR6kN3T9bX5uYcMvPw2qE\",\n\t\t\t\t\tname: \"Schiff\",\n\t\t\t\t},\n\t\t\t\tMETH: {\n\t\t\t\t\taddress: \"MEW1gQWJ3nEXg2qgERiKu7FAFj79PHvQVREQUzScPP5\",\n\t\t\t\t\tname: \"Meth\",\n\t\t\t\t},\n\t\t\t\tBRAH: {\n\t\t\t\t\taddress: \"6Mv8Cdt2bKdY7F2Hp9Ry5qKjCwT3pD4vX8uE1N2aS9mB\",\n\t\t\t\t\tname: \"Brah\",\n\t\t\t\t},\n\t\t\t\tTWINS: {\n\t\t\t\t\taddress: \"2YbKvnUmZ8fP3gGqW5N7vR4xQ6j9S1tE8cHa5mXp6NdA\",\n\t\t\t\t\tname: \"Twins\",\n\t\t\t\t},\n\t\t\t\tCHAOS: {\n\t\t\t\t\taddress: \"5Tqn9G2fR8eQ4HaS6pN7jK9xL3wU4yV5cD2bY1mE8vP\",\n\t\t\t\t\tname: \"Chaos\",\n\t\t\t\t},\n\t\t\t\tKOTO: {\n\t\t\t\t\taddress: \"DEF1R2s6o9rN4eQ5jY8fX7pK3cL2wE6tV9bH5gAm1StU\",\n\t\t\t\t\tname: \"Koto\",\n\t\t\t\t},\n\t\t\t\tDEV: {\n\t\t\t\t\taddress: \"DEVeLopER123456789aBcDeFgHiJkLmNoPqRsTuVwXyZ\",\n\t\t\t\t\tname: \"Dev\",\n\t\t\t\t},\n\t\t\t\tEREBRO: {\n\t\t\t\t\taddress: \"7mHq9P3fN8eW2rA4sL6kQ1oY5tG3vX9bE8cZ2jR7iUxM\",\n\t\t\t\t\tname: \"Erebro\",\n\t\t\t\t},\n\t\t\t\t// More popular tokens from Discord data\n\t\t\t\tAIGENT: {\n\t\t\t\t\taddress: \"CEB5RVRvC4p8e2NHT5Nw9g6fhGtB3dSkNYpJ8KzF5mqA\",\n\t\t\t\t\tname: \"Aigent\",\n\t\t\t\t},\n\t\t\t\tAICZ: {\n\t\t\t\t\taddress: \"CJvVNpBcuk88JfT3v2fkSePvFgJ5Fy4BhQ3KN2rXV4j\",\n\t\t\t\t\tname: \"AICZ\",\n\t\t\t\t},\n\t\t\t\tTURA: {\n\t\t\t\t\taddress: \"9cA4M3RfGvBZYrQmnHJgCB2fX8LKfT9eWp1Vq5Rx3StN\",\n\t\t\t\t\tname: \"Tura\",\n\t\t\t\t},\n\t\t\t\tTNSR: {\n\t\t\t\t\taddress: \"TNSRxcUxoT9xBG3de7PiJyTDYu7kskLqcpddxnEJAS6\",\n\t\t\t\t\tname: \"Tensor\",\n\t\t\t\t},\n\t\t\t\tHIVO: {\n\t\t\t\t\taddress: \"HIVE1234567890abcdefghijklmnopqrstuvwxyzABCD\",\n\t\t\t\t\tname: \"Hivo\",\n\t\t\t\t},\n\t\t\t\tBAIDEN: {\n\t\t\t\t\taddress: \"BAIDENr78901234567890abcdefghijklmnopqrstuv\",\n\t\t\t\t\tname: \"Baiden\",\n\t\t\t\t},\n\t\t\t\tGRIN: {\n\t\t\t\t\taddress: \"GRINtokenaddress1234567890abcdefghijklmnop\",\n\t\t\t\t\tname: \"Grin\",\n\t\t\t\t},\n\t\t\t\tFARTBOOK: {\n\t\t\t\t\taddress: \"FART123456789abcdefghijklmnopqrstuvwxyzABC\",\n\t\t\t\t\tname: \"Fartbook\",\n\t\t\t\t},\n\t\t\t\tLUCE: {\n\t\t\t\t\taddress: \"LUCE567890abcdefghijklmnopqrstuvwxyzABCDEF\",\n\t\t\t\t\tname: \"Luce\",\n\t\t\t\t},\n\t\t\t\tLUCY: {\n\t\t\t\t\taddress: \"LUCY890abcdefghijklmnopqrstuvwxyzABCDEFGH\",\n\t\t\t\t\tname: \"Lucy\",\n\t\t\t\t},\n\t\t\t\tNORM: {\n\t\t\t\t\taddress: \"NORMabcdefghijklmnopqrstuvwxyzABCDEFGHIJ\",\n\t\t\t\t\tname: \"Norm\",\n\t\t\t\t},\n\t\t\t\tELIZA: {\n\t\t\t\t\taddress: \"ELIZA123456789abcdefghijklmnopqrstuvwxy\",\n\t\t\t\t\tname: \"Eliza\",\n\t\t\t\t},\n\t\t\t\tTRUTH: {\n\t\t\t\t\taddress: \"TRUTH456789abcdefghijklmnopqrstuvwxyzABC\",\n\t\t\t\t\tname: \"Truth Terminal\",\n\t\t\t\t},\n\t\t\t\tGRASS: {\n\t\t\t\t\taddress: \"GRASS789abcdefghijklmnopqrstuvwxyzABCDEF\",\n\t\t\t\t\tname: \"Grass\",\n\t\t\t\t},\n\t\t\t\tACT: {\n\t\t\t\t\taddress: \"ACT123456789abcdefghijklmnopqrstuvwxyzAB\",\n\t\t\t\t\tname: \"Act\",\n\t\t\t\t},\n\t\t\t\tAGENTS: {\n\t\t\t\t\taddress: \"AGENTS456789abcdefghijklmnopqrstuvwxyzA\",\n\t\t\t\t\tname: \"Agents\",\n\t\t\t\t},\n\t\t\t\tMIST: {\n\t\t\t\t\taddress: \"MIST789abcdefghijklmnopqrstuvwxyzABCDEFG\",\n\t\t\t\t\tname: \"Mist\",\n\t\t\t\t},\n\t\t\t\tKASUMI: {\n\t\t\t\t\taddress: \"KASUMI123456789abcdefghijklmnopqrstuvwx\",\n\t\t\t\t\tname: \"Kasumi\",\n\t\t\t\t},\n\t\t\t\tSPLICE: {\n\t\t\t\t\taddress: \"SPLICE456789abcdefghijklmnopqrstuvwxyz\",\n\t\t\t\t\tname: \"Splice\",\n\t\t\t\t},\n\t\t\t};\n\n\t\t\tif (knownSolanaTokens[cleanSymbol]) {\n\t\t\t\treturn {\n\t\t\t\t\taddress: knownSolanaTokens[cleanSymbol].address,\n\t\t\t\t\tsymbol: cleanSymbol,\n\t\t\t\t\tname: knownSolanaTokens[cleanSymbol].name,\n\t\t\t\t\tchain: SupportedChain.SOLANA,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\t// Known tokens on Ethereum\n\t\telse if (chain === SupportedChain.ETHEREUM) {\n\t\t\tconst knownEthereumTokens: Record<\n\t\t\t\tstring,\n\t\t\t\t{ address: string; name: string }\n\t\t\t> = {\n\t\t\t\tETH: {\n\t\t\t\t\taddress: \"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\",\n\t\t\t\t\tname: \"Wrapped Ether\",\n\t\t\t\t},\n\t\t\t\tUSDC: {\n\t\t\t\t\taddress: \"0xA0b86a33E6441c69De69b9A87e20b88dd75B61FC\",\n\t\t\t\t\tname: \"USD Coin\",\n\t\t\t\t},\n\t\t\t\tUSDT: {\n\t\t\t\t\taddress: \"0xdAC17F958D2ee523a2206206994597C13D831ec7\",\n\t\t\t\t\tname: \"Tether USD\",\n\t\t\t\t},\n\t\t\t\tDAI: {\n\t\t\t\t\taddress: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n\t\t\t\t\tname: \"Dai Stablecoin\",\n\t\t\t\t},\n\t\t\t\tLINK: {\n\t\t\t\t\taddress: \"0x514910771AF9Ca656af840dff83E8264EcF986CA\",\n\t\t\t\t\tname: \"Chainlink\",\n\t\t\t\t},\n\t\t\t\tUNI: {\n\t\t\t\t\taddress: \"0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984\",\n\t\t\t\t\tname: \"Uniswap\",\n\t\t\t\t},\n\t\t\t\tWBTC: {\n\t\t\t\t\taddress: \"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599\",\n\t\t\t\t\tname: \"Wrapped BTC\",\n\t\t\t\t},\n\t\t\t};\n\n\t\t\tif (knownEthereumTokens[cleanSymbol]) {\n\t\t\t\treturn {\n\t\t\t\t\taddress: knownEthereumTokens[cleanSymbol].address,\n\t\t\t\t\tsymbol: cleanSymbol,\n\t\t\t\t\tname: knownEthereumTokens[cleanSymbol].name,\n\t\t\t\t\tchain: SupportedChain.ETHEREUM,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\t// Known tokens on Base\n\t\telse if (chain === SupportedChain.BASE) {\n\t\t\tconst knownBaseTokens: Record<string, { address: string; name: string }> =\n\t\t\t\t{\n\t\t\t\t\tETH: {\n\t\t\t\t\t\taddress: \"0x4200000000000000000000000000000000000006\",\n\t\t\t\t\t\tname: \"Ether\",\n\t\t\t\t\t},\n\t\t\t\t\tUSDC: {\n\t\t\t\t\t\taddress: \"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\",\n\t\t\t\t\t\tname: \"USD Coin\",\n\t\t\t\t\t},\n\t\t\t\t\tWETH: {\n\t\t\t\t\t\taddress: \"0x4200000000000000000000000000000000000006\",\n\t\t\t\t\t\tname: \"Wrapped Ether\",\n\t\t\t\t\t},\n\t\t\t\t};\n\n\t\t\tif (knownBaseTokens[cleanSymbol]) {\n\t\t\t\treturn {\n\t\t\t\t\taddress: knownBaseTokens[cleanSymbol].address,\n\t\t\t\t\tsymbol: cleanSymbol,\n\t\t\t\t\tname: knownBaseTokens[cleanSymbol].name,\n\t\t\t\t\tchain: SupportedChain.BASE,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t * Search for token using DexScreener API\n\t */\n\tprivate async searchTokenOnDexscreener(\n\t\tsymbol: string,\n\t\tchain: SupportedChain,\n\t): Promise<{\n\t\taddress: string;\n\t\tsymbol: string;\n\t\tname: string;\n\t\tchain: SupportedChain;\n\t} | null> {\n\t\ttry {\n\t\t\tconst searchResults = await this.dexscreenerClient.search(symbol, {\n\t\t\t\texpires: \"5m\",\n\t\t\t});\n\n\t\t\tif (!searchResults?.pairs || searchResults.pairs.length === 0) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// Map chain enum to DexScreener chain ID\n\t\t\tlet chainFilter: string;\n\t\t\tswitch (chain) {\n\t\t\t\tcase SupportedChain.SOLANA:\n\t\t\t\t\tchainFilter = \"solana\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase SupportedChain.ETHEREUM:\n\t\t\t\t\tchainFilter = \"ethereum\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase SupportedChain.BASE:\n\t\t\t\t\tchainFilter = \"base\";\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tchainFilter = \"solana\"; // Default to Solana\n\t\t\t}\n\n\t\t\t// First try exact match on the specified chain\n\t\t\tlet bestPair = searchResults.pairs\n\t\t\t\t.filter(\n\t\t\t\t\t(pair) =>\n\t\t\t\t\t\tpair.baseToken.symbol.toUpperCase() === symbol.toUpperCase() &&\n\t\t\t\t\t\tpair.chainId.toLowerCase() === chainFilter,\n\t\t\t\t)\n\t\t\t\t.sort((a, b) => (b.liquidity?.usd || 0) - (a.liquidity?.usd || 0))[0];\n\n\t\t\t// If no exact match on specified chain, try any chain with exact symbol match\n\t\t\tif (!bestPair) {\n\t\t\t\tbestPair = searchResults.pairs\n\t\t\t\t\t.filter(\n\t\t\t\t\t\t(pair) =>\n\t\t\t\t\t\t\tpair.baseToken.symbol.toUpperCase() === symbol.toUpperCase(),\n\t\t\t\t\t)\n\t\t\t\t\t.sort((a, b) => (b.liquidity?.usd || 0) - (a.liquidity?.usd || 0))[0];\n\t\t\t}\n\n\t\t\t// If still no exact match, try partial symbol match on specified chain\n\t\t\tif (!bestPair) {\n\t\t\t\tbestPair = searchResults.pairs\n\t\t\t\t\t.filter(\n\t\t\t\t\t\t(pair) =>\n\t\t\t\t\t\t\tpair.baseToken.symbol\n\t\t\t\t\t\t\t\t.toUpperCase()\n\t\t\t\t\t\t\t\t.includes(symbol.toUpperCase()) &&\n\t\t\t\t\t\t\tpair.chainId.toLowerCase() === chainFilter,\n\t\t\t\t\t)\n\t\t\t\t\t.sort((a, b) => (b.liquidity?.usd || 0) - (a.liquidity?.usd || 0))[0];\n\t\t\t}\n\n\t\t\t// Last resort: any partial match\n\t\t\tif (!bestPair) {\n\t\t\t\tbestPair = searchResults.pairs\n\t\t\t\t\t.filter(\n\t\t\t\t\t\t(pair) =>\n\t\t\t\t\t\t\tpair.baseToken.symbol\n\t\t\t\t\t\t\t\t.toUpperCase()\n\t\t\t\t\t\t\t\t.includes(symbol.toUpperCase()) ||\n\t\t\t\t\t\t\tpair.baseToken.name.toUpperCase().includes(symbol.toUpperCase()),\n\t\t\t\t\t)\n\t\t\t\t\t.sort((a, b) => (b.liquidity?.usd || 0) - (a.liquidity?.usd || 0))[0];\n\t\t\t}\n\n\t\t\tif (bestPair) {\n\t\t\t\t// Map the actual chain from DexScreener result\n\t\t\t\tlet resolvedChain: SupportedChain;\n\t\t\t\tswitch (bestPair.chainId.toLowerCase()) {\n\t\t\t\t\tcase \"solana\":\n\t\t\t\t\t\tresolvedChain = SupportedChain.SOLANA;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"ethereum\":\n\t\t\t\t\t\tresolvedChain = SupportedChain.ETHEREUM;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"base\":\n\t\t\t\t\t\tresolvedChain = SupportedChain.BASE;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tresolvedChain = chain; // Fallback to original\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\taddress: bestPair.baseToken.address,\n\t\t\t\t\tsymbol: bestPair.baseToken.symbol,\n\t\t\t\t\tname: bestPair.baseToken.name,\n\t\t\t\t\tchain: resolvedChain,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn null;\n\t\t} catch (error) {\n\t\t\tconsole.error(`Error searching DexScreener for symbol ${symbol}:`, error);\n\t\t\treturn null;\n\t\t}\n\t}\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,54 @@
1
+ import type { UUID } from "@elizaos/core";
2
+ import type { SimulatedToken } from "../mockPriceService";
3
+ import { Conviction, type SupportedChain } from "../types";
4
+ import type { TokenScenario } from "./tokenSimulationService";
5
+ export interface SimulatedCallV2 {
6
+ callId: string;
7
+ userId: string;
8
+ username: string;
9
+ timestamp: number;
10
+ tokenMentioned: string;
11
+ tokenAddress: string;
12
+ sentiment: "positive" | "negative" | "neutral";
13
+ conviction: Conviction;
14
+ content: string;
15
+ chain: SupportedChain;
16
+ certainty: "high" | "medium" | "low";
17
+ llmReasoning: string;
18
+ }
19
+ export type ActorArchetypeV2 = "elite_analyst" | "skilled_trader" | "pump_chaser" | "rug_promoter" | "fomo_trader" | "contrarian" | "whale_watcher" | "technical_analyst" | "newbie" | "bot_spammer";
20
+ export interface SimulatedActorV2 {
21
+ id: UUID;
22
+ username: string;
23
+ archetype: ActorArchetypeV2;
24
+ trustScore?: number;
25
+ callHistory: SimulatedCallV2[];
26
+ preferences: {
27
+ favoriteTokenTypes?: TokenScenario["type"][];
28
+ callFrequency: "high" | "medium" | "low";
29
+ timingBias: "early" | "middle" | "late" | "random";
30
+ };
31
+ }
32
+ export declare class SimulationActorsServiceV2 {
33
+ private actors;
34
+ constructor();
35
+ private initializeActors;
36
+ private addActor;
37
+ generateCallsForActor(actor: SimulatedActorV2, token: SimulatedToken, tokenScenario: TokenScenario, currentStep: number, priceHistory: {
38
+ step: number;
39
+ price: number;
40
+ }[]): SimulatedCallV2 | null;
41
+ private shouldMakeCall;
42
+ private determineSentiment;
43
+ private determineConviction;
44
+ private generateCallContent;
45
+ private determineCertainty;
46
+ private generateReasoning;
47
+ getAllActors(): SimulatedActorV2[];
48
+ getActorById(id: UUID): SimulatedActorV2 | undefined;
49
+ getExpectedRankings(): {
50
+ username: string;
51
+ expectedTrustScore: number;
52
+ }[];
53
+ }
54
+ //# sourceMappingURL=simulationActorsV2.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"simulationActorsV2.d.ts","sourceRoot":"","sources":["../../src/services/simulationActorsV2.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAE1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D,MAAM,WAAW,eAAe;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,UAAU,GAAG,UAAU,GAAG,SAAS,CAAC;IAC/C,UAAU,EAAE,UAAU,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,cAAc,CAAC;IACtB,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACrC,YAAY,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,gBAAgB,GACzB,eAAe,GACf,gBAAgB,GAChB,aAAa,GACb,cAAc,GACd,aAAa,GACb,YAAY,GACZ,eAAe,GACf,mBAAmB,GACnB,QAAQ,GACR,aAAa,CAAC;AAEjB,MAAM,WAAW,gBAAgB;IAChC,EAAE,EAAE,IAAI,CAAC;IACT,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,gBAAgB,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,WAAW,EAAE;QACZ,kBAAkB,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7C,aAAa,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;QACzC,UAAU,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;KACnD,CAAC;CACF;AAED,qBAAa,yBAAyB;IACrC,OAAO,CAAC,MAAM,CAA0C;;IAMxD,OAAO,CAAC,gBAAgB;IAgIxB,OAAO,CAAC,QAAQ;IAIhB,qBAAqB,CACpB,KAAK,EAAE,gBAAgB,EACvB,KAAK,EAAE,cAAc,EACrB,aAAa,EAAE,aAAa,EAC5B,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,GAC7C,eAAe,GAAG,IAAI;IAuDzB,OAAO,CAAC,cAAc;IAqCtB,OAAO,CAAC,kBAAkB;IAyD1B,OAAO,CAAC,mBAAmB;IAgD3B,OAAO,CAAC,mBAAmB;IAmF3B,OAAO,CAAC,kBAAkB;IAkB1B,OAAO,CAAC,iBAAiB;IASzB,YAAY,IAAI,gBAAgB,EAAE;IAIlC,YAAY,CAAC,EAAE,EAAE,IAAI,GAAG,gBAAgB,GAAG,SAAS;IAIpD,mBAAmB,IAAI;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,kBAAkB,EAAE,MAAM,CAAA;KAAE,EAAE;CAQzE"}
@@ -0,0 +1,362 @@
1
+ import { v4 as uuidv4 } from "uuid";
2
+ import { Conviction } from "../types.js";
3
+ class SimulationActorsServiceV2 {
4
+ actors = /* @__PURE__ */ new Map();
5
+ constructor() {
6
+ this.initializeActors();
7
+ }
8
+ initializeActors() {
9
+ this.addActor({
10
+ id: uuidv4(),
11
+ username: "CryptoSage",
12
+ archetype: "elite_analyst",
13
+ trustScore: 95,
14
+ // Expected
15
+ callHistory: [],
16
+ preferences: {
17
+ favoriteTokenTypes: ["runner", "successful", "bluechip"],
18
+ callFrequency: "medium",
19
+ timingBias: "early"
20
+ }
21
+ });
22
+ this.addActor({
23
+ id: uuidv4(),
24
+ username: "ProfitHunter",
25
+ archetype: "skilled_trader",
26
+ trustScore: 75,
27
+ callHistory: [],
28
+ preferences: {
29
+ favoriteTokenTypes: ["runner", "successful", "pump_dump"],
30
+ // Sometimes catches pumps
31
+ callFrequency: "medium",
32
+ timingBias: "early"
33
+ }
34
+ });
35
+ this.addActor({
36
+ id: uuidv4(),
37
+ username: "MoonBoy2024",
38
+ archetype: "pump_chaser",
39
+ trustScore: 25,
40
+ callHistory: [],
41
+ preferences: {
42
+ favoriteTokenTypes: ["pump_dump", "rug", "scam"],
43
+ callFrequency: "high",
44
+ timingBias: "late"
45
+ }
46
+ });
47
+ this.addActor({
48
+ id: uuidv4(),
49
+ username: "100xGems",
50
+ archetype: "rug_promoter",
51
+ trustScore: 10,
52
+ callHistory: [],
53
+ preferences: {
54
+ favoriteTokenTypes: ["rug", "scam"],
55
+ callFrequency: "high",
56
+ timingBias: "early"
57
+ // Shills early to dump on followers
58
+ }
59
+ });
60
+ this.addActor({
61
+ id: uuidv4(),
62
+ username: "AlwaysLate",
63
+ archetype: "fomo_trader",
64
+ trustScore: 30,
65
+ callHistory: [],
66
+ preferences: {
67
+ favoriteTokenTypes: ["runner", "pump_dump"],
68
+ // Sees movement, jumps in
69
+ callFrequency: "high",
70
+ timingBias: "late"
71
+ }
72
+ });
73
+ this.addActor({
74
+ id: uuidv4(),
75
+ username: "AgainstGrain",
76
+ archetype: "contrarian",
77
+ trustScore: 60,
78
+ // Mixed results
79
+ callHistory: [],
80
+ preferences: {
81
+ favoriteTokenTypes: ["mediocre", "stagnant", "slow_bleed"],
82
+ callFrequency: "medium",
83
+ timingBias: "random"
84
+ }
85
+ });
86
+ this.addActor({
87
+ id: uuidv4(),
88
+ username: "ChartMaster",
89
+ archetype: "technical_analyst",
90
+ trustScore: 65,
91
+ callHistory: [],
92
+ preferences: {
93
+ favoriteTokenTypes: ["bluechip", "successful", "runner"],
94
+ callFrequency: "low",
95
+ timingBias: "middle"
96
+ }
97
+ });
98
+ this.addActor({
99
+ id: uuidv4(),
100
+ username: "CryptoNoob",
101
+ archetype: "newbie",
102
+ trustScore: 40,
103
+ callHistory: [],
104
+ preferences: {
105
+ favoriteTokenTypes: [],
106
+ // Random
107
+ callFrequency: "medium",
108
+ timingBias: "random"
109
+ }
110
+ });
111
+ this.addActor({
112
+ id: uuidv4(),
113
+ username: "SignalsBot",
114
+ archetype: "bot_spammer",
115
+ trustScore: 15,
116
+ callHistory: [],
117
+ preferences: {
118
+ favoriteTokenTypes: ["scam", "rug", "pump_dump"],
119
+ callFrequency: "high",
120
+ timingBias: "random"
121
+ }
122
+ });
123
+ }
124
+ addActor(actor) {
125
+ this.actors.set(actor.id, actor);
126
+ }
127
+ generateCallsForActor(actor, token, tokenScenario, currentStep, priceHistory) {
128
+ if (!this.shouldMakeCall(
129
+ actor,
130
+ token,
131
+ tokenScenario,
132
+ currentStep,
133
+ priceHistory
134
+ )) {
135
+ return null;
136
+ }
137
+ const sentiment = this.determineSentiment(
138
+ actor,
139
+ token,
140
+ tokenScenario,
141
+ currentStep,
142
+ priceHistory
143
+ );
144
+ const conviction = this.determineConviction(
145
+ actor,
146
+ token,
147
+ tokenScenario,
148
+ currentStep,
149
+ priceHistory
150
+ );
151
+ const content = this.generateCallContent(
152
+ actor,
153
+ token,
154
+ sentiment,
155
+ conviction,
156
+ priceHistory
157
+ );
158
+ const call = {
159
+ callId: uuidv4(),
160
+ userId: actor.id,
161
+ username: actor.username,
162
+ timestamp: Date.now() + currentStep * 24 * 60 * 60 * 1e3,
163
+ // Add days to current time
164
+ tokenMentioned: token.symbol,
165
+ tokenAddress: token.address,
166
+ sentiment,
167
+ conviction,
168
+ content,
169
+ chain: token.chain,
170
+ certainty: this.determineCertainty(actor, tokenScenario),
171
+ llmReasoning: this.generateReasoning(actor, token, sentiment, conviction)
172
+ };
173
+ actor.callHistory.push(call);
174
+ return call;
175
+ }
176
+ shouldMakeCall(actor, _token, _tokenScenario, currentStep, _priceHistory) {
177
+ const frequencyThreshold = {
178
+ high: 0.7,
179
+ medium: 0.4,
180
+ low: 0.2
181
+ };
182
+ if (Math.random() > frequencyThreshold[actor.preferences.callFrequency]) {
183
+ return false;
184
+ }
185
+ const totalSteps = 30;
186
+ const phase = currentStep / totalSteps;
187
+ switch (actor.preferences.timingBias) {
188
+ case "early":
189
+ return phase < 0.4 || phase < 0.6 && Math.random() < 0.5;
190
+ case "middle":
191
+ return phase >= 0.2 && phase <= 0.8;
192
+ case "late":
193
+ return phase > 0.6 || phase > 0.4 && Math.random() < 0.5;
194
+ case "random":
195
+ return true;
196
+ }
197
+ return true;
198
+ }
199
+ determineSentiment(actor, _token, tokenScenario, _currentStep, priceHistory) {
200
+ switch (actor.archetype) {
201
+ case "elite_analyst":
202
+ if (["runner", "successful", "bluechip"].includes(tokenScenario.type)) {
203
+ return "positive";
204
+ } else if (["rug", "scam", "pump_dump", "slow_bleed"].includes(
205
+ tokenScenario.type
206
+ )) {
207
+ return "negative";
208
+ }
209
+ return "neutral";
210
+ case "rug_promoter":
211
+ if (["rug", "scam"].includes(tokenScenario.type)) {
212
+ return "positive";
213
+ }
214
+ return "neutral";
215
+ case "pump_chaser":
216
+ if (priceHistory.length > 1) {
217
+ const recentPrice = priceHistory[priceHistory.length - 1].price;
218
+ const previousPrice = priceHistory[priceHistory.length - 2].price;
219
+ return recentPrice > previousPrice ? "positive" : "negative";
220
+ }
221
+ return "positive";
222
+ case "contrarian":
223
+ if (priceHistory.length > 2) {
224
+ const recentTrend = priceHistory[priceHistory.length - 1].price - priceHistory[priceHistory.length - 3].price;
225
+ return recentTrend > 0 ? "negative" : "positive";
226
+ }
227
+ return "neutral";
228
+ default: {
229
+ const rand = Math.random();
230
+ if (rand < 0.5) return "positive";
231
+ if (rand < 0.8) return "negative";
232
+ return "neutral";
233
+ }
234
+ }
235
+ }
236
+ determineConviction(actor, _token, tokenScenario, _currentStep, priceHistory) {
237
+ switch (actor.archetype) {
238
+ case "elite_analyst":
239
+ if (["runner", "rug", "scam"].includes(tokenScenario.type)) {
240
+ return Conviction.HIGH;
241
+ }
242
+ return Conviction.MEDIUM;
243
+ case "rug_promoter":
244
+ case "bot_spammer":
245
+ return Conviction.HIGH;
246
+ case "newbie":
247
+ return Math.random() < 0.8 ? Conviction.LOW : Conviction.MEDIUM;
248
+ case "technical_analyst":
249
+ if (priceHistory.length > 5) {
250
+ const recent = priceHistory.slice(-5);
251
+ const highestRecent = Math.max(...recent.map((p) => p.price));
252
+ if (priceHistory[priceHistory.length - 1].price > highestRecent * 0.95) {
253
+ return Conviction.HIGH;
254
+ }
255
+ }
256
+ return Conviction.MEDIUM;
257
+ default: {
258
+ const rand = Math.random();
259
+ if (rand < 0.3) return Conviction.HIGH;
260
+ if (rand < 0.7) return Conviction.MEDIUM;
261
+ return Conviction.LOW;
262
+ }
263
+ }
264
+ }
265
+ generateCallContent(actor, token, sentiment, _conviction, priceHistory) {
266
+ const templates = {
267
+ elite_analyst: {
268
+ positive: [
269
+ `$${token.symbol} showing strong fundamentals. This is a long-term hold.`,
270
+ `Been researching $${token.symbol} - solid team and execution plan. Accumulating here.`,
271
+ `$${token.symbol} is undervalued at current levels. Target: ${(priceHistory[priceHistory.length - 1].price * 5).toFixed(6)}`
272
+ ],
273
+ negative: [
274
+ `Warning: $${token.symbol} showing red flags. Low liquidity, suspicious wallet activity.`,
275
+ `Avoid $${token.symbol} - classic rug setup. Dev wallets hold 40%+`,
276
+ `$${token.symbol} is a clear scam. Don't fall for it.`
277
+ ],
278
+ neutral: [
279
+ `Watching $${token.symbol} closely. Need more data before making a call.`,
280
+ `$${token.symbol} on my radar. Waiting for better entry.`
281
+ ]
282
+ },
283
+ rug_promoter: {
284
+ positive: [
285
+ `\u{1F680}\u{1F680} $${token.symbol} TO THE MOON! 1000X GEM! GET IN NOW! \u{1F680}\u{1F680}`,
286
+ `$${token.symbol} NEXT 100X!!! DEV DOXXED! LIQUIDITY LOCKED! SAFU! \u{1F48E}\u{1F48E}`,
287
+ `BREAKING: $${token.symbol} ABOUT TO EXPLODE! WHALES ACCUMULATING! \u{1F40B}`
288
+ ],
289
+ negative: [],
290
+ neutral: []
291
+ },
292
+ pump_chaser: {
293
+ positive: [
294
+ `$${token.symbol} is pumping hard! Just aped in!`,
295
+ `Holy shit $${token.symbol} is flying! This is going to $1!`,
296
+ `Everyone talking about $${token.symbol}! Don't miss out!`
297
+ ],
298
+ negative: [
299
+ `Fuck, $${token.symbol} dumping. Should have sold earlier.`,
300
+ `$${token.symbol} rugged. Lost everything. Stay away.`
301
+ ],
302
+ neutral: []
303
+ },
304
+ technical_analyst: {
305
+ positive: [
306
+ `$${token.symbol} breaking key resistance at ${priceHistory[priceHistory.length - 1].price.toFixed(6)}. Next target: ${(priceHistory[priceHistory.length - 1].price * 1.5).toFixed(6)}`,
307
+ `Bullish divergence on $${token.symbol} 4H chart. Accumulation zone.`,
308
+ `$${token.symbol} forming cup and handle. Breakout imminent.`
309
+ ],
310
+ negative: [
311
+ `$${token.symbol} lost critical support. Expecting further downside.`,
312
+ `Death cross forming on $${token.symbol}. Time to exit.`
313
+ ],
314
+ neutral: [
315
+ `$${token.symbol} consolidating. Waiting for breakout direction.`,
316
+ `$${token.symbol} at key level. Could go either way.`
317
+ ]
318
+ }
319
+ };
320
+ const defaultTemplates = {
321
+ positive: [`I think $${token.symbol} looks good`],
322
+ negative: [`$${token.symbol} doesn't look great`],
323
+ neutral: [`Watching $${token.symbol}`]
324
+ };
325
+ const archetypeTemplates = templates[actor.archetype] || defaultTemplates;
326
+ const sentimentTemplates = archetypeTemplates[sentiment] || archetypeTemplates.positive || defaultTemplates[sentiment] || [`Watching $${token.symbol}`];
327
+ return sentimentTemplates[Math.floor(Math.random() * sentimentTemplates.length)] || `Looking at $${token.symbol}`;
328
+ }
329
+ determineCertainty(actor, _tokenScenario) {
330
+ switch (actor.archetype) {
331
+ case "elite_analyst":
332
+ return "high";
333
+ case "skilled_trader":
334
+ case "technical_analyst":
335
+ return "medium";
336
+ case "newbie":
337
+ case "pump_chaser":
338
+ return "low";
339
+ default:
340
+ return "medium";
341
+ }
342
+ }
343
+ generateReasoning(actor, token, sentiment, conviction) {
344
+ return `${actor.username} (${actor.archetype}) made a ${conviction} conviction ${sentiment} call on ${token.symbol}`;
345
+ }
346
+ getAllActors() {
347
+ return Array.from(this.actors.values());
348
+ }
349
+ getActorById(id) {
350
+ return this.actors.get(id);
351
+ }
352
+ getExpectedRankings() {
353
+ return Array.from(this.actors.values()).map((actor) => ({
354
+ username: actor.username,
355
+ expectedTrustScore: actor.trustScore || 50
356
+ })).sort((a, b) => b.expectedTrustScore - a.expectedTrustScore);
357
+ }
358
+ }
359
+ export {
360
+ SimulationActorsServiceV2
361
+ };
362
+ //# sourceMappingURL=simulationActorsV2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/services/simulationActorsV2.ts"],"sourcesContent":["import type { UUID } from \"@elizaos/core\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport type { SimulatedToken } from \"../mockPriceService.js\";\nimport { Conviction, type SupportedChain } from \"../types.js\";\nimport type { TokenScenario } from \"./tokenSimulationService.js\";\n\nexport interface SimulatedCallV2 {\n\tcallId: string;\n\tuserId: string;\n\tusername: string;\n\ttimestamp: number;\n\ttokenMentioned: string;\n\ttokenAddress: string;\n\tsentiment: \"positive\" | \"negative\" | \"neutral\";\n\tconviction: Conviction;\n\tcontent: string;\n\tchain: SupportedChain;\n\tcertainty: \"high\" | \"medium\" | \"low\";\n\tllmReasoning: string;\n}\n\nexport type ActorArchetypeV2 =\n\t| \"elite_analyst\" // Makes excellent calls on good projects early\n\t| \"skilled_trader\" // Good calls but not perfect\n\t| \"pump_chaser\" // Buys tops, gets rugged\n\t| \"rug_promoter\" // Shills rugs and scams\n\t| \"fomo_trader\" // Always late to the party\n\t| \"contrarian\" // Goes against the crowd\n\t| \"whale_watcher\" // Follows big money (sometimes good, sometimes bad)\n\t| \"technical_analyst\" // Uses TA, mixed results\n\t| \"newbie\" // Random, learning\n\t| \"bot_spammer\"; // High volume, low quality\n\nexport interface SimulatedActorV2 {\n\tid: UUID;\n\tusername: string;\n\tarchetype: ActorArchetypeV2;\n\ttrustScore?: number; // Expected trust score for scenario checks\n\tcallHistory: SimulatedCallV2[];\n\tpreferences: {\n\t\tfavoriteTokenTypes?: TokenScenario[\"type\"][];\n\t\tcallFrequency: \"high\" | \"medium\" | \"low\";\n\t\ttimingBias: \"early\" | \"middle\" | \"late\" | \"random\";\n\t};\n}\n\nexport class SimulationActorsServiceV2 {\n\tprivate actors: Map<UUID, SimulatedActorV2> = new Map();\n\n\tconstructor() {\n\t\tthis.initializeActors();\n\t}\n\n\tprivate initializeActors() {\n\t\t// Elite Analyst - Should have highest trust score\n\t\tthis.addActor({\n\t\t\tid: uuidv4() as UUID,\n\t\t\tusername: \"CryptoSage\",\n\t\t\tarchetype: \"elite_analyst\",\n\t\t\ttrustScore: 95, // Expected\n\t\t\tcallHistory: [],\n\t\t\tpreferences: {\n\t\t\t\tfavoriteTokenTypes: [\"runner\", \"successful\", \"bluechip\"],\n\t\t\t\tcallFrequency: \"medium\",\n\t\t\t\ttimingBias: \"early\",\n\t\t\t},\n\t\t});\n\n\t\t// Skilled Trader\n\t\tthis.addActor({\n\t\t\tid: uuidv4() as UUID,\n\t\t\tusername: \"ProfitHunter\",\n\t\t\tarchetype: \"skilled_trader\",\n\t\t\ttrustScore: 75,\n\t\t\tcallHistory: [],\n\t\t\tpreferences: {\n\t\t\t\tfavoriteTokenTypes: [\"runner\", \"successful\", \"pump_dump\"], // Sometimes catches pumps\n\t\t\t\tcallFrequency: \"medium\",\n\t\t\t\ttimingBias: \"early\",\n\t\t\t},\n\t\t});\n\n\t\t// Pump Chaser - Should have low trust score\n\t\tthis.addActor({\n\t\t\tid: uuidv4() as UUID,\n\t\t\tusername: \"MoonBoy2024\",\n\t\t\tarchetype: \"pump_chaser\",\n\t\t\ttrustScore: 25,\n\t\t\tcallHistory: [],\n\t\t\tpreferences: {\n\t\t\t\tfavoriteTokenTypes: [\"pump_dump\", \"rug\", \"scam\"],\n\t\t\t\tcallFrequency: \"high\",\n\t\t\t\ttimingBias: \"late\",\n\t\t\t},\n\t\t});\n\n\t\t// Rug Promoter - Should have very low trust score\n\t\tthis.addActor({\n\t\t\tid: uuidv4() as UUID,\n\t\t\tusername: \"100xGems\",\n\t\t\tarchetype: \"rug_promoter\",\n\t\t\ttrustScore: 10,\n\t\t\tcallHistory: [],\n\t\t\tpreferences: {\n\t\t\t\tfavoriteTokenTypes: [\"rug\", \"scam\"],\n\t\t\t\tcallFrequency: \"high\",\n\t\t\t\ttimingBias: \"early\", // Shills early to dump on followers\n\t\t\t},\n\t\t});\n\n\t\t// FOMO Trader\n\t\tthis.addActor({\n\t\t\tid: uuidv4() as UUID,\n\t\t\tusername: \"AlwaysLate\",\n\t\t\tarchetype: \"fomo_trader\",\n\t\t\ttrustScore: 30,\n\t\t\tcallHistory: [],\n\t\t\tpreferences: {\n\t\t\t\tfavoriteTokenTypes: [\"runner\", \"pump_dump\"], // Sees movement, jumps in\n\t\t\t\tcallFrequency: \"high\",\n\t\t\t\ttimingBias: \"late\",\n\t\t\t},\n\t\t});\n\n\t\t// Contrarian\n\t\tthis.addActor({\n\t\t\tid: uuidv4() as UUID,\n\t\t\tusername: \"AgainstGrain\",\n\t\t\tarchetype: \"contrarian\",\n\t\t\ttrustScore: 60, // Mixed results\n\t\t\tcallHistory: [],\n\t\t\tpreferences: {\n\t\t\t\tfavoriteTokenTypes: [\"mediocre\", \"stagnant\", \"slow_bleed\"],\n\t\t\t\tcallFrequency: \"medium\",\n\t\t\t\ttimingBias: \"random\",\n\t\t\t},\n\t\t});\n\n\t\t// Technical Analyst\n\t\tthis.addActor({\n\t\t\tid: uuidv4() as UUID,\n\t\t\tusername: \"ChartMaster\",\n\t\t\tarchetype: \"technical_analyst\",\n\t\t\ttrustScore: 65,\n\t\t\tcallHistory: [],\n\t\t\tpreferences: {\n\t\t\t\tfavoriteTokenTypes: [\"bluechip\", \"successful\", \"runner\"],\n\t\t\t\tcallFrequency: \"low\",\n\t\t\t\ttimingBias: \"middle\",\n\t\t\t},\n\t\t});\n\n\t\t// Newbie\n\t\tthis.addActor({\n\t\t\tid: uuidv4() as UUID,\n\t\t\tusername: \"CryptoNoob\",\n\t\t\tarchetype: \"newbie\",\n\t\t\ttrustScore: 40,\n\t\t\tcallHistory: [],\n\t\t\tpreferences: {\n\t\t\t\tfavoriteTokenTypes: [], // Random\n\t\t\t\tcallFrequency: \"medium\",\n\t\t\t\ttimingBias: \"random\",\n\t\t\t},\n\t\t});\n\n\t\t// Bot Spammer\n\t\tthis.addActor({\n\t\t\tid: uuidv4() as UUID,\n\t\t\tusername: \"SignalsBot\",\n\t\t\tarchetype: \"bot_spammer\",\n\t\t\ttrustScore: 15,\n\t\t\tcallHistory: [],\n\t\t\tpreferences: {\n\t\t\t\tfavoriteTokenTypes: [\"scam\", \"rug\", \"pump_dump\"],\n\t\t\t\tcallFrequency: \"high\",\n\t\t\t\ttimingBias: \"random\",\n\t\t\t},\n\t\t});\n\t}\n\n\tprivate addActor(actor: SimulatedActorV2) {\n\t\tthis.actors.set(actor.id, actor);\n\t}\n\n\tgenerateCallsForActor(\n\t\tactor: SimulatedActorV2,\n\t\ttoken: SimulatedToken,\n\t\ttokenScenario: TokenScenario,\n\t\tcurrentStep: number,\n\t\tpriceHistory: { step: number; price: number }[],\n\t): SimulatedCallV2 | null {\n\t\t// Determine if actor should make a call based on their strategy\n\t\tif (\n\t\t\t!this.shouldMakeCall(\n\t\t\t\tactor,\n\t\t\t\ttoken,\n\t\t\t\ttokenScenario,\n\t\t\t\tcurrentStep,\n\t\t\t\tpriceHistory,\n\t\t\t)\n\t\t) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst sentiment = this.determineSentiment(\n\t\t\tactor,\n\t\t\ttoken,\n\t\t\ttokenScenario,\n\t\t\tcurrentStep,\n\t\t\tpriceHistory,\n\t\t);\n\t\tconst conviction = this.determineConviction(\n\t\t\tactor,\n\t\t\ttoken,\n\t\t\ttokenScenario,\n\t\t\tcurrentStep,\n\t\t\tpriceHistory,\n\t\t);\n\t\tconst content = this.generateCallContent(\n\t\t\tactor,\n\t\t\ttoken,\n\t\t\tsentiment,\n\t\t\tconviction,\n\t\t\tpriceHistory,\n\t\t);\n\n\t\tconst call: SimulatedCallV2 = {\n\t\t\tcallId: uuidv4(),\n\t\t\tuserId: actor.id,\n\t\t\tusername: actor.username,\n\t\t\ttimestamp: Date.now() + currentStep * 24 * 60 * 60 * 1000, // Add days to current time\n\t\t\ttokenMentioned: token.symbol,\n\t\t\ttokenAddress: token.address,\n\t\t\tsentiment,\n\t\t\tconviction,\n\t\t\tcontent,\n\t\t\tchain: token.chain,\n\t\t\tcertainty: this.determineCertainty(actor, tokenScenario),\n\t\t\tllmReasoning: this.generateReasoning(actor, token, sentiment, conviction),\n\t\t};\n\n\t\tactor.callHistory.push(call);\n\t\treturn call;\n\t}\n\n\tprivate shouldMakeCall(\n\t\tactor: SimulatedActorV2,\n\t\t_token: SimulatedToken,\n\t\t_tokenScenario: TokenScenario,\n\t\tcurrentStep: number,\n\t\t_priceHistory: { step: number; price: number }[],\n\t): boolean {\n\t\t// Call frequency check - use probability not reverse threshold\n\t\tconst frequencyThreshold = {\n\t\t\thigh: 0.7,\n\t\t\tmedium: 0.4,\n\t\t\tlow: 0.2,\n\t\t};\n\n\t\t// Fixed: should be less than, not greater than\n\t\tif (Math.random() > frequencyThreshold[actor.preferences.callFrequency]) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Check timing preference\n\t\tconst totalSteps = 30; // Assume 30-day simulation\n\t\tconst phase = currentStep / totalSteps;\n\n\t\tswitch (actor.preferences.timingBias) {\n\t\t\tcase \"early\":\n\t\t\t\treturn phase < 0.4 || (phase < 0.6 && Math.random() < 0.5);\n\t\t\tcase \"middle\":\n\t\t\t\treturn phase >= 0.2 && phase <= 0.8;\n\t\t\tcase \"late\":\n\t\t\t\treturn phase > 0.6 || (phase > 0.4 && Math.random() < 0.5);\n\t\t\tcase \"random\":\n\t\t\t\treturn true;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tprivate determineSentiment(\n\t\tactor: SimulatedActorV2,\n\t\t_token: SimulatedToken,\n\t\ttokenScenario: TokenScenario,\n\t\t_currentStep: number,\n\t\tpriceHistory: { step: number; price: number }[],\n\t): \"positive\" | \"negative\" | \"neutral\" {\n\t\tswitch (actor.archetype) {\n\t\t\tcase \"elite_analyst\":\n\t\t\t\t// Correctly identifies good projects as positive, bad as negative\n\t\t\t\tif ([\"runner\", \"successful\", \"bluechip\"].includes(tokenScenario.type)) {\n\t\t\t\t\treturn \"positive\";\n\t\t\t\t} else if (\n\t\t\t\t\t[\"rug\", \"scam\", \"pump_dump\", \"slow_bleed\"].includes(\n\t\t\t\t\t\ttokenScenario.type,\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\treturn \"negative\";\n\t\t\t\t}\n\t\t\t\treturn \"neutral\";\n\n\t\t\tcase \"rug_promoter\":\n\t\t\t\t// Always positive on rugs and scams\n\t\t\t\tif ([\"rug\", \"scam\"].includes(tokenScenario.type)) {\n\t\t\t\t\treturn \"positive\";\n\t\t\t\t}\n\t\t\t\treturn \"neutral\";\n\n\t\t\tcase \"pump_chaser\":\n\t\t\t\t// Positive when price is rising\n\t\t\t\tif (priceHistory.length > 1) {\n\t\t\t\t\tconst recentPrice = priceHistory[priceHistory.length - 1].price;\n\t\t\t\t\tconst previousPrice = priceHistory[priceHistory.length - 2].price;\n\t\t\t\t\treturn recentPrice > previousPrice ? \"positive\" : \"negative\";\n\t\t\t\t}\n\t\t\t\treturn \"positive\";\n\n\t\t\tcase \"contrarian\":\n\t\t\t\t// Opposite of recent price movement\n\t\t\t\tif (priceHistory.length > 2) {\n\t\t\t\t\tconst recentTrend =\n\t\t\t\t\t\tpriceHistory[priceHistory.length - 1].price -\n\t\t\t\t\t\tpriceHistory[priceHistory.length - 3].price;\n\t\t\t\t\treturn recentTrend > 0 ? \"negative\" : \"positive\";\n\t\t\t\t}\n\t\t\t\treturn \"neutral\";\n\n\t\t\tdefault: {\n\t\t\t\t// Random for others\n\t\t\t\tconst rand = Math.random();\n\t\t\t\tif (rand < 0.5) return \"positive\";\n\t\t\t\tif (rand < 0.8) return \"negative\";\n\t\t\t\treturn \"neutral\";\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate determineConviction(\n\t\tactor: SimulatedActorV2,\n\t\t_token: SimulatedToken,\n\t\ttokenScenario: TokenScenario,\n\t\t_currentStep: number,\n\t\tpriceHistory: { step: number; price: number }[],\n\t): Conviction {\n\t\tswitch (actor.archetype) {\n\t\t\tcase \"elite_analyst\":\n\t\t\t\t// High conviction on clear winners/losers\n\t\t\t\tif ([\"runner\", \"rug\", \"scam\"].includes(tokenScenario.type)) {\n\t\t\t\t\treturn Conviction.HIGH;\n\t\t\t\t}\n\t\t\t\treturn Conviction.MEDIUM;\n\n\t\t\tcase \"rug_promoter\":\n\t\t\tcase \"bot_spammer\":\n\t\t\t\t// Always high conviction (to seem convincing)\n\t\t\t\treturn Conviction.HIGH;\n\n\t\t\tcase \"newbie\":\n\t\t\t\t// Usually low conviction\n\t\t\t\treturn Math.random() < 0.8 ? Conviction.LOW : Conviction.MEDIUM;\n\n\t\t\tcase \"technical_analyst\":\n\t\t\t\t// Conviction based on \"technical signals\"\n\t\t\t\tif (priceHistory.length > 5) {\n\t\t\t\t\t// Fake TA: check if breaking \"resistance\"\n\t\t\t\t\tconst recent = priceHistory.slice(-5);\n\t\t\t\t\tconst highestRecent = Math.max(...recent.map((p) => p.price));\n\t\t\t\t\tif (\n\t\t\t\t\t\tpriceHistory[priceHistory.length - 1].price >\n\t\t\t\t\t\thighestRecent * 0.95\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn Conviction.HIGH;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn Conviction.MEDIUM;\n\n\t\t\tdefault: {\n\t\t\t\tconst rand = Math.random();\n\t\t\t\tif (rand < 0.3) return Conviction.HIGH;\n\t\t\t\tif (rand < 0.7) return Conviction.MEDIUM;\n\t\t\t\treturn Conviction.LOW;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate generateCallContent(\n\t\tactor: SimulatedActorV2,\n\t\ttoken: SimulatedToken,\n\t\tsentiment: \"positive\" | \"negative\" | \"neutral\",\n\t\t_conviction: Conviction,\n\t\tpriceHistory: { step: number; price: number }[],\n\t): string {\n\t\tconst templates: Record<\n\t\t\tstring,\n\t\t\t{ positive: string[]; negative: string[]; neutral?: string[] }\n\t\t> = {\n\t\t\telite_analyst: {\n\t\t\t\tpositive: [\n\t\t\t\t\t`$${token.symbol} showing strong fundamentals. This is a long-term hold.`,\n\t\t\t\t\t`Been researching $${token.symbol} - solid team and execution plan. Accumulating here.`,\n\t\t\t\t\t`$${token.symbol} is undervalued at current levels. Target: ${(priceHistory[priceHistory.length - 1].price * 5).toFixed(6)}`,\n\t\t\t\t],\n\t\t\t\tnegative: [\n\t\t\t\t\t`Warning: $${token.symbol} showing red flags. Low liquidity, suspicious wallet activity.`,\n\t\t\t\t\t`Avoid $${token.symbol} - classic rug setup. Dev wallets hold 40%+`,\n\t\t\t\t\t`$${token.symbol} is a clear scam. Don't fall for it.`,\n\t\t\t\t],\n\t\t\t\tneutral: [\n\t\t\t\t\t`Watching $${token.symbol} closely. Need more data before making a call.`,\n\t\t\t\t\t`$${token.symbol} on my radar. Waiting for better entry.`,\n\t\t\t\t],\n\t\t\t},\n\t\t\trug_promoter: {\n\t\t\t\tpositive: [\n\t\t\t\t\t`🚀🚀 $${token.symbol} TO THE MOON! 1000X GEM! GET IN NOW! 🚀🚀`,\n\t\t\t\t\t`$${token.symbol} NEXT 100X!!! DEV DOXXED! LIQUIDITY LOCKED! SAFU! 💎💎`,\n\t\t\t\t\t`BREAKING: $${token.symbol} ABOUT TO EXPLODE! WHALES ACCUMULATING! 🐋`,\n\t\t\t\t],\n\t\t\t\tnegative: [],\n\t\t\t\tneutral: [],\n\t\t\t},\n\t\t\tpump_chaser: {\n\t\t\t\tpositive: [\n\t\t\t\t\t`$${token.symbol} is pumping hard! Just aped in!`,\n\t\t\t\t\t`Holy shit $${token.symbol} is flying! This is going to $1!`,\n\t\t\t\t\t`Everyone talking about $${token.symbol}! Don't miss out!`,\n\t\t\t\t],\n\t\t\t\tnegative: [\n\t\t\t\t\t`Fuck, $${token.symbol} dumping. Should have sold earlier.`,\n\t\t\t\t\t`$${token.symbol} rugged. Lost everything. Stay away.`,\n\t\t\t\t],\n\t\t\t\tneutral: [],\n\t\t\t},\n\t\t\ttechnical_analyst: {\n\t\t\t\tpositive: [\n\t\t\t\t\t`$${token.symbol} breaking key resistance at ${priceHistory[priceHistory.length - 1].price.toFixed(6)}. Next target: ${(priceHistory[priceHistory.length - 1].price * 1.5).toFixed(6)}`,\n\t\t\t\t\t`Bullish divergence on $${token.symbol} 4H chart. Accumulation zone.`,\n\t\t\t\t\t`$${token.symbol} forming cup and handle. Breakout imminent.`,\n\t\t\t\t],\n\t\t\t\tnegative: [\n\t\t\t\t\t`$${token.symbol} lost critical support. Expecting further downside.`,\n\t\t\t\t\t`Death cross forming on $${token.symbol}. Time to exit.`,\n\t\t\t\t],\n\t\t\t\tneutral: [\n\t\t\t\t\t`$${token.symbol} consolidating. Waiting for breakout direction.`,\n\t\t\t\t\t`$${token.symbol} at key level. Could go either way.`,\n\t\t\t\t],\n\t\t\t},\n\t\t};\n\n\t\tconst defaultTemplates = {\n\t\t\tpositive: [`I think $${token.symbol} looks good`],\n\t\t\tnegative: [`$${token.symbol} doesn't look great`],\n\t\t\tneutral: [`Watching $${token.symbol}`],\n\t\t};\n\n\t\tconst archetypeTemplates = templates[actor.archetype] || defaultTemplates;\n\t\tconst sentimentTemplates = archetypeTemplates[sentiment] ||\n\t\t\tarchetypeTemplates.positive ||\n\t\t\tdefaultTemplates[sentiment] || [`Watching $${token.symbol}`];\n\n\t\treturn (\n\t\t\tsentimentTemplates[\n\t\t\t\tMath.floor(Math.random() * sentimentTemplates.length)\n\t\t\t] || `Looking at $${token.symbol}`\n\t\t);\n\t}\n\n\tprivate determineCertainty(\n\t\tactor: SimulatedActorV2,\n\t\t_tokenScenario: TokenScenario,\n\t): \"high\" | \"medium\" | \"low\" {\n\t\tswitch (actor.archetype) {\n\t\t\tcase \"elite_analyst\":\n\t\t\t\treturn \"high\";\n\t\t\tcase \"skilled_trader\":\n\t\t\tcase \"technical_analyst\":\n\t\t\t\treturn \"medium\";\n\t\t\tcase \"newbie\":\n\t\t\tcase \"pump_chaser\":\n\t\t\t\treturn \"low\";\n\t\t\tdefault:\n\t\t\t\treturn \"medium\";\n\t\t}\n\t}\n\n\tprivate generateReasoning(\n\t\tactor: SimulatedActorV2,\n\t\ttoken: SimulatedToken,\n\t\tsentiment: \"positive\" | \"negative\" | \"neutral\",\n\t\tconviction: Conviction,\n\t): string {\n\t\treturn `${actor.username} (${actor.archetype}) made a ${conviction} conviction ${sentiment} call on ${token.symbol}`;\n\t}\n\n\tgetAllActors(): SimulatedActorV2[] {\n\t\treturn Array.from(this.actors.values());\n\t}\n\n\tgetActorById(id: UUID): SimulatedActorV2 | undefined {\n\t\treturn this.actors.get(id);\n\t}\n\n\tgetExpectedRankings(): { username: string; expectedTrustScore: number }[] {\n\t\treturn Array.from(this.actors.values())\n\t\t\t.map((actor) => ({\n\t\t\t\tusername: actor.username,\n\t\t\t\texpectedTrustScore: actor.trustScore || 50,\n\t\t\t}))\n\t\t\t.sort((a, b) => b.expectedTrustScore - a.expectedTrustScore);\n\t}\n}\n"],"mappings":"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","names":[]}