@elizaos/plugin-social-alpha 2.0.3-beta.6 → 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.
- package/dist/clients.d.ts +354 -0
- package/dist/clients.d.ts.map +1 -0
- package/dist/clients.js +670 -0
- package/dist/clients.js.map +1 -0
- package/dist/config.d.ts +144 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +122 -0
- package/dist/config.js.map +1 -0
- package/dist/events.d.ts +5 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +426 -0
- package/dist/events.js.map +1 -0
- package/dist/frontend/LeaderboardView.helpers.d.ts +6 -0
- package/dist/frontend/LeaderboardView.helpers.d.ts.map +1 -0
- package/dist/frontend/LeaderboardView.helpers.js +59 -0
- package/dist/frontend/LeaderboardView.helpers.js.map +1 -0
- package/dist/frontend/SocialAlphaSpatialView.d.ts +52 -0
- package/dist/frontend/SocialAlphaSpatialView.d.ts.map +1 -0
- package/dist/frontend/SocialAlphaSpatialView.js +72 -0
- package/dist/frontend/SocialAlphaSpatialView.js.map +1 -0
- package/dist/frontend/SocialAlphaView.d.ts +35 -0
- package/dist/frontend/SocialAlphaView.d.ts.map +1 -0
- package/dist/frontend/SocialAlphaView.js +125 -0
- package/dist/frontend/SocialAlphaView.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +73 -0
- package/dist/index.js.map +1 -0
- package/dist/mockPriceService.d.ts +22 -0
- package/dist/mockPriceService.d.ts.map +1 -0
- package/dist/mockPriceService.js +21 -0
- package/dist/mockPriceService.js.map +1 -0
- package/dist/providers/socialAlphaProvider.d.ts +15 -0
- package/dist/providers/socialAlphaProvider.d.ts.map +1 -0
- package/dist/providers/socialAlphaProvider.js +261 -0
- package/dist/providers/socialAlphaProvider.js.map +1 -0
- package/dist/register-terminal-view.d.ts +15 -0
- package/dist/register-terminal-view.d.ts.map +1 -0
- package/dist/register-terminal-view.js +21 -0
- package/dist/register-terminal-view.js.map +1 -0
- package/dist/register.d.ts +10 -0
- package/dist/register.d.ts.map +1 -0
- package/dist/register.js +5 -0
- package/dist/register.js.map +1 -0
- package/dist/reports.d.ts +57 -0
- package/dist/reports.d.ts.map +1 -0
- package/dist/reports.js +455 -0
- package/dist/reports.js.map +1 -0
- package/dist/routes.d.ts +3 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +59 -0
- package/dist/routes.js.map +1 -0
- package/dist/schemas.d.ts +151 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +258 -0
- package/dist/schemas.js.map +1 -0
- package/dist/service.d.ts +306 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +3078 -0
- package/dist/service.js.map +1 -0
- package/dist/services/balancedTrustScoreCalculator.d.ts +61 -0
- package/dist/services/balancedTrustScoreCalculator.d.ts.map +1 -0
- package/dist/services/balancedTrustScoreCalculator.js +207 -0
- package/dist/services/balancedTrustScoreCalculator.js.map +1 -0
- package/dist/services/historicalPriceService.d.ts +59 -0
- package/dist/services/historicalPriceService.d.ts.map +1 -0
- package/dist/services/historicalPriceService.js +291 -0
- package/dist/services/historicalPriceService.js.map +1 -0
- package/dist/services/index.d.ts +12 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +17 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/priceEnrichmentService.d.ts +109 -0
- package/dist/services/priceEnrichmentService.d.ts.map +1 -0
- package/dist/services/priceEnrichmentService.js +780 -0
- package/dist/services/priceEnrichmentService.js.map +1 -0
- package/dist/services/simulationActorsV2.d.ts +54 -0
- package/dist/services/simulationActorsV2.d.ts.map +1 -0
- package/dist/services/simulationActorsV2.js +362 -0
- package/dist/services/simulationActorsV2.js.map +1 -0
- package/dist/services/simulationRunner.d.ts +113 -0
- package/dist/services/simulationRunner.d.ts.map +1 -0
- package/dist/services/simulationRunner.js +771 -0
- package/dist/services/simulationRunner.js.map +1 -0
- package/dist/services/tokenSimulationService.d.ts +34 -0
- package/dist/services/tokenSimulationService.d.ts.map +1 -0
- package/dist/services/tokenSimulationService.js +297 -0
- package/dist/services/tokenSimulationService.js.map +1 -0
- package/dist/services/trustScoreOptimizer.d.ts +110 -0
- package/dist/services/trustScoreOptimizer.d.ts.map +1 -0
- package/dist/services/trustScoreOptimizer.js +635 -0
- package/dist/services/trustScoreOptimizer.js.map +1 -0
- package/dist/simulationActors.d.ts +35 -0
- package/dist/simulationActors.d.ts.map +1 -0
- package/dist/simulationActors.js +160 -0
- package/dist/simulationActors.js.map +1 -0
- package/dist/social-alpha-view-bundle.d.ts +2 -0
- package/dist/social-alpha-view-bundle.d.ts.map +1 -0
- package/dist/social-alpha-view-bundle.js +5 -0
- package/dist/social-alpha-view-bundle.js.map +1 -0
- package/dist/types.d.ts +937 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +46 -0
- package/dist/types.js.map +1 -0
- package/dist/views/brand/background/clouds_background.jpg +0 -0
- package/dist/views/brand/banners/eliza_banner.svg +20 -0
- package/dist/views/brand/banners/elizacloud_banner.svg +20 -0
- package/dist/views/brand/banners/elizaos_banner.svg +20 -0
- package/dist/views/brand/concepts/billboard_concept_1200.jpg +0 -0
- package/dist/views/brand/concepts/chibi_usb_concept_900.jpg +0 -0
- package/dist/views/brand/concepts/concept_minipc_900.jpg +0 -0
- package/dist/views/brand/concepts/concept_phone_800.jpg +0 -0
- package/dist/views/brand/concepts/concept_usbdrive_900.jpg +0 -0
- package/dist/views/brand/favicons/android-chrome-192x192.png +0 -0
- package/dist/views/brand/favicons/android-chrome-512x512.png +0 -0
- package/dist/views/brand/favicons/apple-touch-icon.png +0 -0
- package/dist/views/brand/favicons/favicon-16x16.png +0 -0
- package/dist/views/brand/favicons/favicon-32x32.png +0 -0
- package/dist/views/brand/favicons/favicon.ico +0 -0
- package/dist/views/brand/favicons/favicon.svg +17 -0
- package/dist/views/brand/logos/elizaOS_text_black.svg +3 -0
- package/dist/views/brand/logos/elizaOS_text_white.svg +3 -0
- package/dist/views/brand/logos/eliza_logotext.svg +26 -0
- package/dist/views/brand/logos/eliza_logotext_black.svg +26 -0
- package/dist/views/brand/logos/eliza_text_black.svg +3 -0
- package/dist/views/brand/logos/eliza_text_white.svg +3 -0
- package/dist/views/brand/logos/elizacloud_logotext.svg +26 -0
- package/dist/views/brand/logos/elizacloud_logotext_black.svg +26 -0
- package/dist/views/brand/logos/elizacloud_text_black.svg +3 -0
- package/dist/views/brand/logos/elizacloud_text_white.svg +3 -0
- package/dist/views/brand/logos/elizaos_logotext.svg +26 -0
- package/dist/views/brand/logos/elizaos_logotext_black.svg +26 -0
- package/dist/views/brand/logos/logo_blue_blackbg.svg +18 -0
- package/dist/views/brand/logos/logo_blue_nobg.svg +17 -0
- package/dist/views/brand/logos/logo_orange_blackbg.svg +18 -0
- package/dist/views/brand/logos/logo_orange_nobg.svg +17 -0
- package/dist/views/brand/logos/logo_white_blackbg.svg +25 -0
- package/dist/views/brand/logos/logo_white_bluebg.svg +25 -0
- package/dist/views/brand/logos/logo_white_graybg.svg +18 -0
- package/dist/views/brand/logos/logo_white_nobg.svg +24 -0
- package/dist/views/brand/logos/logo_white_orangebg.svg +25 -0
- package/dist/views/brand/ogembeds/eliza_ogembed.png +0 -0
- package/dist/views/brand/ogembeds/eliza_ogembed.svg +20 -0
- package/dist/views/brand/ogembeds/elizacloud_ogembed.png +0 -0
- package/dist/views/brand/ogembeds/elizacloud_ogembed.svg +20 -0
- package/dist/views/brand/ogembeds/elizaos_ogembed.png +0 -0
- package/dist/views/brand/ogembeds/elizaos_ogembed.svg +20 -0
- package/dist/views/bundle.js +268 -0
- package/dist/views/bundle.js.map +1 -0
- package/dist/views/site.webmanifest +19 -0
- package/package.json +5 -5
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/service.ts"],"sourcesContent":["import {\n\tasUUID,\n\tChannelType,\n\tlogger as coreLogger,\n\tcreateUniqueUuid,\n\ttype Entity,\n\ttype IAgentRuntime,\n\ttype JsonValue,\n\ttype Memory,\n\tModelType,\n\tService,\n\ttype Task,\n\ttype UUID,\n} from \"@elizaos/core\";\n\nfunction toJsonRecord(value: unknown): { [key: string]: JsonValue } {\n\treturn JSON.parse(JSON.stringify(value ?? {})) as {\n\t\t[key: string]: JsonValue;\n\t};\n}\n\nfunction logValue(value: unknown): string {\n\tif (typeof value === \"string\") return value;\n\tif (value instanceof Error) return value.stack ?? value.message;\n\ttry {\n\t\treturn JSON.stringify(value);\n\t} catch {\n\t\treturn String(value);\n\t}\n}\n\nconst logger = {\n\tdebug: (...args: unknown[]) => coreLogger.debug(args.map(logValue).join(\" \")),\n\tinfo: (...args: unknown[]) => coreLogger.info(args.map(logValue).join(\" \")),\n\twarn: (...args: unknown[]) => coreLogger.warn(args.map(logValue).join(\" \")),\n\terror: (...args: unknown[]) => coreLogger.error(args.map(logValue).join(\" \")),\n};\n\nimport { v4 as uuidv4 } from \"uuid\";\nimport { BirdeyeClient, DexscreenerClient, HeliusClient } from \"./clients.js\";\nimport {\n\tDEFAULT_TRADING_CONFIG,\n\tgetConvictionMultiplier,\n\tgetLiquidityMultiplier,\n\tgetMarketCapMultiplier,\n\tgetVolumeMultiplier,\n\tTRUST_LEADERBOARD_WORLD_SEED,\n\ttype TradingConfig,\n} from \"./config.js\"; // Import the seed\nimport { formatFullReport } from \"./reports.js\";\nimport { BalancedTrustScoreCalculator } from \"./services/balancedTrustScoreCalculator.js\";\nimport {\n\ttype BuySignalMessage,\n\tConviction,\n\ttype HighValueHolder,\n\ttype ICommunityInvestorService,\n\ttype LeaderboardEntry,\n\ttype Position,\n\ttype PositionWithBalance,\n\ttype ProcessedTokenData,\n\ttype Recommendation,\n\ttype RecommendationMetric,\n\tRecommendationType,\n\ttype RecommenderMetrics,\n\ttype RecommenderMetricsHistory,\n\tServiceType,\n\tSupportedChain,\n\ttype TokenAPIData,\n\ttype TokenMarketData,\n\ttype TokenMetadata,\n\ttype TokenPerformance,\n\ttype TokenRecommendation,\n\ttype TokenSecurityData,\n\ttype TokenTradeData,\n\tTRUST_MARKETPLACE_COMPONENT_TYPE,\n\ttype Transaction,\n\tTransactionType,\n\ttype TrustMarketplaceComponentData,\n\ttype UserTrustProfile,\n} from \"./types.js\";\n\ntype ExtractedSignal = {\n\tmessageIndex: number | string;\n\tisCall: boolean | string;\n\ttokenMentioned?: string;\n\tnameMentioned?: string;\n\tcaMentioned?: string;\n\tchain?: string;\n\tsentiment?: string;\n\tconviction?: string;\n\tllmReasoning?: string;\n};\n\n/** Shape of one message inside a historical batch file. */\ninterface HistoricalBatchMessage {\n\tid: UUID;\n\tuid: string;\n\tts: string | number;\n\tcontent: string;\n}\n\ntype NormalizedExtractedSignal = Omit<\n\tExtractedSignal,\n\t\"messageIndex\" | \"isCall\"\n> & {\n\tmessageIndex: number;\n\tisCall: boolean;\n};\n\nfunction parseRecommendationExtraction(response: string): {\n\trecommendations: NormalizedExtractedSignal[];\n} | null {\n\tconst parsed = parseJsonObject<{ recommendations?: ExtractedSignal[] }>(\n\t\tresponse,\n\t);\n\n\tif (!parsed?.recommendations || !Array.isArray(parsed.recommendations)) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\trecommendations: parsed.recommendations.map((rec) => ({\n\t\t\t...rec,\n\t\t\tmessageIndex:\n\t\t\t\ttypeof rec.messageIndex === \"number\"\n\t\t\t\t\t? rec.messageIndex\n\t\t\t\t\t: Number.parseInt(String(rec.messageIndex ?? \"0\"), 10),\n\t\t\tisCall:\n\t\t\t\trec.isCall === true || String(rec.isCall).toLowerCase() === \"true\",\n\t\t})),\n\t};\n}\n\nfunction parseJsonObject<T extends Record<string, unknown>>(\n\tvalue: string,\n): T | null {\n\ttry {\n\t\tconst parsed: unknown = JSON.parse(value.trim());\n\t\treturn parsed && typeof parsed === \"object\" && !Array.isArray(parsed)\n\t\t\t? (parsed as T)\n\t\t\t: null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n// Event types\n/**\n * Represents different types of trading events that can occur.\n * @typedef {Object} TradingEvent\n * @property {string} type - The type of trading event.\n * @property {Position} [position] - The position associated with the event. (if type is 'position_opened' or 'position_closed')\n * @property {Transaction} [transaction] - The transaction associated with the event. (if type is 'transaction_added')\n * @property {TokenRecommendation} [recommendation] - The token recommendation associated with the event. (if type is 'recommendation_added')\n * @property {TokenPerformance} [performance] - The token performance associated with the event. (if type is 'token_performance_updated')\n */\nexport type TradingEvent =\n\t| { type: \"position_opened\"; position: Position }\n\t| { type: \"position_closed\"; position: Position }\n\t| { type: \"transaction_added\"; transaction: Transaction }\n\t| { type: \"recommendation_added\"; recommendation: TokenRecommendation }\n\t| { type: \"token_performance_updated\"; performance: TokenPerformance };\n\n/**\n * Trading Service that centralizes all trading operations\n */\n\n/**\n * Narrow a memory's loosely-typed `content.transaction` field to a Transaction.\n * Returns null when the field is not a transaction-shaped object. The input is a\n * widened `ContentValue` union, so the internal casts are plain `as`\n * (ratchet-neutral); this replaces the scattered `as unknown as Transaction`\n * reads with one validated path.\n */\nfunction asContentObject<T>(\n\tvalue: unknown,\n\trequiredStringKeys: readonly string[],\n): T | null {\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\treturn null;\n\t}\n\tconst record = value as Record<string, unknown>;\n\tfor (const key of requiredStringKeys) {\n\t\tif (typeof record[key] !== \"string\") {\n\t\t\treturn null;\n\t\t}\n\t}\n\treturn value as T;\n}\n\nfunction asTransaction(value: unknown): Transaction | null {\n\treturn asContentObject<Transaction>(value, [\"positionId\", \"tokenAddress\"]);\n}\n\n/**\n * CommunityInvestorService class representing a service for trading on the Solana blockchain.\n * @extends Service\n * @property {string} serviceType - The type of service, set to ServiceType.COMMUNITY_INVESTOR.\n * @property {string} capabilityDescription - Description of the agent's ability to trade on the Solana blockchain.\n * @method storeRecommenderMetrics - Store entity metrics and cache for 5 minutes.\n * @method storeRecommenderMetricsHistory - Store entity metrics history.\n */\nexport class CommunityInvestorService\n\textends Service\n\timplements ICommunityInvestorService\n{\n\tstatic override serviceType = ServiceType.COMMUNITY_INVESTOR;\n\tpublic capabilityDescription =\n\t\t\"Manages community-driven investment trust scores and recommendations.\";\n\n\t// Client instances\n\tprivate birdeyeClient: BirdeyeClient;\n\tprivate dexscreenerClient: DexscreenerClient;\n\tprivate heliusClient: HeliusClient | null = null;\n\n\t// Configuration\n\ttradingConfig: TradingConfig;\n\n\tprivate apiKeys: {\n\t\tbirdeye?: string;\n\t\tmoralis?: string;\n\t\tdexscreener?: string;\n\t} = {};\n\n\tprivate balancedTrustCalculator: BalancedTrustScoreCalculator;\n\n\t// Constants can be defined here or loaded from config/settings\n\tprivate readonly POSITIVE_TRADE_THRESHOLD = 10; // Trust score above this might trigger a trade\n\tprivate readonly NEUTRAL_MARGIN = 5; // Trust scores within +/- this from 0 are neutral\n\tprivate readonly RECENCY_WEIGHT_MONTHS = 6;\n\tprivate readonly USER_TRADE_COOLDOWN_HOURS = 12;\n\tprivate readonly METRIC_REFRESH_INTERVAL = 24 * 60 * 60 * 1000; // 1 day to re-evaluate metrics\n\n\t// Add this property to the class\n\tprivate userRegistry: Set<UUID> = new Set();\n\tpublic readonly componentWorldId: UUID;\n\tpublic readonly componentRoomId: UUID; // This will be the same as componentWorldId\n\n\tconstructor(runtime?: IAgentRuntime) {\n\t\tif (!runtime) {\n\t\t\tthrow new Error(\"CommunityInvestorService requires an agent runtime\");\n\t\t}\n\t\tsuper(runtime);\n\t\tthis.runtime = runtime;\n\n\t\t// Initialize the balanced trust calculator\n\t\tthis.balancedTrustCalculator = new BalancedTrustScoreCalculator();\n\n\t\t// Generate the consistent World ID and Room ID for this plugin's components\n\t\tthis.componentWorldId = createUniqueUuid(\n\t\t\truntime,\n\t\t\tTRUST_LEADERBOARD_WORLD_SEED,\n\t\t);\n\t\tthis.componentRoomId = this.componentWorldId; // Use the same ID for room context of components\n\t\tlogger.info(\n\t\t\t`[CommunityInvestorService] Using Component World/Room ID: ${this.componentWorldId}`,\n\t\t);\n\n\t\t// Ensure this world and room exist for the plugin's components\n\t\tthis.ensurePluginComponentContext();\n\n\t\t// Initialize API clients\n\t\tthis.birdeyeClient = BirdeyeClient.createFromRuntime(runtime);\n\t\tthis.dexscreenerClient = DexscreenerClient.createFromRuntime(runtime);\n\n\t\ttry {\n\t\t\tthis.heliusClient = HeliusClient.createFromRuntime(runtime);\n\t\t} catch (error) {\n\t\t\tlogger.warn(\n\t\t\t\t\"Failed to initialize Helius client, holder data will be limited:\",\n\t\t\t\terror,\n\t\t\t);\n\t\t}\n\n\t\t// Merge provided config with defaults\n\t\tthis.tradingConfig = DEFAULT_TRADING_CONFIG;\n\n\t\tthis.initialize(runtime);\n\t\tthis.registerTaskWorkers(runtime); // Register task workers on service instantiation\n\t}\n\n\tstatic async start(\n\t\truntime: IAgentRuntime,\n\t): Promise<CommunityInvestorService> {\n\t\tconst service = new CommunityInvestorService(runtime);\n\t\treturn service;\n\t}\n\n\tstatic async stop(runtime: IAgentRuntime): Promise<void> {\n\t\tconst service = runtime.getService(\"trading\");\n\t\tif (service) {\n\t\t\tawait service.stop?.();\n\t\t}\n\t}\n\n\tasync stop(): Promise<void> {\n\t\treturn Promise.resolve();\n\t}\n\n\t/**\n\t * Process a buy signal from an entity\n\t */\n\tasync processBuySignal(\n\t\tbuySignal: BuySignalMessage,\n\t\tentity: Entity,\n\t): Promise<Position | null> {\n\t\tlogger.debug(\"processing buy signal\", buySignal, entity);\n\t\ttry {\n\t\t\t// Ensure entity has a valid ID\n\t\t\tif (!entity.id) {\n\t\t\t\tlogger.error(\"Entity ID is required for processing buy signal\");\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// Validate the token\n\t\t\tconst tokenPerformance = await this.getOrFetchTokenPerformance(\n\t\t\t\tbuySignal.tokenAddress,\n\t\t\t\tbuySignal.chain || this.tradingConfig.defaultChain,\n\t\t\t);\n\n\t\t\tif (!tokenPerformance) {\n\t\t\t\tlogger.error(`Token not found: ${buySignal.tokenAddress}`);\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// Check if token meets criteria\n\t\t\tif (!this.validateToken(tokenPerformance)) {\n\t\t\t\tlogger.error(`Token failed validation: ${buySignal.tokenAddress}`);\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// Create recommendation\n\t\t\tconst recommendation = await this.createTokenRecommendation(\n\t\t\t\tentity.id,\n\t\t\t\ttokenPerformance,\n\t\t\t\tbuySignal.conviction || Conviction.MEDIUM,\n\t\t\t\tRecommendationType.BUY,\n\t\t\t);\n\n\t\t\tif (!recommendation) {\n\t\t\t\tlogger.error(\n\t\t\t\t\t`Failed to create recommendation for token: ${buySignal.tokenAddress}`,\n\t\t\t\t);\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// Calculate buy amount\n\t\t\tconst buyAmount = this.calculateBuyAmount(\n\t\t\t\tentity,\n\t\t\t\tbuySignal.conviction || Conviction.MEDIUM,\n\t\t\t\ttokenPerformance,\n\t\t\t);\n\n\t\t\t// Create position\n\t\t\tconst position = await this.createPosition(\n\t\t\t\trecommendation.id,\n\t\t\t\tentity.id,\n\t\t\t\tbuySignal.tokenAddress,\n\t\t\t\tbuySignal.walletAddress || \"simulation\",\n\t\t\t\tbuyAmount,\n\t\t\t\ttokenPerformance.price?.toString() || \"0\",\n\t\t\t\tbuySignal.isSimulation || this.tradingConfig.forceSimulation,\n\t\t\t);\n\n\t\t\tif (!position) {\n\t\t\t\tlogger.error(\n\t\t\t\t\t`Failed to create position for token: ${buySignal.tokenAddress}`,\n\t\t\t\t);\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// Record transaction\n\t\t\tawait this.recordTransaction(\n\t\t\t\tposition.id as UUID,\n\t\t\t\tbuySignal.tokenAddress,\n\t\t\t\tTransactionType.BUY,\n\t\t\t\tbuyAmount,\n\t\t\t\ttokenPerformance.price || 0,\n\t\t\t\tposition.isSimulation,\n\t\t\t);\n\n\t\t\t// Emit event\n\t\t\t// this.emitEvent({ type: 'position_opened', position });\n\n\t\t\treturn position;\n\t\t} catch (error) {\n\t\t\tlogger.error(\"Error processing buy signal:\", error);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Process a sell signal for an existing position\n\t */\n\tasync processSellSignal(\n\t\tpositionId: UUID,\n\t\t_sellRecommenderId: UUID,\n\t): Promise<boolean> {\n\t\ttry {\n\t\t\tlogger.debug(\"processing sell signal\", positionId, _sellRecommenderId);\n\t\t\t// Get position\n\t\t\tconst position = await this.getPosition(positionId);\n\t\t\tif (!position) {\n\t\t\t\tlogger.error(`Position not found: ${positionId}`);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Check if position is already closed\n\t\t\tif (position.closedAt) {\n\t\t\t\tlogger.error(`Position already closed: ${positionId}`);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Get token performance\n\t\t\tconst tokenPerformance = await this.getOrFetchTokenPerformance(\n\t\t\t\tposition.tokenAddress,\n\t\t\t\tposition.chain,\n\t\t\t);\n\n\t\t\tif (!tokenPerformance) {\n\t\t\t\tlogger.error(`Token not found: ${position.tokenAddress}`);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Calculate performance metrics\n\t\t\tconst initialPrice = Number.parseFloat(position.initialPrice);\n\t\t\tconst currentPrice = tokenPerformance.price || 0;\n\t\t\tconst priceChange =\n\t\t\t\tinitialPrice > 0 ? (currentPrice - initialPrice) / initialPrice : 0;\n\n\t\t\t// Update position\n\t\t\tconst updatedPosition: Position = {\n\t\t\t\t...position,\n\t\t\t\tcurrentPrice: currentPrice.toString(),\n\t\t\t\tclosedAt: new Date(),\n\t\t\t};\n\n\t\t\t// Store updated position\n\t\t\tawait this.storePosition(updatedPosition);\n\n\t\t\t// Record transaction\n\t\t\tawait this.recordTransaction(\n\t\t\t\tposition.id as UUID,\n\t\t\t\tposition.tokenAddress,\n\t\t\t\tTransactionType.SELL,\n\t\t\t\tBigInt(position.amount),\n\t\t\t\tcurrentPrice,\n\t\t\t\tposition.isSimulation,\n\t\t\t);\n\n\t\t\t// Update entity metrics\n\t\t\tawait this.updateRecommenderMetrics(position.entityId, priceChange * 100);\n\n\t\t\t// Emit event\n\t\t\t// this.emitEvent({ type: 'position_closed', position: updatedPosition });\n\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tlogger.error(\"Error processing sell signal:\", error);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Handle a recommendation from a entity\n\t */\n\tasync handleRecommendation(\n\t\tentity: Entity,\n\t\trecommendation: {\n\t\t\tchain: string;\n\t\t\ttokenAddress: string;\n\t\t\tconviction: Conviction;\n\t\t\ttype: RecommendationType;\n\t\t\ttimestamp: Date;\n\t\t\tmetadata?: Record<string, unknown>;\n\t\t},\n\t): Promise<Position | null> {\n\t\ttry {\n\t\t\tlogger.debug(\"handling recommendation\", entity, recommendation);\n\n\t\t\t// Ensure entity has a valid ID\n\t\t\tif (!entity.id) {\n\t\t\t\tlogger.error(\"Entity ID is required for handling recommendation\");\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// Get token performance\n\t\t\tconst tokenPerformance = await this.getOrFetchTokenPerformance(\n\t\t\t\trecommendation.tokenAddress,\n\t\t\t\trecommendation.chain,\n\t\t\t);\n\n\t\t\tif (!tokenPerformance) {\n\t\t\t\tlogger.error(`Token not found: ${recommendation.tokenAddress}`);\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// Create recommendation\n\t\t\tconst tokenRecommendation = await this.createTokenRecommendation(\n\t\t\t\tentity.id,\n\t\t\t\ttokenPerformance,\n\t\t\t\trecommendation.conviction,\n\t\t\t\trecommendation.type,\n\t\t\t);\n\n\t\t\tif (!tokenRecommendation) {\n\t\t\t\tlogger.error(\n\t\t\t\t\t`Failed to create recommendation for token: ${recommendation.tokenAddress}`,\n\t\t\t\t);\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// For buy recommendations, create a position\n\t\t\tif (recommendation.type === RecommendationType.BUY) {\n\t\t\t\t// Calculate buy amount\n\t\t\t\tconst buyAmount = this.calculateBuyAmount(\n\t\t\t\t\tentity,\n\t\t\t\t\trecommendation.conviction,\n\t\t\t\t\ttokenPerformance,\n\t\t\t\t);\n\n\t\t\t\t// Create position\n\t\t\t\tconst position = await this.createPosition(\n\t\t\t\t\ttokenRecommendation.id,\n\t\t\t\t\tentity.id,\n\t\t\t\t\trecommendation.tokenAddress,\n\t\t\t\t\t\"simulation\", // Use simulation wallet by default\n\t\t\t\t\tbuyAmount,\n\t\t\t\t\ttokenPerformance.price?.toString() || \"0\",\n\t\t\t\t\ttrue, // Simulation by default\n\t\t\t\t);\n\n\t\t\t\tif (!position) {\n\t\t\t\t\tlogger.error(\n\t\t\t\t\t\t`Failed to create position for token: ${recommendation.tokenAddress}`,\n\t\t\t\t\t);\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\n\t\t\t\t// Record transaction\n\t\t\t\tawait this.recordTransaction(\n\t\t\t\t\tposition.id as UUID,\n\t\t\t\t\trecommendation.tokenAddress,\n\t\t\t\t\tTransactionType.BUY,\n\t\t\t\t\tbuyAmount,\n\t\t\t\t\ttokenPerformance.price || 0,\n\t\t\t\t\ttrue, // Simulation by default\n\t\t\t\t);\n\n\t\t\t\t// Return position\n\t\t\t\treturn position;\n\t\t\t}\n\n\t\t\treturn null;\n\t\t} catch (error) {\n\t\t\tlogger.error(\"Error handling recommendation:\", error);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Check if a wallet is registered for a chain\n\t */\n\thasWallet(chain: string): boolean {\n\t\tlogger.debug(\"hasWallet\", chain);\n\t\t// This implementation would check if a wallet config exists for the specified chain\n\t\treturn chain.toLowerCase() === \"solana\"; // Assuming Solana is always supported\n\t}\n\n\t// ===================== TOKEN PROVIDER METHODS =====================\n\n\t/**\n\t * Get token overview data\n\t */\n\tasync getTokenOverview(\n\t\tchain: string,\n\t\ttokenAddress: string,\n\t\tforceRefresh = false,\n\t): Promise<TokenMetadata & TokenMarketData> {\n\t\ttry {\n\t\t\tlogger.debug(\"getting token overview\", chain, tokenAddress, forceRefresh);\n\t\t\t// Check cache first unless force refresh is requested\n\t\t\tif (!forceRefresh) {\n\t\t\t\tconst cacheKey = `token:${chain}:${tokenAddress}:overview`;\n\t\t\t\tconst cachedData = await this.runtime.getCache<\n\t\t\t\t\tTokenMetadata & TokenMarketData\n\t\t\t\t>(cacheKey);\n\n\t\t\t\tif (cachedData) {\n\t\t\t\t\treturn cachedData;\n\t\t\t\t}\n\n\t\t\t\t// Also check in memory\n\t\t\t\tconst tokenPerformance = await this.getTokenPerformance(\n\t\t\t\t\ttokenAddress,\n\t\t\t\t\tchain,\n\t\t\t\t);\n\t\t\t\tif (tokenPerformance) {\n\t\t\t\t\tconst tokenData = {\n\t\t\t\t\t\tchain: tokenPerformance.chain || chain,\n\t\t\t\t\t\taddress: tokenPerformance.address || tokenAddress,\n\t\t\t\t\t\tname: tokenPerformance.name || \"\",\n\t\t\t\t\t\tsymbol: tokenPerformance.symbol || \"\",\n\t\t\t\t\t\tdecimals: tokenPerformance.decimals || 0,\n\t\t\t\t\t\tmetadata: tokenPerformance.metadata || {},\n\t\t\t\t\t\tprice: tokenPerformance.price || 0,\n\t\t\t\t\t\tpriceUsd: tokenPerformance.price?.toString() || \"0\",\n\t\t\t\t\t\tprice24hChange: tokenPerformance.price24hChange || 0,\n\t\t\t\t\t\tmarketCap: tokenPerformance.currentMarketCap || 0,\n\t\t\t\t\t\tliquidityUsd: tokenPerformance.liquidity || 0,\n\t\t\t\t\t\tvolume24h: tokenPerformance.volume || 0,\n\t\t\t\t\t\tvolume24hChange: tokenPerformance.volume24hChange || 0,\n\t\t\t\t\t\ttrades: tokenPerformance.trades || 0,\n\t\t\t\t\t\ttrades24hChange: tokenPerformance.trades24hChange || 0,\n\t\t\t\t\t\tuniqueWallet24h: 0, // Would need to be fetched\n\t\t\t\t\t\tuniqueWallet24hChange: 0, // Would need to be fetched\n\t\t\t\t\t\tholders: tokenPerformance.holders || 0,\n\t\t\t\t\t};\n\n\t\t\t\t\t// Cache the token data\n\t\t\t\t\tawait this.runtime.setCache<TokenMetadata & TokenMarketData>(\n\t\t\t\t\t\tcacheKey,\n\t\t\t\t\t\ttokenData,\n\t\t\t\t\t); // Cache for 5 minutes\n\n\t\t\t\t\treturn tokenData;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Need to fetch fresh data\n\t\t\tif (chain.toLowerCase() === \"solana\") {\n\t\t\t\tconst [dexScreenerData, birdeyeData] = await Promise.all([\n\t\t\t\t\tthis.dexscreenerClient.searchForHighestLiquidityPair(\n\t\t\t\t\t\ttokenAddress,\n\t\t\t\t\t\tchain,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\texpires: \"5m\",\n\t\t\t\t\t\t},\n\t\t\t\t\t),\n\t\t\t\t\tthis.birdeyeClient.fetchTokenOverview(\n\t\t\t\t\t\ttokenAddress,\n\t\t\t\t\t\t{ expires: \"5m\" },\n\t\t\t\t\t\tforceRefresh,\n\t\t\t\t\t),\n\t\t\t\t]);\n\n\t\t\t\t// If we have DexScreener data, it's typically more reliable for prices and liquidity\n\t\t\t\tconst tokenData = {\n\t\t\t\t\tchain,\n\t\t\t\t\taddress: tokenAddress,\n\t\t\t\t\tname: birdeyeData?.name || dexScreenerData?.baseToken?.name || \"\",\n\t\t\t\t\tsymbol:\n\t\t\t\t\t\tbirdeyeData?.symbol || dexScreenerData?.baseToken?.symbol || \"\",\n\t\t\t\t\tdecimals: birdeyeData?.decimals || 9, // Default for Solana tokens\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\tlogoURI: birdeyeData?.logoURI || \"\",\n\t\t\t\t\t\tpairAddress: dexScreenerData?.pairAddress || \"\",\n\t\t\t\t\t\tdexId: dexScreenerData?.dexId || \"\",\n\t\t\t\t\t},\n\t\t\t\t\tprice: Number.parseFloat(dexScreenerData?.priceUsd || \"0\"),\n\t\t\t\t\tpriceUsd: dexScreenerData?.priceUsd || \"0\",\n\t\t\t\t\tprice24hChange: dexScreenerData?.priceChange?.h24 || 0,\n\t\t\t\t\tmarketCap: dexScreenerData?.marketCap || 0,\n\t\t\t\t\tliquidityUsd: dexScreenerData?.liquidity?.usd || 0,\n\t\t\t\t\tvolume24h: dexScreenerData?.volume?.h24 || 0,\n\t\t\t\t\tvolume24hChange: 0, // Need to calculate from historical data\n\t\t\t\t\ttrades: 0, // Would need additional data\n\t\t\t\t\ttrades24hChange: 0, // Would need additional data\n\t\t\t\t\tuniqueWallet24h: 0, // Would need additional data\n\t\t\t\t\tuniqueWallet24hChange: 0, // Would need additional data\n\t\t\t\t\tholders: 0,\n\t\t\t\t};\n\n\t\t\t\t// Cache the token data\n\t\t\t\tconst cacheKey = `token:${chain}:${tokenAddress}:overview`;\n\t\t\t\tawait this.runtime.setCache<TokenMetadata & TokenMarketData>(\n\t\t\t\t\tcacheKey,\n\t\t\t\t\ttokenData,\n\t\t\t\t); // Cache for 5 minutes\n\n\t\t\t\treturn tokenData;\n\t\t\t}\n\t\t\tthrow new Error(`Chain ${chain} not supported`);\n\t\t} catch (error) {\n\t\t\tlogger.error(`Error fetching token overview for ${tokenAddress}:`, error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Resolve a ticker to a token address\n\t */\n\tasync resolveTicker(\n\t\tticker: string,\n\t\tchain: SupportedChain = SupportedChain.SOLANA,\n\t\tcontextMessages?: Memory[], // Context might be used to disambiguate if multiple matches\n\t): Promise<{\n\t\taddress: string;\n\t\tchain: SupportedChain;\n\t\tticker?: string;\n\t} | null> {\n\t\tlogger.debug(\n\t\t\t`[CommunityInvestorService] Attempting to resolve ticker \"${ticker}\" on chain ${chain}`,\n\t\t);\n\n\t\tconst cleanTicker = ticker.startsWith(\"$\")\n\t\t\t? ticker.substring(1).toUpperCase()\n\t\t\t: ticker.toUpperCase();\n\n\t\t// Check context messages first if they contain a contract address for this ticker recently\n\t\tif (contextMessages) {\n\t\t\tfor (const msg of contextMessages.slice().reverse()) {\n\t\t\t\t// Check recent first\n\t\t\t\tif (\n\t\t\t\t\tmsg.content?.text?.includes(ticker) &&\n\t\t\t\t\tmsg.content.text.length > ticker.length + 5\n\t\t\t\t) {\n\t\t\t\t\t// Basic check for potential address patterns\n\t\t\t\t\t// Split by spaces, parentheses, commas, then iterate and validate\n\t\t\t\t\tconst potentialAddressParts = msg.content.text.split(/[\\s(),]+/);\n\t\t\t\t\tfor (const part of potentialAddressParts) {\n\t\t\t\t\t\t// Solana addresses: 32-44 alphanumeric characters\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tchain === SupportedChain.SOLANA &&\n\t\t\t\t\t\t\tpart.length >= 32 &&\n\t\t\t\t\t\t\tpart.length <= 44 &&\n\t\t\t\t\t\t\t/^[a-zA-Z0-9]+$/.test(part)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tlogger.info(\n\t\t\t\t\t\t\t\t`[CommunityInvestorService] Found potential Solana address ${part} for ticker ${ticker} in context.`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\taddress: part,\n\t\t\t\t\t\t\t\tchain: SupportedChain.SOLANA,\n\t\t\t\t\t\t\t\tticker: cleanTicker,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Ethereum/Base addresses: 0x followed by 40 hex characters\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t(chain === SupportedChain.ETHEREUM ||\n\t\t\t\t\t\t\t\tchain === SupportedChain.BASE) &&\n\t\t\t\t\t\t\tpart.length === 42 &&\n\t\t\t\t\t\t\tpart.toLowerCase().startsWith(\"0x\") &&\n\t\t\t\t\t\t\t/^0x[a-fA-F0-9]{40}$/.test(part)\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tlogger.info(\n\t\t\t\t\t\t\t\t`[CommunityInvestorService] Found potential Ethereum/Base address ${part} for ticker ${ticker} in context.`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\taddress: part,\n\t\t\t\t\t\t\t\tchain: chain,\n\t\t\t\t\t\t\t\tticker: cleanTicker,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Known tokens on Solana\n\t\tif (chain === SupportedChain.SOLANA) {\n\t\t\tconst knownSolanaTokens: Record<string, string> = {\n\t\t\t\tSOL: \"So11111111111111111111111111111111111111112\",\n\t\t\t\tUSDC: \"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v\",\n\t\t\t\tUSDT: \"Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB\",\n\t\t\t\tWIF: \"EKpQGSJtjMFqKZ9KQanSqYXRcF8fBopzL7WDb43cuQu2\",\n\t\t\t\tBONK: \"DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263\",\n\t\t\t\tJUP: \"JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN\",\n\t\t\t\tRAY: \"4k3Dyjzvzp8eMZWUXbBCjEvwSkkk59S5iCNLY3QrkX6R\",\n\t\t\t\tORCA: \"orcaEKTdK7LKz57vaAYr9QeNsVEPfiu6QeMU1kektZE\",\n\t\t\t\tSRM: \"SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt\",\n\t\t\t\tFTT: \"AGFEad2et2ZJif9jaGpdMixQqvW5i81aBdvKe7PHNfz3\",\n\t\t\t};\n\n\t\t\tif (knownSolanaTokens[cleanTicker]) {\n\t\t\t\treturn {\n\t\t\t\t\taddress: knownSolanaTokens[cleanTicker],\n\t\t\t\t\tchain: SupportedChain.SOLANA,\n\t\t\t\t\tticker: cleanTicker,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Try to search using DexScreener for Solana\n\t\t\ttry {\n\t\t\t\tconst searchResults = await this.dexscreenerClient.search(cleanTicker, {\n\t\t\t\t\texpires: \"5m\",\n\t\t\t\t});\n\t\t\t\tif (searchResults?.pairs && searchResults.pairs.length > 0) {\n\t\t\t\t\t// Find the most liquid pair for this token\n\t\t\t\t\tconst bestPair = searchResults.pairs\n\t\t\t\t\t\t.filter(\n\t\t\t\t\t\t\t(pair) => pair.baseToken.symbol.toUpperCase() === cleanTicker,\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.sort(\n\t\t\t\t\t\t\t(a, b) => (b.liquidity?.usd || 0) - (a.liquidity?.usd || 0),\n\t\t\t\t\t\t)[0];\n\n\t\t\t\t\tif (bestPair) {\n\t\t\t\t\t\tlogger.info(\n\t\t\t\t\t\t\t`[CommunityInvestorService] Found ${cleanTicker} via DexScreener: ${bestPair.baseToken.address}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\taddress: bestPair.baseToken.address,\n\t\t\t\t\t\t\tchain: SupportedChain.SOLANA,\n\t\t\t\t\t\t\tticker: cleanTicker,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`[CommunityInvestorService] DexScreener search failed for ${cleanTicker}:`,\n\t\t\t\t\terror,\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<string, string> = {\n\t\t\t\tETH: \"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\", // WETH\n\t\t\t\tUSDC: \"0xA0b86a33E6441c69De69b9A87e20b88dd75B61FC\",\n\t\t\t\tUSDT: \"0xdAC17F958D2ee523a2206206994597C13D831ec7\",\n\t\t\t\tDAI: \"0x6B175474E89094C44Da98b954EedeAC495271d0F\",\n\t\t\t\tLINK: \"0x514910771AF9Ca656af840dff83E8264EcF986CA\",\n\t\t\t\tUNI: \"0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984\",\n\t\t\t\tWBTC: \"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599\",\n\t\t\t};\n\n\t\t\tif (knownEthereumTokens[cleanTicker]) {\n\t\t\t\treturn {\n\t\t\t\t\taddress: knownEthereumTokens[cleanTicker],\n\t\t\t\t\tchain: SupportedChain.ETHEREUM,\n\t\t\t\t\tticker: cleanTicker,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Try to search using DexScreener for Ethereum\n\t\t\ttry {\n\t\t\t\tconst searchResults = await this.dexscreenerClient.search(cleanTicker, {\n\t\t\t\t\texpires: \"5m\",\n\t\t\t\t});\n\t\t\t\tif (searchResults?.pairs && searchResults.pairs.length > 0) {\n\t\t\t\t\tconst bestPair = searchResults.pairs\n\t\t\t\t\t\t.filter(\n\t\t\t\t\t\t\t(pair) =>\n\t\t\t\t\t\t\t\tpair.chainId.toLowerCase() === \"ethereum\" &&\n\t\t\t\t\t\t\t\tpair.baseToken.symbol.toUpperCase() === cleanTicker,\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.sort(\n\t\t\t\t\t\t\t(a, b) => (b.liquidity?.usd || 0) - (a.liquidity?.usd || 0),\n\t\t\t\t\t\t)[0];\n\n\t\t\t\t\tif (bestPair) {\n\t\t\t\t\t\tlogger.info(\n\t\t\t\t\t\t\t`[CommunityInvestorService] Found ${cleanTicker} via DexScreener on Ethereum: ${bestPair.baseToken.address}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\taddress: bestPair.baseToken.address,\n\t\t\t\t\t\t\tchain: SupportedChain.ETHEREUM,\n\t\t\t\t\t\t\tticker: cleanTicker,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`[CommunityInvestorService] DexScreener search failed for ${cleanTicker} on Ethereum:`,\n\t\t\t\t\terror,\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, string> = {\n\t\t\t\tETH: \"0x4200000000000000000000000000000000000006\", // Base ETH\n\t\t\t\tUSDC: \"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\",\n\t\t\t\tWETH: \"0x4200000000000000000000000000000000000006\",\n\t\t\t};\n\n\t\t\tif (knownBaseTokens[cleanTicker]) {\n\t\t\t\treturn {\n\t\t\t\t\taddress: knownBaseTokens[cleanTicker],\n\t\t\t\t\tchain: SupportedChain.BASE,\n\t\t\t\t\tticker: cleanTicker,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Try to search using DexScreener for Base\n\t\t\ttry {\n\t\t\t\tconst searchResults = await this.dexscreenerClient.search(cleanTicker, {\n\t\t\t\t\texpires: \"5m\",\n\t\t\t\t});\n\t\t\t\tif (searchResults?.pairs && searchResults.pairs.length > 0) {\n\t\t\t\t\tconst bestPair = searchResults.pairs\n\t\t\t\t\t\t.filter(\n\t\t\t\t\t\t\t(pair) =>\n\t\t\t\t\t\t\t\tpair.chainId.toLowerCase() === \"base\" &&\n\t\t\t\t\t\t\t\tpair.baseToken.symbol.toUpperCase() === cleanTicker,\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.sort(\n\t\t\t\t\t\t\t(a, b) => (b.liquidity?.usd || 0) - (a.liquidity?.usd || 0),\n\t\t\t\t\t\t)[0];\n\n\t\t\t\t\tif (bestPair) {\n\t\t\t\t\t\tlogger.info(\n\t\t\t\t\t\t\t`[CommunityInvestorService] Found ${cleanTicker} via DexScreener on Base: ${bestPair.baseToken.address}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\taddress: bestPair.baseToken.address,\n\t\t\t\t\t\t\tchain: SupportedChain.BASE,\n\t\t\t\t\t\t\tticker: cleanTicker,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`[CommunityInvestorService] DexScreener search failed for ${cleanTicker} on Base:`,\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tlogger.warn(\n\t\t\t`[CommunityInvestorService] Could not resolve ticker ${ticker} on chain ${chain}`,\n\t\t);\n\t\treturn null;\n\t}\n\n\t/**\n\t * Get current price for a token\n\t */\n\tasync getCurrentPrice(chain: string, tokenAddress: string): Promise<number> {\n\t\tlogger.debug(\"getting current price\", chain, tokenAddress);\n\t\ttry {\n\t\t\t// Check cache first\n\t\t\tconst cacheKey = `token:${chain}:${tokenAddress}:price`;\n\t\t\tconst cachedPrice = await this.runtime.getCache<string>(cacheKey);\n\n\t\t\tif (cachedPrice) {\n\t\t\t\treturn Number.parseFloat(cachedPrice);\n\t\t\t}\n\n\t\t\t// Try to get from token performance\n\t\t\tconst token = await this.getTokenPerformance(tokenAddress, chain);\n\t\t\tif (token?.price) {\n\t\t\t\t// Cache the price\n\t\t\t\tawait this.runtime.setCache<string>(cacheKey, token.price.toString()); // Cache for 1 minute\n\t\t\t\treturn token.price;\n\t\t\t}\n\n\t\t\t// Fetch fresh price\n\t\t\tif (chain.toLowerCase() === \"solana\") {\n\t\t\t\tconst price = await this.birdeyeClient.fetchPrice(tokenAddress, {\n\t\t\t\t\tchain: \"solana\",\n\t\t\t\t});\n\n\t\t\t\t// Cache the price\n\t\t\t\tawait this.runtime.setCache<string>(cacheKey, price.toString()); // Cache for 1 minute\n\n\t\t\t\treturn price;\n\t\t\t}\n\t\t\tthrow new Error(`Chain ${chain} not supported for price fetching`);\n\t\t} catch (error) {\n\t\t\tlogger.error(`Error fetching current price for ${tokenAddress}:`, error);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t/**\n\t * Determine if a token should be traded\n\t */\n\tasync shouldTradeToken(\n\t\tchain: string,\n\t\ttokenAddress: string,\n\t): Promise<boolean> {\n\t\tlogger.debug(\"shouldTradeToken\", chain, tokenAddress);\n\t\ttry {\n\t\t\tconst tokenData = await this.getProcessedTokenData(chain, tokenAddress);\n\n\t\t\tif (!tokenData) return false;\n\n\t\t\t// Get the key metrics\n\t\t\tconst { tradeData, security, dexScreenerData } = tokenData;\n\n\t\t\tif (!dexScreenerData?.pairs || dexScreenerData.pairs.length === 0) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst pair = dexScreenerData.pairs[0];\n\n\t\t\t// Check liquidity\n\t\t\tif (\n\t\t\t\t!pair.liquidity ||\n\t\t\t\tpair.liquidity.usd < this.tradingConfig.minLiquidityUsd\n\t\t\t) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Check market cap\n\t\t\tif (\n\t\t\t\t!pair.marketCap ||\n\t\t\t\tpair.marketCap > this.tradingConfig.maxMarketCapUsd\n\t\t\t) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Check for suspicious holder distribution\n\t\t\tif (security && security.top10HolderPercent > 80) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Check for suspicious volume\n\t\t\tif (tradeData && tradeData.volume_24h_usd < 1000) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tlogger.error(\n\t\t\t\t`Error checking if token ${tokenAddress} should be traded:`,\n\t\t\t\terror,\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Get processed token data with security and trade information\n\t */\n\tasync getProcessedTokenData(\n\t\tchain: string,\n\t\ttokenAddress: string,\n\t): Promise<ProcessedTokenData | null> {\n\t\tlogger.debug(\"getting processed token data\", chain, tokenAddress);\n\t\ttry {\n\t\t\t// Check cache first\n\t\t\tconst cacheKey = `token:${chain}:${tokenAddress}:processed`;\n\t\t\tconst cachedData =\n\t\t\t\tawait this.runtime.getCache<ProcessedTokenData>(cacheKey);\n\n\t\t\tif (cachedData) {\n\t\t\t\treturn cachedData;\n\t\t\t}\n\n\t\t\t// Use token provider functionality to get complete token data\n\t\t\tif (chain.toLowerCase() === \"solana\") {\n\t\t\t\t// Get DexScreener data\n\t\t\t\tconst dexScreenerData = await this.dexscreenerClient.search(\n\t\t\t\t\ttokenAddress,\n\t\t\t\t\t{\n\t\t\t\t\t\texpires: \"5m\",\n\t\t\t\t\t},\n\t\t\t\t);\n\n\t\t\t\t// Try to get token data from Birdeye\n\t\t\t\tlet tokenTradeData: TokenTradeData;\n\t\t\t\tlet tokenSecurityData: TokenSecurityData;\n\n\t\t\t\ttry {\n\t\t\t\t\ttokenTradeData = await this.birdeyeClient.fetchTokenTradeData(\n\t\t\t\t\t\ttokenAddress,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tchain: \"solana\",\n\t\t\t\t\t\t\texpires: \"5m\",\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\n\t\t\t\t\ttokenSecurityData = await this.birdeyeClient.fetchTokenSecurity(\n\t\t\t\t\t\ttokenAddress,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tchain: \"solana\",\n\t\t\t\t\t\t\texpires: \"5m\",\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tlogger.error(`Error fetching token data for ${tokenAddress}:`, error);\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\n\t\t\t\t// Analyze holder distribution\n\t\t\t\tconst holderDistributionTrend =\n\t\t\t\t\tawait this.analyzeHolderDistribution(tokenTradeData);\n\n\t\t\t\t// Try to get holder data if Helius client is available\n\t\t\t\tlet highValueHolders: HighValueHolder[] = [];\n\t\t\t\tlet highSupplyHoldersCount = 0;\n\n\t\t\t\tif (this.heliusClient) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst holders = await this.heliusClient.fetchHolderList(\n\t\t\t\t\t\t\ttokenAddress,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\texpires: \"30m\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// Calculate high value holders\n\t\t\t\t\t\tconst tokenPrice = Number.parseFloat(\n\t\t\t\t\t\t\ttokenTradeData.price.toString(),\n\t\t\t\t\t\t);\n\t\t\t\t\t\thighValueHolders = holders\n\t\t\t\t\t\t\t.filter((holder) => {\n\t\t\t\t\t\t\t\tconst balance = Number.parseFloat(holder.balance);\n\t\t\t\t\t\t\t\tconst balanceUsd = balance * tokenPrice;\n\t\t\t\t\t\t\t\treturn balanceUsd > 5; // More than $5 USD\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.map((holder) => ({\n\t\t\t\t\t\t\t\tholderAddress: holder.address,\n\t\t\t\t\t\t\t\tbalanceUsd: (\n\t\t\t\t\t\t\t\t\tNumber.parseFloat(holder.balance) * tokenPrice\n\t\t\t\t\t\t\t\t).toFixed(2),\n\t\t\t\t\t\t\t}));\n\n\t\t\t\t\t\t// Calculate high supply holders\n\t\t\t\t\t\tconst totalSupply = \"0\";\n\t\t\t\t\t\thighSupplyHoldersCount = holders.filter((holder) => {\n\t\t\t\t\t\t\tconst holderRatio =\n\t\t\t\t\t\t\t\tNumber.parseFloat(holder.balance) /\n\t\t\t\t\t\t\t\tNumber.parseFloat(totalSupply);\n\t\t\t\t\t\t\treturn holderRatio > 0.02; // More than 2% of supply\n\t\t\t\t\t\t}).length;\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t\t`Error fetching holder data for ${tokenAddress}:`,\n\t\t\t\t\t\t\terror,\n\t\t\t\t\t\t);\n\t\t\t\t\t\t// Continue without holder data\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Check if there were any trades in last 24h\n\t\t\t\tconst recentTrades = tokenTradeData.volume_24h > 0;\n\n\t\t\t\t// Check if token is listed on DexScreener\n\t\t\t\tconst isDexScreenerListed = dexScreenerData.pairs.length > 0;\n\t\t\t\tconst isDexScreenerPaid = dexScreenerData.pairs.some(\n\t\t\t\t\t(pair) => pair.boosts && pair.boosts.active > 0,\n\t\t\t\t);\n\n\t\t\t\tconst processedData: ProcessedTokenData = {\n\t\t\t\t\ttoken: {\n\t\t\t\t\t\taddress: tokenAddress,\n\t\t\t\t\t\tname: dexScreenerData.pairs[0]?.baseToken?.name || \"\",\n\t\t\t\t\t\tsymbol: dexScreenerData.pairs[0]?.baseToken?.symbol || \"\",\n\t\t\t\t\t\tdecimals: 9, // Default for Solana\n\t\t\t\t\t\tlogoURI: \"\",\n\t\t\t\t\t},\n\t\t\t\t\tsecurity: tokenSecurityData,\n\t\t\t\t\ttradeData: tokenTradeData,\n\t\t\t\t\tholderDistributionTrend,\n\t\t\t\t\thighValueHolders,\n\t\t\t\t\trecentTrades,\n\t\t\t\t\thighSupplyHoldersCount,\n\t\t\t\t\tdexScreenerData,\n\t\t\t\t\tisDexScreenerListed,\n\t\t\t\t\tisDexScreenerPaid,\n\t\t\t\t};\n\n\t\t\t\t// Cache the processed data\n\t\t\t\tawait this.runtime.setCache<ProcessedTokenData>(\n\t\t\t\t\tcacheKey,\n\t\t\t\t\tprocessedData,\n\t\t\t\t); // Cache for 5 minutes\n\n\t\t\t\treturn processedData;\n\t\t\t}\n\t\t\tthrow new Error(`Chain ${chain} not supported for processed token data`);\n\t\t} catch (error) {\n\t\t\tlogger.error(\n\t\t\t\t`Error fetching processed token data for ${tokenAddress}:`,\n\t\t\t\terror,\n\t\t\t);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Analyze holder distribution trend\n\t */\n\tprivate async analyzeHolderDistribution(\n\t\ttradeData: TokenTradeData,\n\t): Promise<string> {\n\t\tlogger.debug(\"analyzing holder distribution\", tradeData);\n\t\t// Define the time intervals to consider\n\t\tconst intervals = [\n\t\t\t{\n\t\t\t\tperiod: \"30m\",\n\t\t\t\tchange: tradeData.unique_wallet_30m_change_percent,\n\t\t\t},\n\t\t\t{ period: \"1h\", change: tradeData.unique_wallet_1h_change_percent },\n\t\t\t{ period: \"2h\", change: tradeData.unique_wallet_2h_change_percent },\n\t\t\t{ period: \"4h\", change: tradeData.unique_wallet_4h_change_percent },\n\t\t\t{ period: \"8h\", change: tradeData.unique_wallet_8h_change_percent },\n\t\t\t{\n\t\t\t\tperiod: \"24h\",\n\t\t\t\tchange: tradeData.unique_wallet_24h_change_percent,\n\t\t\t},\n\t\t];\n\n\t\t// Calculate the average change percentage\n\t\tconst validChanges = intervals\n\t\t\t.map((interval) => interval.change)\n\t\t\t.filter((change) => change !== null && change !== undefined) as number[];\n\n\t\tif (validChanges.length === 0) {\n\t\t\treturn \"stable\";\n\t\t}\n\n\t\tconst averageChange =\n\t\t\tvalidChanges.reduce((acc, curr) => acc + curr, 0) / validChanges.length;\n\n\t\tconst increaseThreshold = 10; // e.g., average change > 10%\n\t\tconst decreaseThreshold = -10; // e.g., average change < -10%\n\n\t\tif (averageChange > increaseThreshold) {\n\t\t\treturn \"increasing\";\n\t\t}\n\t\tif (averageChange < decreaseThreshold) {\n\t\t\treturn \"decreasing\";\n\t\t}\n\t\treturn \"stable\";\n\t}\n\n\t// ===================== SCORE MANAGER METHODS =====================\n\n\t/**\n\t * Update token performance data\n\t */\n\tasync updateTokenPerformance(\n\t\tchain: string,\n\t\ttokenAddress: string,\n\t): Promise<TokenPerformance> {\n\t\tlogger.debug(\"updating token performance\", chain, tokenAddress);\n\t\ttry {\n\t\t\tconst tokenData = await this.getTokenOverview(chain, tokenAddress, true);\n\n\t\t\tconst performance: TokenPerformance = {\n\t\t\t\tchain,\n\t\t\t\taddress: tokenAddress,\n\t\t\t\tname: tokenData.name,\n\t\t\t\tsymbol: tokenData.symbol,\n\t\t\t\tdecimals: tokenData.decimals,\n\t\t\t\tprice: Number.parseFloat(tokenData.priceUsd),\n\t\t\t\tvolume: tokenData.volume24h,\n\t\t\t\tliquidity: tokenData.liquidityUsd,\n\t\t\t\tcurrentMarketCap: tokenData.marketCap,\n\t\t\t\tholders: tokenData.holders,\n\t\t\t\tprice24hChange: tokenData.price24hChange,\n\t\t\t\tvolume24hChange: tokenData.volume24hChange,\n\t\t\t\tmetadata: tokenData.metadata,\n\t\t\t\tcreatedAt: new Date(),\n\t\t\t\tupdatedAt: new Date(),\n\t\t\t};\n\n\t\t\t// Store in memory\n\t\t\tawait this.storeTokenPerformance(performance);\n\n\t\t\t// Emit event\n\t\t\t/* this.emitEvent({\n type: 'token_performance_updated',\n performance,\n }); */\n\n\t\t\treturn performance;\n\t\t} catch (error) {\n\t\t\tlogger.error(\n\t\t\t\t`Error updating token performance for ${tokenAddress}:`,\n\t\t\t\terror,\n\t\t\t);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Calculate risk score for a token\n\t */\n\tcalculateRiskScore(token: TokenPerformance): number {\n\t\tlogger.debug(\"calculating risk score\", token);\n\t\tlet score = 50; // Base score\n\n\t\t// Adjust based on liquidity\n\t\tconst liquidity = token.liquidity || 0;\n\t\tscore -= getLiquidityMultiplier(liquidity);\n\n\t\t// Adjust based on market cap\n\t\tconst marketCap = token.currentMarketCap || 0;\n\t\tscore += getMarketCapMultiplier(marketCap);\n\n\t\t// Adjust based on volume\n\t\tconst volume = token.volume || 0;\n\t\tscore -= getVolumeMultiplier(volume);\n\n\t\t// Risk adjustments for known issues\n\t\tif (token.rugPull) score += 30;\n\t\tif (token.isScam) score += 30;\n\t\tif (token.rapidDump) score += 15;\n\t\tif (token.suspiciousVolume) score += 15;\n\n\t\t// Clamp between 0-100\n\t\treturn Math.max(0, Math.min(100, score));\n\t}\n\n\t/**\n\t * Update entity metrics based on their recommendation performance\n\t */\n\tasync updateRecommenderMetrics(\n\t\tentityId: UUID,\n\t\tperformance = 0,\n\t): Promise<void> {\n\t\tlogger.debug(\"updating recommender metrics\", entityId, performance);\n\t\tconst metrics = await this.getRecommenderMetrics(entityId);\n\n\t\tif (!metrics) {\n\t\t\t// Initialize metrics if they don't exist\n\t\t\tawait this.initializeRecommenderMetrics(entityId, \"default\");\n\t\t\treturn;\n\t\t}\n\n\t\t// Update metrics\n\t\tconst updatedMetrics: RecommenderMetrics = {\n\t\t\t...metrics,\n\t\t\ttotalRecommendations: metrics.totalRecommendations + 1,\n\t\t\tsuccessfulRecs:\n\t\t\t\tperformance > 0 ? metrics.successfulRecs + 1 : metrics.successfulRecs,\n\t\t\tavgTokenPerformance:\n\t\t\t\t(metrics.avgTokenPerformance * metrics.totalRecommendations +\n\t\t\t\t\tperformance) /\n\t\t\t\t(metrics.totalRecommendations + 1),\n\t\t\ttrustScore: this.calculateTrustScore(metrics, performance),\n\t\t};\n\n\t\t// Store updated metrics\n\t\tawait this.storeRecommenderMetrics(updatedMetrics);\n\n\t\t// Also store in history\n\t\tconst historyEntry: RecommenderMetricsHistory = {\n\t\t\tentityId,\n\t\t\tmetrics: updatedMetrics,\n\t\t\ttimestamp: new Date(),\n\t\t};\n\n\t\tawait this.storeRecommenderMetricsHistory(historyEntry);\n\t}\n\n\t/**\n\t * Calculate trust score based on metrics and new performance\n\t */\n\tprivate calculateTrustScore(\n\t\tmetrics: RecommenderMetrics,\n\t\tnewPerformance: number,\n\t): number {\n\t\tlogger.debug(\"calculating trust score\", metrics, newPerformance);\n\t\t// Weight factors\n\t\tconst HISTORY_WEIGHT = 0.7;\n\t\tconst NEW_PERFORMANCE_WEIGHT = 0.3;\n\n\t\t// Calculate success rate\n\t\tconst newSuccessRate =\n\t\t\t(metrics.successfulRecs + (newPerformance > 0 ? 1 : 0)) /\n\t\t\t(metrics.totalRecommendations + 1);\n\n\t\t// Calculate consistency (based on standard deviation of performance)\n\t\t// This is a simplified approach\n\t\tconst consistencyScore = metrics.consistencyScore || 50;\n\n\t\t// Calculate new trust score\n\t\tconst newTrustScore =\n\t\t\tmetrics.trustScore * HISTORY_WEIGHT +\n\t\t\t(newPerformance > 0 ? 100 : 0) * NEW_PERFORMANCE_WEIGHT;\n\n\t\t// Adjust based on success rate\n\t\tconst successFactor = newSuccessRate * 100;\n\n\t\t// Combine scores with weights\n\t\tconst combinedScore =\n\t\t\tnewTrustScore * 0.6 + successFactor * 0.3 + consistencyScore * 0.1;\n\n\t\t// Clamp between 0-100\n\t\treturn Math.max(0, Math.min(100, combinedScore));\n\t}\n\n\t// ===================== POSITION METHODS =====================\n\n\t/**\n\t * Get or fetch token performance data\n\t */\n\tprivate async getOrFetchTokenPerformance(\n\t\ttokenAddress: string,\n\t\tchain: string,\n\t): Promise<TokenPerformance | null> {\n\t\tlogger.debug(\"getting or fetching token performance\", tokenAddress, chain);\n\t\ttry {\n\t\t\t// Try to get from memory first\n\t\t\tlet tokenPerformance = await this.getTokenPerformance(\n\t\t\t\ttokenAddress,\n\t\t\t\tchain,\n\t\t\t);\n\n\t\t\t// If not found, fetch from API\n\t\t\tif (!tokenPerformance) {\n\t\t\t\tconst tokenOverview = await this.getTokenOverview(chain, tokenAddress);\n\n\t\t\t\t// Convert token overview to token performance\n\t\t\t\ttokenPerformance = {\n\t\t\t\t\tchain,\n\t\t\t\t\taddress: tokenAddress,\n\t\t\t\t\tname: tokenOverview.name,\n\t\t\t\t\tsymbol: tokenOverview.symbol,\n\t\t\t\t\tdecimals: tokenOverview.decimals,\n\t\t\t\t\tprice: Number.parseFloat(tokenOverview.priceUsd),\n\t\t\t\t\tvolume: tokenOverview.volume24h,\n\t\t\t\t\tprice24hChange: tokenOverview.price24hChange,\n\t\t\t\t\tliquidity: tokenOverview.liquidityUsd,\n\t\t\t\t\tholders: tokenOverview.holders,\n\t\t\t\t\tcreatedAt: new Date(),\n\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t};\n\n\t\t\t\t// Store in memory if found\n\t\t\t\tif (tokenPerformance) {\n\t\t\t\t\tawait this.storeTokenPerformance(tokenPerformance);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn tokenPerformance;\n\t\t} catch (error) {\n\t\t\tlogger.error(\n\t\t\t\t`Error fetching token performance for ${tokenAddress}:`,\n\t\t\t\terror,\n\t\t\t);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Validate if a token meets trading criteria\n\t */\n\tprivate validateToken(token: TokenPerformance): boolean {\n\t\t// Skip validation for simulation tokens\n\t\tif (token.address?.startsWith(\"sim_\")) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Check for scam or rug pull flags\n\t\tif (token.isScam || token.rugPull) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Check liquidity\n\t\tconst liquidity = token.liquidity || 0;\n\t\tif (liquidity < this.tradingConfig.minLiquidityUsd) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Check market cap\n\t\tconst marketCap = token.currentMarketCap || 0;\n\t\tif (marketCap > this.tradingConfig.maxMarketCapUsd) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Create a token recommendation\n\t */\n\tprivate async createTokenRecommendation(\n\t\tentityId: UUID,\n\t\ttoken: TokenPerformance,\n\t\tconviction: Conviction = Conviction.MEDIUM,\n\t\ttype: RecommendationType = RecommendationType.BUY,\n\t): Promise<TokenRecommendation | null> {\n\t\tlogger.debug(\n\t\t\t\"creating token recommendation\",\n\t\t\tentityId,\n\t\t\ttoken,\n\t\t\tconviction,\n\t\t\ttype,\n\t\t);\n\t\ttry {\n\t\t\tconst recommendation: TokenRecommendation = {\n\t\t\t\tid: uuidv4() as UUID,\n\t\t\t\tentityId,\n\t\t\t\tchain: token.chain || this.tradingConfig.defaultChain,\n\t\t\t\ttokenAddress: token.address || \"\",\n\t\t\t\ttype,\n\t\t\t\tconviction,\n\t\t\t\tinitialMarketCap: (token.initialMarketCap || 0).toString(),\n\t\t\t\tinitialLiquidity: (token.liquidity || 0).toString(),\n\t\t\t\tinitialPrice: (token.price || 0).toString(),\n\t\t\t\tmarketCap: (token.currentMarketCap || 0).toString(),\n\t\t\t\tliquidity: (token.liquidity || 0).toString(),\n\t\t\t\tprice: (token.price || 0).toString(),\n\t\t\t\trugPull: token.rugPull || false,\n\t\t\t\tisScam: token.isScam || false,\n\t\t\t\triskScore: this.calculateRiskScore(token),\n\t\t\t\tperformanceScore: 0,\n\t\t\t\tmetadata: {},\n\t\t\t\tstatus: \"ACTIVE\",\n\t\t\t\tcreatedAt: new Date(),\n\t\t\t\tupdatedAt: new Date(),\n\t\t\t};\n\n\t\t\t// Store in memory\n\t\t\tawait this.storeTokenRecommendation(recommendation);\n\n\t\t\t// Emit event\n\t\t\t/* this.emitEvent({\n type: 'recommendation_added',\n recommendation,\n }); */\n\n\t\t\treturn recommendation;\n\t\t} catch (error) {\n\t\t\tlogger.error(\"Error creating token recommendation:\", error);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Calculate buy amount based on entity trust score and conviction\n\t */\n\tprivate calculateBuyAmount(\n\t\tentity: Entity,\n\t\tconviction: Conviction,\n\t\ttoken: TokenPerformance,\n\t): bigint {\n\t\tlogger.debug(\"calculating buy amount\", entity, conviction, token);\n\t\t// Get entity trust score from metrics\n\t\tlet trustScore = 50; // Default value\n\n\t\t// Try to get actual metrics if entity has an ID\n\t\tif (entity.id) {\n\t\t\tconst metricsPromise = this.getRecommenderMetrics(entity.id);\n\t\t\tmetricsPromise\n\t\t\t\t.then((metrics) => {\n\t\t\t\t\tif (metrics) {\n\t\t\t\t\t\ttrustScore = metrics.trustScore;\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.catch((error) => {\n\t\t\t\t\tlogger.error(`Error getting entity metrics for ${entity.id}:`, error);\n\t\t\t\t});\n\t\t}\n\n\t\t// Get base amount from config\n\t\tconst { baseAmount, minAmount, maxAmount, trustScoreMultiplier } =\n\t\t\tthis.tradingConfig.buyAmountConfig;\n\n\t\t// Calculate multipliers\n\t\tconst trustMultiplier = 1 + (trustScore / 100) * trustScoreMultiplier;\n\t\tconst convMultiplier = getConvictionMultiplier(conviction);\n\n\t\t// Apply multipliers to base amount\n\t\tlet amount = baseAmount * trustMultiplier * convMultiplier;\n\n\t\t// Apply token-specific multipliers\n\t\tif (token.liquidity) {\n\t\t\tamount *= getLiquidityMultiplier(token.liquidity);\n\t\t}\n\n\t\t// Ensure amount is within bounds\n\t\tamount = Math.max(minAmount, Math.min(maxAmount, amount));\n\n\t\t// Convert to bigint (in smallest units)\n\t\treturn BigInt(Math.floor(amount * 1e9)); // Convert to lamports (SOL smallest unit)\n\t}\n\n\t/**\n\t * Create a new position\n\t */\n\tprivate async createPosition(\n\t\trecommendationId: UUID,\n\t\tentityId: UUID,\n\t\ttokenAddress: string,\n\t\twalletAddress: string,\n\t\tamount: bigint,\n\t\tprice: string,\n\t\tisSimulation: boolean,\n\t): Promise<Position | null> {\n\t\tlogger.debug(\n\t\t\t\"creating position\",\n\t\t\trecommendationId,\n\t\t\tentityId,\n\t\t\ttokenAddress,\n\t\t\twalletAddress,\n\t\t\tamount,\n\t\t\tprice,\n\t\t\tisSimulation,\n\t\t);\n\t\ttry {\n\t\t\tconst position: Position = {\n\t\t\t\tid: uuidv4() as UUID,\n\t\t\t\tchain: this.tradingConfig.defaultChain,\n\t\t\t\ttokenAddress,\n\t\t\t\twalletAddress,\n\t\t\t\tisSimulation,\n\t\t\t\tentityId,\n\t\t\t\trecommendationId,\n\t\t\t\tinitialPrice: price,\n\t\t\t\tbalance: \"0\",\n\t\t\t\tstatus: \"OPEN\",\n\t\t\t\tamount: amount.toString(),\n\t\t\t\tcreatedAt: new Date(),\n\t\t\t};\n\n\t\t\t// Store in memory\n\t\t\tawait this.storePosition(position);\n\n\t\t\treturn position;\n\t\t} catch (error) {\n\t\t\tlogger.error(\"Error creating position:\", error);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Record a transaction\n\t */\n\tprivate async recordTransaction(\n\t\tpositionId: UUID,\n\t\ttokenAddress: string,\n\t\ttype: TransactionType,\n\t\tamount: bigint,\n\t\tprice: number,\n\t\tisSimulation: boolean,\n\t): Promise<boolean> {\n\t\tlogger.debug(\n\t\t\t\"recording transaction\",\n\t\t\tpositionId,\n\t\t\ttokenAddress,\n\t\t\ttype,\n\t\t\tamount,\n\t\t\tprice,\n\t\t\tisSimulation,\n\t\t);\n\t\ttry {\n\t\t\tconst transaction: Transaction = {\n\t\t\t\tid: uuidv4() as UUID,\n\t\t\t\tpositionId,\n\t\t\t\tchain: this.tradingConfig.defaultChain,\n\t\t\t\ttokenAddress,\n\t\t\t\ttype,\n\t\t\t\tamount: amount.toString(),\n\t\t\t\tprice: price.toString(),\n\t\t\t\tisSimulation,\n\t\t\t\ttimestamp: new Date(),\n\t\t\t};\n\n\t\t\t// Store in memory\n\t\t\tawait this.storeTransaction(transaction);\n\n\t\t\t// Emit event\n\t\t\t// this.emitEvent({ type: 'transaction_added', transaction });\n\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tlogger.error(\"Error recording transaction:\", error);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Get all positions for an entity\n\t */\n\tasync getPositionsByRecommender(entityId: UUID): Promise<Position[]> {\n\t\tlogger.debug(\"getting positions by recommender\", entityId);\n\t\ttry {\n\t\t\tconst recommendations =\n\t\t\t\tawait this.getRecommendationsByRecommender(entityId);\n\t\t\tconst positions: Position[] = [];\n\n\t\t\tfor (const recommendation of recommendations) {\n\t\t\t\tconst positionMatches = await this.getPositionsByToken(\n\t\t\t\t\trecommendation.tokenAddress,\n\t\t\t\t);\n\n\t\t\t\t// Filter for positions associated with this entity\n\t\t\t\tconst entityPositions = positionMatches.filter(\n\t\t\t\t\t(position) => position.entityId === entityId,\n\t\t\t\t);\n\n\t\t\t\tpositions.push(...entityPositions);\n\t\t\t}\n\n\t\t\treturn positions;\n\t\t} catch (error) {\n\t\t\tlogger.error(\"Error getting positions by entity:\", error);\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Get all positions for a token\n\t */\n\tprivate async getPositionsByToken(tokenAddress: string): Promise<Position[]> {\n\t\tlogger.debug(\"getting positions by token\", tokenAddress);\n\t\ttry {\n\t\t\t// This is a simplified implementation\n\t\t\t// In a real-world scenario, you'd query the database\n\t\t\tconst positions = await this.getOpenPositionsWithBalance();\n\t\t\treturn positions.filter(\n\t\t\t\t(position) => position.tokenAddress === tokenAddress,\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tlogger.error(\"Error getting positions by token:\", error);\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Get all transactions for a position\n\t */\n\tasync getTransactionsByPosition(positionId: UUID): Promise<Transaction[]> {\n\t\tlogger.debug(\"getting transactions by position\", positionId);\n\t\ttry {\n\t\t\t// Search for transactions with this position ID\n\t\t\tconst query = `transactions for position ${positionId}`;\n\t\t\tconst embedding = await this.runtime.useModel(\n\t\t\t\tModelType.TEXT_EMBEDDING,\n\t\t\t\tquery,\n\t\t\t);\n\n\t\t\tconst memories = await this.runtime.searchMemories({\n\t\t\t\ttableName: \"transactions\",\n\t\t\t\tembedding,\n\t\t\t\tmatch_threshold: 0.7,\n\t\t\t\tlimit: 20,\n\t\t\t});\n\n\t\t\tconst transactions: Transaction[] = [];\n\n\t\t\tfor (const memory of memories) {\n\t\t\t\tconst transaction = asTransaction(memory.content.transaction);\n\t\t\t\tif (transaction && transaction.positionId === positionId) {\n\t\t\t\t\ttransactions.push(transaction);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn transactions;\n\t\t} catch (error) {\n\t\t\tlogger.error(\"Error getting transactions by position:\", error);\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Get all transactions for a token\n\t */\n\tasync getTransactionsByToken(tokenAddress: string): Promise<Transaction[]> {\n\t\tlogger.debug(\"getting transactions by token\", tokenAddress);\n\t\ttry {\n\t\t\t// Search for transactions with this token address\n\t\t\tconst query = `transactions for token ${tokenAddress}`;\n\t\t\tconst embedding = await this.runtime.useModel(\n\t\t\t\tModelType.TEXT_EMBEDDING,\n\t\t\t\tquery,\n\t\t\t);\n\n\t\t\tconst memories = await this.runtime.searchMemories({\n\t\t\t\ttableName: \"transactions\",\n\t\t\t\tembedding,\n\t\t\t\tmatch_threshold: 0.7,\n\t\t\t\tlimit: 50,\n\t\t\t});\n\n\t\t\tconst transactions: Transaction[] = [];\n\n\t\t\tfor (const memory of memories) {\n\t\t\t\tconst transaction = asTransaction(memory.content.transaction);\n\t\t\t\tif (transaction && transaction.tokenAddress === tokenAddress) {\n\t\t\t\t\ttransactions.push(transaction);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn transactions;\n\t\t} catch (error) {\n\t\t\tlogger.error(\"Error getting transactions by token:\", error);\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Get a position by ID\n\t */\n\tasync getPosition(positionId: UUID): Promise<Position | null> {\n\t\tlogger.debug(\"getting position\", positionId);\n\t\ttry {\n\t\t\t// Check cache first\n\t\t\tconst cacheKey = `position:${positionId}`;\n\t\t\tconst cachedPosition = await this.runtime.getCache<Position>(cacheKey);\n\n\t\t\tif (cachedPosition) {\n\t\t\t\treturn cachedPosition;\n\t\t\t}\n\n\t\t\t// Search for position in memory\n\t\t\tconst query = `position with ID ${positionId}`;\n\t\t\tconst embedding = await this.runtime.useModel(\n\t\t\t\tModelType.TEXT_EMBEDDING,\n\t\t\t\tquery,\n\t\t\t);\n\n\t\t\tconst memories = await this.runtime.searchMemories({\n\t\t\t\ttableName: \"positions\",\n\t\t\t\tembedding,\n\t\t\t\tmatch_threshold: 0.7,\n\t\t\t\tlimit: 1,\n\t\t\t});\n\n\t\t\tconst position = asContentObject<Position>(\n\t\t\t\tmemories[0]?.content.position,\n\t\t\t\t[\"id\", \"entityId\", \"tokenAddress\"],\n\t\t\t);\n\t\t\tif (position) {\n\t\t\t\t// Cache the position\n\t\t\t\tawait this.runtime.setCache<Position>(cacheKey, position); // Cache for 5 minutes\n\n\t\t\t\treturn position;\n\t\t\t}\n\n\t\t\treturn null;\n\t\t} catch (error) {\n\t\t\tlogger.error(\"Error getting position:\", error);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Get all recommendations by a entity\n\t */\n\tasync getRecommendationsByRecommender(\n\t\tentityId: UUID,\n\t): Promise<TokenRecommendation[]> {\n\t\tlogger.debug(\"getting recommendations by recommender\", entityId);\n\t\ttry {\n\t\t\t// Search for recommendations by this entity\n\t\t\tconst query = `recommendations by entity ${entityId}`;\n\t\t\tconst embedding = await this.runtime.useModel(\n\t\t\t\tModelType.TEXT_EMBEDDING,\n\t\t\t\tquery,\n\t\t\t);\n\n\t\t\tconst memories = await this.runtime.searchMemories({\n\t\t\t\ttableName: \"recommendations\",\n\t\t\t\tembedding,\n\t\t\t\tmatch_threshold: 0.7,\n\t\t\t\tlimit: 50,\n\t\t\t});\n\n\t\t\tconst recommendations: TokenRecommendation[] = [];\n\n\t\t\tfor (const memory of memories) {\n\t\t\t\tconst meta = memory.metadata as Record<string, unknown> | undefined;\n\t\t\t\tif (\n\t\t\t\t\tmeta?.recommendation &&\n\t\t\t\t\t(meta.recommendation as TokenRecommendation).entityId === entityId\n\t\t\t\t) {\n\t\t\t\t\trecommendations.push(meta.recommendation as TokenRecommendation);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn recommendations;\n\t\t} catch (error) {\n\t\t\tlogger.error(\"Error getting recommendations by entity:\", error);\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Close a position and update metrics\n\t */\n\tasync closePosition(positionId: UUID): Promise<boolean> {\n\t\tlogger.debug(\"closing position\", positionId);\n\t\ttry {\n\t\t\tconst position = await this.getPosition(positionId);\n\t\t\tif (!position) {\n\t\t\t\tlogger.error(`Position ${positionId} not found`);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Update position status\n\t\t\tposition.status = \"CLOSED\";\n\t\t\tposition.closedAt = new Date();\n\n\t\t\t// Calculate final metrics\n\t\t\tconst transactions = await this.getTransactionsByPosition(positionId);\n\t\t\tconst performance = await this.calculatePositionPerformance(\n\t\t\t\tposition,\n\t\t\t\ttransactions,\n\t\t\t);\n\n\t\t\t// Update entity metrics\n\t\t\tawait this.updateRecommenderMetrics(position.entityId, performance);\n\n\t\t\t// Store in memory\n\t\t\tawait this.storePosition(position);\n\n\t\t\t// Emit event\n\t\t\t// this.emitEvent({ type: 'position_closed', position });\n\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tlogger.error(`Failed to close position ${positionId}:`, error);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Calculate position performance\n\t */\n\tprivate async calculatePositionPerformance(\n\t\tposition: Position,\n\t\ttransactions: Transaction[],\n\t): Promise<number> {\n\t\tlogger.debug(\"calculating position performance\", position, transactions);\n\t\tif (!transactions.length) return 0;\n\n\t\tconst buyTxs = transactions.filter((t) => t.type === TransactionType.BUY);\n\t\tconst sellTxs = transactions.filter((t) => t.type === TransactionType.SELL);\n\n\t\tconst totalBuyAmount = buyTxs.reduce(\n\t\t\t(sum, tx) => sum + BigInt(tx.amount),\n\t\t\t0n,\n\t\t);\n\t\tconst _totalSellAmount = sellTxs.reduce(\n\t\t\t(sum, tx) => sum + BigInt(tx.amount),\n\t\t\t0n,\n\t\t);\n\n\t\tposition.amount = totalBuyAmount.toString();\n\n\t\tconst avgBuyPrice =\n\t\t\tbuyTxs.reduce((sum, tx) => sum + Number(tx.price), 0) / buyTxs.length;\n\t\tconst avgSellPrice = sellTxs.length\n\t\t\t? sellTxs.reduce((sum, tx) => sum + Number(tx.price), 0) / sellTxs.length\n\t\t\t: await this.getCurrentPrice(position.chain, position.tokenAddress);\n\n\t\tposition.currentPrice = avgSellPrice.toString();\n\n\t\treturn ((avgSellPrice - avgBuyPrice) / avgBuyPrice) * 100;\n\t}\n\n\t/**\n\t * Store token performance data\n\t */\n\tprivate async storeTokenPerformance(token: TokenPerformance): Promise<void> {\n\t\tlogger.debug(\"storing token performance\", token);\n\t\ttry {\n\t\t\tconst text = `Token performance data for ${token.symbol || token.address} on ${token.chain}`;\n\t\t\t// Create memory object\n\t\t\tconst memory: Memory = {\n\t\t\t\tid: uuidv4() as UUID,\n\t\t\t\tentityId: this.runtime.agentId,\n\t\t\t\troomId: \"global\" as UUID,\n\t\t\t\tcontent: {\n\t\t\t\t\ttext,\n\t\t\t\t\ttoken: toJsonRecord(token),\n\t\t\t\t},\n\t\t\t\tcreatedAt: Date.now(),\n\t\t\t};\n\n\t\t\t// Add embedding to memory\n\t\t\tconst embedding = await this.runtime.useModel(\"TEXT_EMBEDDING\", text);\n\t\t\tconst memoryWithEmbedding: Memory = { ...memory, embedding };\n\n\t\t\t// Store in memory manager\n\t\t\tawait this.runtime.createMemory(memoryWithEmbedding, \"tokens\", true);\n\n\t\t\t// Also cache for quick access\n\t\t\tconst cacheKey = `token:${token.chain}:${token.address}:performance`;\n\t\t\tawait this.runtime.setCache<TokenPerformance>(cacheKey, token); // Cache for 5 minutes\n\t\t} catch (error) {\n\t\t\tlogger.error(\n\t\t\t\t`Error storing token performance for ${token.address}:`,\n\t\t\t\terror,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Store position data\n\t */\n\tprivate async storePosition(position: Position): Promise<void> {\n\t\tlogger.debug(\"storing position\", position);\n\t\ttry {\n\t\t\tconst text = `Position data for token ${position.tokenAddress} by entity ${position.entityId}`;\n\t\t\t// Create memory object\n\t\t\tconst memory: Memory = {\n\t\t\t\tid: uuidv4() as UUID,\n\t\t\t\tentityId: this.runtime.agentId,\n\t\t\t\troomId: \"global\" as UUID,\n\t\t\t\tcontent: {\n\t\t\t\t\ttext,\n\t\t\t\t\tposition: toJsonRecord(position),\n\t\t\t\t},\n\t\t\t\tcreatedAt: Date.now(),\n\t\t\t};\n\n\t\t\t// Add embedding to memory\n\t\t\tconst embedding = await this.runtime.useModel(\"TEXT_EMBEDDING\", text);\n\t\t\tconst memoryWithEmbedding: Memory = { ...memory, embedding };\n\n\t\t\t// Store in memory manager\n\t\t\tawait this.runtime.createMemory(memoryWithEmbedding, \"positions\", true);\n\n\t\t\t// Also cache for quick access\n\t\t\tconst cacheKey = `position:${position.id}`;\n\t\t\tawait this.runtime.setCache<Position>(cacheKey, position);\n\t\t} catch (error) {\n\t\t\tlogger.error(\n\t\t\t\t`Error storing position for ${position.tokenAddress}:`,\n\t\t\t\terror,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Store transaction data\n\t */\n\tprivate async storeTransaction(transaction: Transaction): Promise<void> {\n\t\tlogger.debug(\"storing transaction\", transaction);\n\t\ttry {\n\t\t\tconst text = `Transaction data for position ${transaction.positionId} token ${transaction.tokenAddress} ${transaction.type}`;\n\t\t\t// Create memory object\n\t\t\tconst memory: Memory = {\n\t\t\t\tid: uuidv4() as UUID,\n\t\t\t\tentityId: this.runtime.agentId,\n\t\t\t\troomId: \"global\" as UUID,\n\t\t\t\tcontent: {\n\t\t\t\t\ttext,\n\t\t\t\t\ttransaction: toJsonRecord(transaction),\n\t\t\t\t},\n\t\t\t\tcreatedAt: Date.now(),\n\t\t\t};\n\n\t\t\t// Add embedding to memory\n\t\t\tconst embedding = await this.runtime.useModel(\"TEXT_EMBEDDING\", text);\n\t\t\tconst memoryWithEmbedding: Memory = { ...memory, embedding };\n\n\t\t\t// Store in memory manager\n\t\t\tawait this.runtime.createMemory(\n\t\t\t\tmemoryWithEmbedding,\n\t\t\t\t\"transactions\",\n\t\t\t\ttrue,\n\t\t\t);\n\n\t\t\t// Also cache transaction list for position\n\t\t\tconst cacheKey = `position:${transaction.positionId}:transactions`;\n\t\t\tconst cachedTxs = await this.runtime.getCache<Transaction[]>(cacheKey);\n\n\t\t\tif (cachedTxs) {\n\t\t\t\tconst txs = cachedTxs as Transaction[];\n\t\t\t\ttxs.push(transaction);\n\t\t\t\tawait this.runtime.setCache<Transaction[]>(cacheKey, txs); // Cache for 5 minutes\n\t\t\t} else {\n\t\t\t\tawait this.runtime.setCache<Transaction[]>(cacheKey, [transaction]); // Cache for 5 minutes\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tlogger.error(\n\t\t\t\t`Error storing transaction for position ${transaction.positionId}:`,\n\t\t\t\terror,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Store token recommendation data\n\t */\n\tprivate async storeTokenRecommendation(\n\t\trecommendation: TokenRecommendation,\n\t): Promise<void> {\n\t\tlogger.debug(\"storing token recommendation\", recommendation);\n\t\ttry {\n\t\t\tconst text = `Token recommendation for ${recommendation.tokenAddress} by entity ${recommendation.entityId}`;\n\t\t\t// Create memory object\n\t\t\tconst memory: Memory = {\n\t\t\t\tid: uuidv4() as UUID,\n\t\t\t\tentityId: this.runtime.agentId,\n\t\t\t\troomId: \"global\" as UUID,\n\t\t\t\tcontent: {\n\t\t\t\t\ttext,\n\t\t\t\t\trecommendation: toJsonRecord(recommendation),\n\t\t\t\t},\n\t\t\t\tcreatedAt: Date.now(),\n\t\t\t};\n\n\t\t\t// Add embedding to memory\n\t\t\tconst embedding = await this.runtime.useModel(\"TEXT_EMBEDDING\", text);\n\t\t\tconst memoryWithEmbedding: Memory = { ...memory, embedding };\n\n\t\t\t// Store in memory manager\n\t\t\tawait this.runtime.createMemory(\n\t\t\t\tmemoryWithEmbedding,\n\t\t\t\t\"recommendations\",\n\t\t\t\ttrue,\n\t\t\t);\n\n\t\t\t// Also cache for quick access\n\t\t\tconst cacheKey = `recommendation:${recommendation.id}`;\n\t\t\tawait this.runtime.setCache<TokenRecommendation>(\n\t\t\t\tcacheKey,\n\t\t\t\trecommendation,\n\t\t\t); // Cache for 5 minutes\n\t\t} catch (error) {\n\t\t\tlogger.error(\n\t\t\t\t`Error storing recommendation for ${recommendation.tokenAddress}:`,\n\t\t\t\terror,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Store entity metrics\n\t */\n\tprivate async storeRecommenderMetrics(\n\t\tmetrics: RecommenderMetrics,\n\t): Promise<void> {\n\t\tlogger.debug(\"storing recommender metrics\", metrics);\n\t\ttry {\n\t\t\tconst text = `Recommender metrics for ${metrics.entityId}`;\n\t\t\t// Create memory object\n\t\t\tconst memory: Memory = {\n\t\t\t\tid: uuidv4() as UUID,\n\t\t\t\tentityId: this.runtime.agentId,\n\t\t\t\troomId: \"global\" as UUID,\n\t\t\t\tcontent: {\n\t\t\t\t\ttext,\n\t\t\t\t\tmetrics: toJsonRecord(metrics),\n\t\t\t\t},\n\t\t\t\tcreatedAt: Date.now(),\n\t\t\t};\n\n\t\t\t// Add embedding to memory\n\t\t\tconst embedding = await this.runtime.useModel(\"TEXT_EMBEDDING\", text);\n\t\t\tconst memoryWithEmbedding: Memory = { ...memory, embedding };\n\n\t\t\t// Store in memory manager\n\t\t\tawait this.runtime.createMemory(\n\t\t\t\tmemoryWithEmbedding,\n\t\t\t\t\"recommender_metrics\",\n\t\t\t\ttrue,\n\t\t\t);\n\n\t\t\t// Also cache for quick access\n\t\t\tconst cacheKey = `entity:${metrics.entityId}:metrics`;\n\t\t\tawait this.runtime.setCache<RecommenderMetrics>(cacheKey, metrics); // Cache for 5 minutes\n\t\t} catch (error) {\n\t\t\tlogger.error(\n\t\t\t\t`Error storing entity metrics for ${metrics.entityId}:`,\n\t\t\t\terror,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Store entity metrics history\n\t */\n\tprivate async storeRecommenderMetricsHistory(\n\t\thistory: RecommenderMetricsHistory,\n\t): Promise<void> {\n\t\tlogger.debug(\"storing recommender metrics history\", history);\n\t\ttry {\n\t\t\tconst text = `Recommender metrics history for ${history.entityId}`;\n\t\t\t// Create memory object\n\t\t\tconst memory: Memory = {\n\t\t\t\tid: uuidv4() as UUID,\n\t\t\t\tentityId: this.runtime.agentId,\n\t\t\t\troomId: \"global\" as UUID,\n\t\t\t\tcontent: {\n\t\t\t\t\ttext,\n\t\t\t\t\thistory: toJsonRecord(history),\n\t\t\t\t},\n\t\t\t\tcreatedAt: Date.now(),\n\t\t\t};\n\n\t\t\t// Add embedding to memory\n\t\t\tconst embedding = await this.runtime.useModel(\"TEXT_EMBEDDING\", text);\n\t\t\tconst memoryWithEmbedding: Memory = { ...memory, embedding };\n\n\t\t\t// Store in memory manager\n\t\t\tawait this.runtime.createMemory(\n\t\t\t\tmemoryWithEmbedding,\n\t\t\t\t\"recommender_metrics_history\",\n\t\t\t\ttrue,\n\t\t\t);\n\n\t\t\t// Also update history list in cache\n\t\t\tconst cacheKey = `entity:${history.entityId}:history`;\n\t\t\tconst cachedHistory =\n\t\t\t\tawait this.runtime.getCache<RecommenderMetricsHistory[]>(cacheKey);\n\n\t\t\tif (cachedHistory) {\n\t\t\t\tconst histories = cachedHistory as RecommenderMetricsHistory[];\n\t\t\t\thistories.push(history);\n\t\t\t\t// Keep only the last 10 entries\n\t\t\t\tconst recentHistories = histories\n\t\t\t\t\t.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime())\n\t\t\t\t\t.slice(0, 10);\n\t\t\t\tawait this.runtime.setCache<RecommenderMetricsHistory[]>(\n\t\t\t\t\tcacheKey,\n\t\t\t\t\trecentHistories,\n\t\t\t\t); // Cache for 1 hour\n\t\t\t} else {\n\t\t\t\tawait this.runtime.setCache<RecommenderMetricsHistory[]>(cacheKey, [\n\t\t\t\t\thistory,\n\t\t\t\t]); // Cache for 1 hour\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tlogger.error(\n\t\t\t\t`Error storing entity metrics history for ${history.entityId}:`,\n\t\t\t\terror,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Get entity metrics\n\t */\n\tasync getRecommenderMetrics(\n\t\tentityId: UUID,\n\t): Promise<RecommenderMetrics | null> {\n\t\tlogger.debug(\"getting recommender metrics\", entityId);\n\t\ttry {\n\t\t\t// Check cache first\n\t\t\tconst cacheKey = `entity:${entityId}:metrics`;\n\t\t\tconst cachedMetrics =\n\t\t\t\tawait this.runtime.getCache<RecommenderMetrics>(cacheKey);\n\n\t\t\tif (cachedMetrics) {\n\t\t\t\treturn cachedMetrics as RecommenderMetrics;\n\t\t\t}\n\n\t\t\t// Search for metrics in memory\n\t\t\tconst query = `entity metrics for entity ${entityId}`;\n\t\t\tconst embedding = await this.runtime.useModel(\n\t\t\t\tModelType.TEXT_EMBEDDING,\n\t\t\t\tquery,\n\t\t\t);\n\n\t\t\tconst memories = await this.runtime.searchMemories({\n\t\t\t\ttableName: \"recommender_metrics\",\n\t\t\t\tembedding,\n\t\t\t\tmatch_threshold: 0.7,\n\t\t\t\tlimit: 1,\n\t\t\t});\n\n\t\t\tconst metrics = asContentObject<RecommenderMetrics>(\n\t\t\t\tmemories[0]?.content.metrics,\n\t\t\t\t[\"entityId\", \"platform\"],\n\t\t\t);\n\t\t\tif (metrics) {\n\t\t\t\t// Cache the metrics\n\t\t\t\tawait this.runtime.setCache<RecommenderMetrics>(cacheKey, metrics); // Cache for 5 minutes\n\n\t\t\t\treturn metrics;\n\t\t\t}\n\n\t\t\treturn null;\n\t\t} catch (error) {\n\t\t\tlogger.error(`Error getting entity metrics for ${entityId}:`, error);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Get entity metrics history\n\t */\n\tasync getRecommenderMetricsHistory(\n\t\tentityId: UUID,\n\t): Promise<RecommenderMetricsHistory[]> {\n\t\tlogger.debug(\"getting recommender metrics history\", entityId);\n\t\ttry {\n\t\t\t// Check cache first\n\t\t\tconst cacheKey = `entity:${entityId}:history`;\n\t\t\tconst cachedHistory =\n\t\t\t\tawait this.runtime.getCache<RecommenderMetricsHistory[]>(cacheKey);\n\n\t\t\tif (cachedHistory) {\n\t\t\t\treturn cachedHistory as RecommenderMetricsHistory[];\n\t\t\t}\n\n\t\t\t// Search for history in memory\n\t\t\tconst query = `entity metrics history for entity ${entityId}`;\n\t\t\tconst embedding = await this.runtime.useModel(\n\t\t\t\tModelType.TEXT_EMBEDDING,\n\t\t\t\tquery,\n\t\t\t);\n\n\t\t\tconst memories = await this.runtime.searchMemories({\n\t\t\t\ttableName: \"recommender_metrics_history\",\n\t\t\t\tembedding,\n\t\t\t\tmatch_threshold: 0.7,\n\t\t\t\tlimit: 10,\n\t\t\t});\n\n\t\t\tconst historyEntries: RecommenderMetricsHistory[] = [];\n\n\t\t\tfor (const memory of memories) {\n\t\t\t\tconst history = asContentObject<RecommenderMetricsHistory>(\n\t\t\t\t\tmemory.content.history,\n\t\t\t\t\t[\"entityId\"],\n\t\t\t\t);\n\t\t\t\tif (history && history.entityId === entityId) {\n\t\t\t\t\thistoryEntries.push(history);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Sort by timestamp, newest first\n\t\t\tconst sortedEntries = historyEntries.sort(\n\t\t\t\t(a, b) => b.timestamp.getTime() - a.timestamp.getTime(),\n\t\t\t);\n\n\t\t\t// Cache the history\n\t\t\tawait this.runtime.setCache<RecommenderMetricsHistory[]>(\n\t\t\t\tcacheKey,\n\t\t\t\tsortedEntries,\n\t\t\t); // Cache for 1 hour\n\n\t\t\treturn sortedEntries;\n\t\t} catch (error) {\n\t\t\tlogger.error(\n\t\t\t\t`Error getting entity metrics history for ${entityId}:`,\n\t\t\t\terror,\n\t\t\t);\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Initialize entity metrics\n\t */\n\tasync initializeRecommenderMetrics(\n\t\tentityId: UUID,\n\t\tplatform: string,\n\t): Promise<void> {\n\t\tlogger.debug(\"initializing recommender metrics\", entityId, platform);\n\t\ttry {\n\t\t\tconst initialMetrics: RecommenderMetrics = {\n\t\t\t\tentityId,\n\t\t\t\tplatform,\n\t\t\t\ttotalRecommendations: 0,\n\t\t\t\tsuccessfulRecs: 0,\n\t\t\t\tconsistencyScore: 50,\n\t\t\t\ttrustScore: 50,\n\t\t\t\tfailedTrades: 0,\n\t\t\t\ttotalProfit: 0,\n\t\t\t\tavgTokenPerformance: 0,\n\t\t\t\tlastUpdated: new Date(),\n\t\t\t\tcreatedAt: new Date(),\n\t\t\t};\n\n\t\t\tawait this.storeRecommenderMetrics(initialMetrics);\n\n\t\t\t// Also create initial history entry\n\t\t\tconst historyEntry: RecommenderMetricsHistory = {\n\t\t\t\tentityId,\n\t\t\t\tmetrics: initialMetrics,\n\t\t\t\ttimestamp: new Date(),\n\t\t\t};\n\n\t\t\tawait this.storeRecommenderMetricsHistory(historyEntry);\n\t\t} catch (error) {\n\t\t\tlogger.error(`Error initializing entity metrics for ${entityId}:`, error);\n\t\t}\n\t}\n\n\t/**\n\t * Get token performance\n\t */\n\tasync getTokenPerformance(\n\t\ttokenAddress: string,\n\t\tchain: string,\n\t): Promise<TokenPerformance | null> {\n\t\tlogger.debug(\"getting token performance\", tokenAddress, chain);\n\t\ttry {\n\t\t\t// Check cache first\n\t\t\tconst cacheKey = `token:${chain}:${tokenAddress}:performance`;\n\t\t\tconst cachedToken =\n\t\t\t\tawait this.runtime.getCache<TokenPerformance>(cacheKey);\n\n\t\t\tif (cachedToken) {\n\t\t\t\treturn cachedToken as TokenPerformance;\n\t\t\t}\n\n\t\t\t// Search for token in memory\n\t\t\tconst query = `token performance for ${tokenAddress}`;\n\t\t\tconst embedding = await this.runtime.useModel(\n\t\t\t\tModelType.TEXT_EMBEDDING,\n\t\t\t\tquery,\n\t\t\t);\n\n\t\t\tconst memories = await this.runtime.searchMemories({\n\t\t\t\ttableName: \"tokens\",\n\t\t\t\tembedding,\n\t\t\t\tmatch_threshold: 0.7,\n\t\t\t\tlimit: 1,\n\t\t\t});\n\n\t\t\tif (memories.length > 0 && memories[0].content.token) {\n\t\t\t\tconst token = memories[0].content.token as TokenPerformance;\n\n\t\t\t\t// Cache the token\n\t\t\t\tawait this.runtime.setCache<TokenPerformance>(cacheKey, token); // Cache for 5 minutes\n\n\t\t\t\treturn token;\n\t\t\t}\n\n\t\t\treturn null;\n\t\t} catch (error) {\n\t\t\tlogger.error(\n\t\t\t\t`Error getting token performance for ${tokenAddress}:`,\n\t\t\t\terror,\n\t\t\t);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Get open positions with balance\n\t */\n\tasync getOpenPositionsWithBalance(): Promise<PositionWithBalance[]> {\n\t\tlogger.debug(\"getting open positions with balance\");\n\t\ttry {\n\t\t\t// Check cache first\n\t\t\tconst cacheKey = \"positions:open:with-balance\";\n\t\t\tconst cachedPositions =\n\t\t\t\tawait this.runtime.getCache<PositionWithBalance[]>(cacheKey);\n\n\t\t\tif (cachedPositions) {\n\t\t\t\treturn cachedPositions as PositionWithBalance[];\n\t\t\t}\n\n\t\t\t// Search for open positions in memory\n\t\t\tconst query = \"open positions with balance\";\n\t\t\tconst embedding = await this.runtime.useModel(\n\t\t\t\tModelType.TEXT_EMBEDDING,\n\t\t\t\tquery,\n\t\t\t);\n\n\t\t\tconst memories = await this.runtime.searchMemories({\n\t\t\t\ttableName: \"positions\",\n\t\t\t\tembedding,\n\t\t\t\tmatch_threshold: 0.7,\n\t\t\t\tlimit: 50,\n\t\t\t});\n\n\t\t\tconst positions: PositionWithBalance[] = [];\n\n\t\t\tfor (const memory of memories) {\n\t\t\t\tconst position = asContentObject<Position>(memory.content.position, [\n\t\t\t\t\t\"id\",\n\t\t\t\t\t\"entityId\",\n\t\t\t\t\t\"tokenAddress\",\n\t\t\t\t]);\n\t\t\t\tif (position) {\n\t\t\t\t\t// Check if position is open\n\t\t\t\t\tif (position.status === \"OPEN\") {\n\t\t\t\t\t\t// Convert to PositionWithBalance\n\t\t\t\t\t\tpositions.push({\n\t\t\t\t\t\t\t...position,\n\t\t\t\t\t\t\tbalance: BigInt(position.balance || \"0\") as never,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Cache the positions\n\t\t\tawait this.runtime.setCache<PositionWithBalance[]>(cacheKey, positions); // Cache for 5 minutes\n\n\t\t\treturn positions;\n\t\t} catch (error) {\n\t\t\tlogger.error(\"Error getting open positions with balance:\", error);\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Get positions transactions\n\t */\n\tasync getPositionsTransactions(positionIds: UUID[]): Promise<Transaction[]> {\n\t\tlogger.debug(\"getting positions transactions\", positionIds);\n\t\ttry {\n\t\t\tconst allTransactions: Transaction[] = [];\n\n\t\t\tfor (const positionId of positionIds) {\n\t\t\t\tconst transactions = await this.getTransactionsByPosition(positionId);\n\t\t\t\tallTransactions.push(...transactions);\n\t\t\t}\n\n\t\t\treturn allTransactions;\n\t\t} catch (error) {\n\t\t\tlogger.error(\"Error getting transactions for positions:\", error);\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Get formatted portfolio report\n\t */\n\tasync getFormattedPortfolioReport(entityId?: UUID): Promise<string> {\n\t\tlogger.debug(\"getting formatted portfolio report\", entityId);\n\t\ttry {\n\t\t\t// Get positions\n\t\t\tconst positions = await this.getOpenPositionsWithBalance();\n\n\t\t\t// Filter by entity if provided\n\t\t\tconst filteredPositions = entityId\n\t\t\t\t? positions.filter((p) => p.entityId === entityId)\n\t\t\t\t: positions;\n\n\t\t\tif (filteredPositions.length === 0) {\n\t\t\t\treturn \"No open positions found.\";\n\t\t\t}\n\n\t\t\t// Get tokens and transactions\n\t\t\tconst tokens: TokenPerformance[] = [];\n\t\t\tconst tokenSet = new Set<string>();\n\n\t\t\tfor (const position of filteredPositions) {\n\t\t\t\tif (tokenSet.has(`${position.chain}:${position.tokenAddress}`))\n\t\t\t\t\tcontinue;\n\n\t\t\t\tconst token = await this.getTokenPerformance(\n\t\t\t\t\tposition.tokenAddress,\n\t\t\t\t\tposition.chain,\n\t\t\t\t);\n\t\t\t\tif (token) tokens.push(token);\n\n\t\t\t\ttokenSet.add(`${position.chain}:${position.tokenAddress}`);\n\t\t\t}\n\n\t\t\t// Get transactions\n\t\t\tconst transactions = await this.getPositionsTransactions(\n\t\t\t\tfilteredPositions.map((p) => p.id),\n\t\t\t);\n\n\t\t\t// Format the report\n\t\t\tconst report = formatFullReport(tokens, filteredPositions, transactions);\n\n\t\t\treturn `\nPortfolio Summary:\nTotal Current Value: ${report.totalCurrentValue}\nTotal Realized P&L: ${report.totalRealizedPnL}\nTotal Unrealized P&L: ${report.totalUnrealizedPnL}\nTotal P&L: ${report.totalPnL}\n\nPositions:\n${report.positionReports.join(\"\\n\")}\n\nTokens:\n${report.tokenReports.join(\"\\n\")}\n `.trim();\n\t\t} catch (error) {\n\t\t\tlogger.error(\"Error generating portfolio report:\", error);\n\t\t\treturn \"Error generating portfolio report.\";\n\t\t}\n\t}\n\n\tasync initialize(runtime: IAgentRuntime): Promise<void> {\n\t\tlogger.info(\"[CommunityInvestorService] Initializing...\");\n\t\tthis.apiKeys.birdeye = runtime.getSetting(\"BIRDEYE_API_KEY\") as\n\t\t\t| string\n\t\t\t| undefined;\n\t\tthis.apiKeys.moralis = runtime.getSetting(\"MORALIS_API_KEY\") as\n\t\t\t| string\n\t\t\t| undefined;\n\t\t// Load the user registry\n\t\tawait this.loadUserRegistry();\n\t\tlogger.info(\"[CommunityInvestorService] Initialized.\");\n\t}\n\n\t/**\n\t * Fetches token data from an external API.\n\t * Uses Birdeye and DexScreener for real market data.\n\t */\n\tasync getTokenAPIData(\n\t\taddress: string,\n\t\tchain: SupportedChain,\n\t): Promise<TokenAPIData | null> {\n\t\tlogger.debug(\n\t\t\t`[CommunityInvestorService] Fetching token API data for ${address} on ${chain}`,\n\t\t);\n\n\t\ttry {\n\t\t\tlet tokenData: TokenAPIData = {};\n\n\t\t\tif (chain === SupportedChain.SOLANA) {\n\t\t\t\t// Fetch from Birdeye for Solana tokens\n\t\t\t\ttry {\n\t\t\t\t\tconst [tokenOverview, price, security, tradeData, dexScreenerData] =\n\t\t\t\t\t\tawait Promise.all([\n\t\t\t\t\t\t\tthis.birdeyeClient.fetchTokenOverview(address, {\n\t\t\t\t\t\t\t\tchain: \"solana\",\n\t\t\t\t\t\t\t\texpires: \"5m\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\tthis.birdeyeClient.fetchPrice(address, {\n\t\t\t\t\t\t\t\tchain: \"solana\",\n\t\t\t\t\t\t\t\texpires: \"1m\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\tthis.birdeyeClient.fetchTokenSecurity(address, {\n\t\t\t\t\t\t\t\tchain: \"solana\",\n\t\t\t\t\t\t\t\texpires: \"10m\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\tthis.birdeyeClient.fetchTokenTradeData(address, {\n\t\t\t\t\t\t\t\tchain: \"solana\",\n\t\t\t\t\t\t\t\texpires: \"5m\",\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\tthis.dexscreenerClient.search(address, { expires: \"5m\" }),\n\t\t\t\t\t\t]);\n\n\t\t\t\t\tconst dexPair = dexScreenerData.pairs?.[0];\n\n\t\t\t\t\ttokenData = {\n\t\t\t\t\t\tname: tokenOverview.name || dexPair?.baseToken?.name,\n\t\t\t\t\t\tsymbol: tokenOverview.symbol || dexPair?.baseToken?.symbol,\n\t\t\t\t\t\tcurrentPrice: price || parseFloat(dexPair?.priceUsd || \"0\"),\n\t\t\t\t\t\tliquidity: dexPair?.liquidity?.usd || 0,\n\t\t\t\t\t\tmarketCap: dexPair?.marketCap || tradeData.market || 0,\n\t\t\t\t\t\tisKnownScam: false, // Would need additional scam detection logic\n\t\t\t\t\t};\n\n\t\t\t\t\t// Calculate ATH and ATL from recent trade data if available\n\t\t\t\t\tif (tradeData) {\n\t\t\t\t\t\tconst recent24hPrices = [\n\t\t\t\t\t\t\ttradeData.price,\n\t\t\t\t\t\t\ttradeData.history_24h_price,\n\t\t\t\t\t\t\ttradeData.history_12h_price,\n\t\t\t\t\t\t\ttradeData.history_8h_price,\n\t\t\t\t\t\t\ttradeData.history_6h_price,\n\t\t\t\t\t\t\ttradeData.history_4h_price,\n\t\t\t\t\t\t\ttradeData.history_2h_price,\n\t\t\t\t\t\t\ttradeData.history_1h_price,\n\t\t\t\t\t\t\ttradeData.history_30m_price,\n\t\t\t\t\t\t].filter((p) => p != null && p > 0);\n\n\t\t\t\t\t\tif (recent24hPrices.length > 0) {\n\t\t\t\t\t\t\ttokenData.ath = Math.max(...recent24hPrices);\n\t\t\t\t\t\t\ttokenData.atl = Math.min(...recent24hPrices);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Create simplified price history from trade data\n\t\t\t\t\t\tconst now = Date.now();\n\t\t\t\t\t\ttokenData.priceHistory = [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttimestamp: now - 24 * 60 * 60 * 1000,\n\t\t\t\t\t\t\t\tprice: tradeData.history_24h_price || tradeData.price,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttimestamp: now - 12 * 60 * 60 * 1000,\n\t\t\t\t\t\t\t\tprice: tradeData.history_12h_price || tradeData.price,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttimestamp: now - 8 * 60 * 60 * 1000,\n\t\t\t\t\t\t\t\tprice: tradeData.history_8h_price || tradeData.price,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttimestamp: now - 6 * 60 * 60 * 1000,\n\t\t\t\t\t\t\t\tprice: tradeData.history_6h_price || tradeData.price,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttimestamp: now - 4 * 60 * 60 * 1000,\n\t\t\t\t\t\t\t\tprice: tradeData.history_4h_price || tradeData.price,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttimestamp: now - 2 * 60 * 60 * 1000,\n\t\t\t\t\t\t\t\tprice: tradeData.history_2h_price || tradeData.price,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttimestamp: now - 1 * 60 * 60 * 1000,\n\t\t\t\t\t\t\t\tprice: tradeData.history_1h_price || tradeData.price,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttimestamp: now - 30 * 60 * 1000,\n\t\t\t\t\t\t\t\tprice: tradeData.history_30m_price || tradeData.price,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{ timestamp: now, price: tradeData.price },\n\t\t\t\t\t\t].filter((p) => p.price != null && p.price > 0);\n\n\t\t\t\t\t\t// Simple scam detection based on trade data patterns\n\t\t\t\t\t\tconst hasRugPullPattern =\n\t\t\t\t\t\t\ttradeData.price_change_24h_percent < -90 || // 90% drop in 24h\n\t\t\t\t\t\t\t(tradeData.volume_24h_usd < 1000 &&\n\t\t\t\t\t\t\t\ttokenData.marketCap &&\n\t\t\t\t\t\t\t\ttokenData.marketCap > 100000) || // Low volume but high market cap\n\t\t\t\t\t\t\t(security && security.top10HolderPercent > 95); // Top 10 holders own >95%\n\n\t\t\t\t\t\ttokenData.isKnownScam = hasRugPullPattern;\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t`[CommunityInvestorService] Error fetching Solana token data for ${address}:`,\n\t\t\t\t\t\terror,\n\t\t\t\t\t);\n\n\t\t\t\t\t// Fallback to DexScreener only\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst dexScreenerData = await this.dexscreenerClient.search(\n\t\t\t\t\t\t\taddress,\n\t\t\t\t\t\t\t{ expires: \"5m\" },\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst dexPair = dexScreenerData.pairs?.[0];\n\n\t\t\t\t\t\tif (dexPair) {\n\t\t\t\t\t\t\ttokenData = {\n\t\t\t\t\t\t\t\tname: dexPair.baseToken.name,\n\t\t\t\t\t\t\t\tsymbol: dexPair.baseToken.symbol,\n\t\t\t\t\t\t\t\tcurrentPrice: parseFloat(dexPair.priceUsd || \"0\"),\n\t\t\t\t\t\t\t\tliquidity: dexPair.liquidity?.usd || 0,\n\t\t\t\t\t\t\t\tmarketCap: dexPair.marketCap || 0,\n\t\t\t\t\t\t\t\tisKnownScam: false, // Dexscreener doesn't directly provide this\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t// No extensive history from this fallback, but it's better than nothing\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t\t\t`[CommunityInvestorService] DexScreener found no pair for ${address} after Birdeye failure.`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t// Explicitly return null if DexScreener also fails for Solana\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (fallbackError) {\n\t\t\t\t\t\tlogger.error(\n\t\t\t\t\t\t\t`[CommunityInvestorService] Fallback DexScreener search also failed for ${address}:`,\n\t\t\t\t\t\t\tfallbackError,\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn null; // Return null on fallback failure too\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (\n\t\t\t\tchain === SupportedChain.ETHEREUM ||\n\t\t\t\tchain === SupportedChain.BASE\n\t\t\t) {\n\t\t\t\t// For Ethereum and Base, use DexScreener as primary source\n\t\t\t\ttry {\n\t\t\t\t\tconst dexScreenerData = await this.dexscreenerClient.search(address, {\n\t\t\t\t\t\texpires: \"5m\",\n\t\t\t\t\t});\n\t\t\t\t\tconst chainFilter =\n\t\t\t\t\t\tchain === SupportedChain.ETHEREUM ? \"ethereum\" : \"base\";\n\t\t\t\t\tconst dexPair = dexScreenerData.pairs?.find(\n\t\t\t\t\t\t(pair) => pair.chainId.toLowerCase() === chainFilter,\n\t\t\t\t\t);\n\n\t\t\t\t\tif (dexPair) {\n\t\t\t\t\t\ttokenData = {\n\t\t\t\t\t\t\tname: dexPair.baseToken.name,\n\t\t\t\t\t\t\tsymbol: dexPair.baseToken.symbol,\n\t\t\t\t\t\t\tcurrentPrice: parseFloat(dexPair.priceUsd || \"0\"),\n\t\t\t\t\t\t\tliquidity: dexPair.liquidity?.usd || 0,\n\t\t\t\t\t\t\tmarketCap: dexPair.marketCap || 0,\n\t\t\t\t\t\t\tisKnownScam: false,\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\t// Extract price history from DexScreener price changes\n\t\t\t\t\t\tconst now = Date.now();\n\t\t\t\t\t\tconst currentPrice = parseFloat(dexPair.priceUsd || \"0\");\n\n\t\t\t\t\t\ttokenData.priceHistory = [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttimestamp: now - 24 * 60 * 60 * 1000,\n\t\t\t\t\t\t\t\tprice:\n\t\t\t\t\t\t\t\t\tcurrentPrice / (1 + (dexPair.priceChange?.h24 || 0) / 100),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttimestamp: now - 6 * 60 * 60 * 1000,\n\t\t\t\t\t\t\t\tprice:\n\t\t\t\t\t\t\t\t\tcurrentPrice / (1 + (dexPair.priceChange?.h6 || 0) / 100),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttimestamp: now - 1 * 60 * 60 * 1000,\n\t\t\t\t\t\t\t\tprice:\n\t\t\t\t\t\t\t\t\tcurrentPrice / (1 + (dexPair.priceChange?.h1 || 0) / 100),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttimestamp: now - 5 * 60 * 1000,\n\t\t\t\t\t\t\t\tprice:\n\t\t\t\t\t\t\t\t\tcurrentPrice / (1 + (dexPair.priceChange?.m5 || 0) / 100),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{ timestamp: now, price: currentPrice },\n\t\t\t\t\t\t].filter((p) => p.price > 0);\n\n\t\t\t\t\t\tif (tokenData.priceHistory.length > 0) {\n\t\t\t\t\t\t\ttokenData.ath = Math.max(\n\t\t\t\t\t\t\t\t...tokenData.priceHistory.map((p) => p.price),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\ttokenData.atl = Math.min(\n\t\t\t\t\t\t\t\t...tokenData.priceHistory.map((p) => p.price),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Simple scam detection for Ethereum/Base tokens\n\t\t\t\t\t\tconst hasRugPullPattern =\n\t\t\t\t\t\t\t(dexPair.priceChange?.h24 || 0) < -90 || // 90% drop in 24h\n\t\t\t\t\t\t\t((dexPair.volume?.h24 || 0) < 1000 &&\n\t\t\t\t\t\t\t\t(dexPair.marketCap || 0) > 100000); // Low volume but high market cap\n\n\t\t\t\t\t\ttokenData.isKnownScam = hasRugPullPattern;\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tlogger.error(\n\t\t\t\t\t\t`[CommunityInvestorService] Error fetching ${chain} token data for ${address}:`,\n\t\t\t\t\t\terror,\n\t\t\t\t\t);\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tlogger.warn(`[CommunityInvestorService] Unsupported chain: ${chain}`);\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// Fill in derived defaults when at least one source populated tokenData.\n\t\t\tif (Object.keys(tokenData).length > 0) {\n\t\t\t\tif (!tokenData.ath && tokenData.currentPrice) {\n\t\t\t\t\ttokenData.ath = tokenData.currentPrice * 1.1; // Assume current price is close to ATH if unknown\n\t\t\t\t}\n\t\t\t\tif (!tokenData.atl && tokenData.currentPrice) {\n\t\t\t\t\ttokenData.atl = tokenData.currentPrice * 0.9; // Assume current price is close to ATL if unknown\n\t\t\t\t}\n\t\t\t\tlogger.debug(\n\t\t\t\t\t`[CommunityInvestorService] Successfully fetched token data for ${tokenData.symbol || address} (${address})`,\n\t\t\t\t);\n\t\t\t\treturn tokenData;\n\t\t\t} else if (chain !== SupportedChain.SOLANA) {\n\t\t\t\t// If not SOLANA and tokenData is still empty (e.g. ETH/BASE failed)\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`[CommunityInvestorService] Failed to fetch token data for ${address} on ${chain} after all attempts.`,\n\t\t\t\t);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\t// If it's SOLANA and tokenData is empty, we would have returned null already.\n\t\t\t// For other chains, if tokenData is empty, this means their primary fetch failed.\n\t\t\t// This final return null should ideally not be hit if all paths correctly return null on failure.\n\t\t\treturn null;\n\t\t} catch (error) {\n\t\t\tlogger.error(\n\t\t\t\t`[CommunityInvestorService] Unexpected error fetching token API data for ${address}:`,\n\t\t\t\terror,\n\t\t\t);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tasync isLikelyScamOrRug(\n\t\ttokenData: TokenAPIData,\n\t\trecommendationTimestamp: number,\n\t): Promise<boolean> {\n\t\t// Check if already flagged as scam\n\t\tif (tokenData.isKnownScam) {\n\t\t\tlogger.warn(\n\t\t\t\t`[CommunityInvestorService] Token ${tokenData.symbol} already flagged as known scam`,\n\t\t\t);\n\t\t\treturn true;\n\t\t}\n\n\t\tconst warnings: string[] = [];\n\t\tlet riskScore = 0;\n\n\t\t// 1. Price drop analysis\n\t\tconst pricesPostRecommendation =\n\t\t\ttokenData.priceHistory?.filter(\n\t\t\t\t(p) => p.timestamp > recommendationTimestamp,\n\t\t\t) || [];\n\n\t\tif (pricesPostRecommendation.length > 1) {\n\t\t\tconst peakPricePostRec = Math.max(\n\t\t\t\t...pricesPostRecommendation.map((p) => p.price),\n\t\t\t);\n\t\t\tconst lastKnownPricePostRec =\n\t\t\t\tpricesPostRecommendation[pricesPostRecommendation.length - 1].price;\n\t\t\tconst currentPrice = tokenData.currentPrice || 0;\n\n\t\t\t// Severe price drop from peak (>90%)\n\t\t\tif (\n\t\t\t\tpeakPricePostRec > 0 &&\n\t\t\t\tlastKnownPricePostRec < peakPricePostRec * 0.1 &&\n\t\t\t\tcurrentPrice < peakPricePostRec * 0.1\n\t\t\t) {\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Severe price drop: >90% from peak ($${peakPricePostRec.toFixed(6)} to $${currentPrice.toFixed(6)})`,\n\t\t\t\t);\n\t\t\t\triskScore += 40;\n\t\t\t}\n\t\t\t// Major price drop (>70%)\n\t\t\telse if (\n\t\t\t\tpeakPricePostRec > 0 &&\n\t\t\t\tlastKnownPricePostRec < peakPricePostRec * 0.3 &&\n\t\t\t\tcurrentPrice < peakPricePostRec * 0.3\n\t\t\t) {\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Major price drop: >70% from peak ($${peakPricePostRec.toFixed(6)} to $${currentPrice.toFixed(6)})`,\n\t\t\t\t);\n\t\t\t\triskScore += 25;\n\t\t\t}\n\t\t}\n\n\t\t// 2. Liquidity-to-MarketCap ratio analysis\n\t\tconst marketCap = tokenData.marketCap || 0;\n\t\tconst liquidity = tokenData.liquidity || 0;\n\n\t\tif (marketCap > 10000 && liquidity > 0) {\n\t\t\tconst liquidityRatio = liquidity / marketCap;\n\n\t\t\t// Extremely low liquidity ratio (<0.5%)\n\t\t\tif (liquidityRatio < 0.005) {\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Extremely low liquidity ratio: ${(liquidityRatio * 100).toFixed(2)}% (Liquidity: $${liquidity.toFixed(0)}, MC: $${marketCap.toFixed(0)})`,\n\t\t\t\t);\n\t\t\t\triskScore += 30;\n\t\t\t}\n\t\t\t// Very low liquidity ratio (<1%)\n\t\t\telse if (liquidityRatio < 0.01) {\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Very low liquidity ratio: ${(liquidityRatio * 100).toFixed(2)}%`,\n\t\t\t\t);\n\t\t\t\triskScore += 20;\n\t\t\t}\n\t\t\t// Low liquidity ratio (<2%)\n\t\t\telse if (liquidityRatio < 0.02) {\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Low liquidity ratio: ${(liquidityRatio * 100).toFixed(2)}%`,\n\t\t\t\t);\n\t\t\t\triskScore += 10;\n\t\t\t}\n\t\t}\n\n\t\t// 3. Absolute liquidity thresholds\n\t\tif (liquidity > 0) {\n\t\t\tif (liquidity < 500) {\n\t\t\t\twarnings.push(`Critical liquidity: $${liquidity.toFixed(0)}`);\n\t\t\t\triskScore += 35;\n\t\t\t} else if (liquidity < 2000) {\n\t\t\t\twarnings.push(`Very low liquidity: $${liquidity.toFixed(0)}`);\n\t\t\t\triskScore += 20;\n\t\t\t} else if (liquidity < 5000) {\n\t\t\t\twarnings.push(`Low liquidity: $${liquidity.toFixed(0)}`);\n\t\t\t\triskScore += 10;\n\t\t\t}\n\t\t} else {\n\t\t\twarnings.push(\"No liquidity data available\");\n\t\t\triskScore += 15;\n\t\t}\n\n\t\t// 4. Market cap sanity check\n\t\tif (marketCap > 0 && tokenData.currentPrice && tokenData.currentPrice > 0) {\n\t\t\t// If market cap seems unrealistically high compared to liquidity\n\t\t\tif (marketCap > 1000000 && liquidity < 10000) {\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Suspicious MC/Liquidity: MC $${marketCap.toFixed(0)} vs Liquidity $${liquidity.toFixed(0)}`,\n\t\t\t\t);\n\t\t\t\triskScore += 25;\n\t\t\t}\n\t\t}\n\n\t\t// 5. Price movement patterns (volatility spikes)\n\t\tif (tokenData.priceHistory && tokenData.priceHistory.length >= 3) {\n\t\t\tconst prices = tokenData.priceHistory.map((p) => p.price);\n\t\t\tconst priceChanges: number[] = [];\n\n\t\t\tfor (let i = 1; i < prices.length; i++) {\n\t\t\t\tif (prices[i - 1] > 0) {\n\t\t\t\t\tconst change = ((prices[i] - prices[i - 1]) / prices[i - 1]) * 100;\n\t\t\t\t\tpriceChanges.push(Math.abs(change));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (priceChanges.length > 0) {\n\t\t\t\tconst avgVolatility =\n\t\t\t\t\tpriceChanges.reduce((sum, change) => sum + change, 0) /\n\t\t\t\t\tpriceChanges.length;\n\t\t\t\tconst maxChange = Math.max(...priceChanges);\n\n\t\t\t\t// Extreme volatility patterns\n\t\t\t\tif (maxChange > 200) {\n\t\t\t\t\t// >200% price change in one period\n\t\t\t\t\twarnings.push(\n\t\t\t\t\t\t`Extreme volatility: ${maxChange.toFixed(1)}% max change`,\n\t\t\t\t\t);\n\t\t\t\t\triskScore += 20;\n\t\t\t\t} else if (avgVolatility > 50) {\n\t\t\t\t\t// Average >50% volatility\n\t\t\t\t\twarnings.push(\n\t\t\t\t\t\t`High volatility: ${avgVolatility.toFixed(1)}% average change`,\n\t\t\t\t\t);\n\t\t\t\t\triskScore += 10;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// 6. Age-based risk (newer tokens are riskier)\n\t\tconst tokenAge = Date.now() - recommendationTimestamp;\n\t\tconst ageInHours = tokenAge / (1000 * 60 * 60);\n\n\t\tif (ageInHours < 24) {\n\t\t\twarnings.push(`Very new token: ${ageInHours.toFixed(1)} hours old`);\n\t\t\triskScore += 15;\n\t\t} else if (ageInHours < 72) {\n\t\t\twarnings.push(`New token: ${ageInHours.toFixed(1)} hours old`);\n\t\t\triskScore += 8;\n\t\t}\n\n\t\t// Determine if it's likely a scam/rug based on risk score\n\t\tconst isLikelyRug = riskScore >= 50; // Threshold for rug pull classification\n\n\t\tif (warnings.length > 0) {\n\t\t\tconst logLevel = isLikelyRug ? \"warn\" : \"debug\";\n\t\t\tlogger[logLevel](\n\t\t\t\t`[CommunityInvestorService] Token ${tokenData.symbol} risk analysis (Score: ${riskScore}/100): ${warnings.join(\"; \")}`,\n\t\t\t);\n\t\t}\n\n\t\tif (isLikelyRug) {\n\t\t\tlogger.warn(\n\t\t\t\t`[CommunityInvestorService] Token ${tokenData.symbol} classified as likely scam/rug (Risk Score: ${riskScore}/100)`,\n\t\t\t);\n\t\t}\n\n\t\treturn isLikelyRug;\n\t}\n\n\tasync evaluateRecommendationPerformance(\n\t\trecommendation: Recommendation,\n\t\ttokenData: TokenAPIData,\n\t): Promise<RecommendationMetric> {\n\t\tlogger.debug(\n\t\t\t`[CommunityInvestorService] Evaluating performance for rec ID: ${recommendation.id}`,\n\t\t);\n\t\tconst metric: RecommendationMetric = {\n\t\t\tevaluationTimestamp: Date.now(),\n\t\t\tisScamOrRug: await this.isLikelyScamOrRug(\n\t\t\t\ttokenData,\n\t\t\t\trecommendation.timestamp,\n\t\t\t),\n\t\t\tnotes: \"\",\n\t\t};\n\t\tconst priceAtRec =\n\t\t\trecommendation.priceAtRecommendation ||\n\t\t\ttokenData.priceHistory?.find(\n\t\t\t\t(p) => p.timestamp >= recommendation.timestamp,\n\t\t\t)?.price ||\n\t\t\ttokenData.currentPrice ||\n\t\t\t0;\n\t\tconst pricesAfterRec =\n\t\t\ttokenData.priceHistory?.filter(\n\t\t\t\t(p) => p.timestamp > recommendation.timestamp,\n\t\t\t) || [];\n\n\t\tif (metric.isScamOrRug) {\n\t\t\tif (recommendation.recommendationType === \"BUY\") {\n\t\t\t\tmetric.potentialProfitPercent = -99;\n\t\t\t\tmetric.notes =\n\t\t\t\t\t\"Token identified as likely scam/rug pull after BUY recommendation.\";\n\t\t\t}\n\t\t\tif (recommendation.recommendationType === \"SELL\") {\n\t\t\t\tmetric.avoidedLossPercent = 99;\n\t\t\t\tmetric.notes =\n\t\t\t\t\t\"Criticism/SELL recommendation was correct; token identified as likely scam/rug pull.\";\n\t\t\t}\n\t\t\tlogger.debug(\n\t\t\t\t`[CommunityInvestorService] Rec ${recommendation.id} (Scam/Rug): Performance ${metric.potentialProfitPercent || metric.avoidedLossPercent}%`,\n\t\t\t);\n\t\t\treturn metric;\n\t\t}\n\n\t\tif (pricesAfterRec.length === 0) {\n\t\t\tmetric.notes =\n\t\t\t\t\"No significant price data available after recommendation time to evaluate performance yet.\";\n\t\t\tif (\n\t\t\t\ttokenData.currentPrice &&\n\t\t\t\ttokenData.currentPrice !== priceAtRec &&\n\t\t\t\tpriceAtRec > 0\n\t\t\t) {\n\t\t\t\tconst currentPerformance =\n\t\t\t\t\t((tokenData.currentPrice - priceAtRec) / priceAtRec) * 100;\n\t\t\t\tif (recommendation.recommendationType === \"BUY\")\n\t\t\t\t\tmetric.potentialProfitPercent = currentPerformance;\n\t\t\t\tif (recommendation.recommendationType === \"SELL\")\n\t\t\t\t\tmetric.avoidedLossPercent = -currentPerformance;\n\t\t\t\tmetric.notes =\n\t\t\t\t\t\"Evaluated based on current price vs price at recommendation.\";\n\t\t\t} else if (\n\t\t\t\tpriceAtRec === 0 &&\n\t\t\t\ttokenData.currentPrice &&\n\t\t\t\ttokenData.currentPrice > 0 &&\n\t\t\t\trecommendation.recommendationType === \"BUY\"\n\t\t\t) {\n\t\t\t\tmetric.potentialProfitPercent = Infinity; // Bought at 0, price is now > 0\n\t\t\t\tmetric.notes =\n\t\t\t\t\t\"Token acquired at effectively zero cost and now has value.\";\n\t\t\t} else if (\n\t\t\t\tpriceAtRec > 0 &&\n\t\t\t\ttokenData.currentPrice === 0 &&\n\t\t\t\trecommendation.recommendationType === \"SELL\"\n\t\t\t) {\n\t\t\t\tmetric.avoidedLossPercent = 100; // Sold before it went to zero\n\t\t\t\tmetric.notes = \"Token value went to zero after sell recommendation.\";\n\t\t\t}\n\t\t\tlogger.debug(\n\t\t\t\t`[CommunityInvestorService] Rec ${recommendation.id} (No prices after): Performance ${metric.potentialProfitPercent || metric.avoidedLossPercent}%`,\n\t\t\t);\n\t\t\treturn metric;\n\t\t}\n\t\tconst peakPriceAfterRec = Math.max(\n\t\t\t...pricesAfterRec.map((p) => p.price),\n\t\t\tpriceAtRec,\n\t\t);\n\t\tconst troughPriceAfterRec = Math.min(\n\t\t\t...pricesAfterRec.map((p) => p.price),\n\t\t\tpriceAtRec,\n\t\t);\n\n\t\tif (recommendation.recommendationType === \"BUY\") {\n\t\t\tif (priceAtRec > 0) {\n\t\t\t\tif (peakPriceAfterRec > priceAtRec) {\n\t\t\t\t\tmetric.potentialProfitPercent =\n\t\t\t\t\t\t((peakPriceAfterRec - priceAtRec) / priceAtRec) * 100;\n\t\t\t\t\tmetric.notes = `Potential profit to peak of $${peakPriceAfterRec.toFixed(4)} from $${priceAtRec.toFixed(4)}.`;\n\t\t\t\t} else {\n\t\t\t\t\tconst lossPrice = Math.min(\n\t\t\t\t\t\ttokenData.currentPrice || 0,\n\t\t\t\t\t\ttroughPriceAfterRec,\n\t\t\t\t\t);\n\t\t\t\t\tmetric.potentialProfitPercent =\n\t\t\t\t\t\t((lossPrice - priceAtRec) / priceAtRec) * 100;\n\t\t\t\t\tmetric.notes = `No profitable exit; current/trough price $${lossPrice.toFixed(4)} vs buy $${priceAtRec.toFixed(4)}.`;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Bought at zero or near-zero\n\t\t\t\tmetric.potentialProfitPercent = peakPriceAfterRec > 0 ? Infinity : 0; // Effectively infinite profit if price rose\n\t\t\t\tmetric.notes = `Bought at effectively zero, peak price $${peakPriceAfterRec.toFixed(4)}.`;\n\t\t\t}\n\t\t} else if (recommendation.recommendationType === \"SELL\") {\n\t\t\tif (priceAtRec > 0) {\n\t\t\t\tif (troughPriceAfterRec < priceAtRec) {\n\t\t\t\t\tmetric.avoidedLossPercent =\n\t\t\t\t\t\t((priceAtRec - troughPriceAfterRec) / priceAtRec) * 100;\n\t\t\t\t\tmetric.notes = `Avoided loss as price dropped to $${troughPriceAfterRec.toFixed(4)} from $${priceAtRec.toFixed(4)}.`;\n\t\t\t\t} else {\n\t\t\t\t\tconst missedProfitPrice = Math.max(\n\t\t\t\t\t\ttokenData.currentPrice || 0,\n\t\t\t\t\t\tpeakPriceAfterRec,\n\t\t\t\t\t);\n\t\t\t\t\tmetric.avoidedLossPercent =\n\t\t\t\t\t\t((priceAtRec - missedProfitPrice) / priceAtRec) * 100;\n\t\t\t\t\tmetric.notes = `Missed potential gains; price rose/stayed above $${priceAtRec.toFixed(4)}, reaching $${missedProfitPrice.toFixed(4)}.`;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Sold at zero (e.g. criticized a non-existent token that remained zero)\n\t\t\t\tmetric.avoidedLossPercent = 0; // No loss to avoid if it started at zero and stayed zero\n\t\t\t\tmetric.notes = `Token was at zero or near-zero at time of sell/criticism.`;\n\t\t\t}\n\t\t}\n\t\tlogger.debug(\n\t\t\t`[CommunityInvestorService] Rec ${recommendation.id}: Performance ${metric.potentialProfitPercent || metric.avoidedLossPercent}%`,\n\t\t);\n\t\treturn metric;\n\t}\n\n\tgetRecencyWeight(recommendationTimestamp: number): number {\n\t\tconst now = Date.now();\n\t\tconst ageInMilliseconds = now - recommendationTimestamp;\n\t\tconst ageInMonths = ageInMilliseconds / (1000 * 60 * 60 * 24 * 30.44);\n\t\tif (ageInMonths > this.RECENCY_WEIGHT_MONTHS) return 0.1;\n\t\treturn Math.max(0.1, 1 - (ageInMonths / this.RECENCY_WEIGHT_MONTHS) * 0.9);\n\t}\n\n\tgetConvictionWeight(conviction: Recommendation[\"conviction\"]): number {\n\t\tswitch (conviction) {\n\t\t\tcase \"HIGH\":\n\t\t\t\treturn 1.5;\n\t\t\tcase \"MEDIUM\":\n\t\t\t\treturn 1.0;\n\t\t\tcase \"LOW\":\n\t\t\t\treturn 0.5;\n\t\t\tdefault:\n\t\t\t\treturn 0.25;\n\t\t}\n\t}\n\n\tasync calculateUserTrustScore(\n\t\tuserId: UUID,\n\t\truntime: IAgentRuntime,\n\t\t_worldId?: UUID,\n\t): Promise<number> {\n\t\t// Matches interface now\n\t\tlogger.info(\n\t\t\t`[CommunityInvestorService] Starting calculateUserTrustScore for user ${userId} (components in world/room: ${this.componentWorldId})`,\n\t\t);\n\n\t\tconst componentResult = await runtime.getComponent(\n\t\t\tuserId,\n\t\t\tTRUST_MARKETPLACE_COMPONENT_TYPE,\n\t\t\tthis.componentWorldId,\n\t\t\truntime.agentId,\n\t\t);\n\n\t\tif (!componentResult) {\n\t\t\t// Create new profile for user\n\t\t\tconst newProfile: TrustMarketplaceComponentData = {\n\t\t\t\tversion: \"1.0.0\",\n\t\t\t\tuserId: userId,\n\t\t\t\ttrustScore: 0,\n\t\t\t\tlastTrustScoreCalculationTimestamp: Date.now(),\n\t\t\t\trecommendations: [],\n\t\t\t};\n\n\t\t\tawait runtime.createComponent({\n\t\t\t\tid: userId, // Use userId as component ID\n\t\t\t\tentityId: userId,\n\t\t\t\tagentId: runtime.agentId,\n\t\t\t\tworldId: this.componentWorldId,\n\t\t\t\troomId: this.componentRoomId,\n\t\t\t\tsourceEntityId: runtime.agentId,\n\t\t\t\ttype: TRUST_MARKETPLACE_COMPONENT_TYPE,\n\t\t\t\tcreatedAt: Date.now(),\n\t\t\t\tdata: newProfile,\n\t\t\t});\n\n\t\t\tthis.registerUser(userId);\n\t\t\tlogger.info(\n\t\t\t\t`[CommunityInvestorService] User ${userId} trust score is now: 0.00. Profile marked for update.`,\n\t\t\t);\n\t\t\treturn 0; // Return 0 for new user\n\t\t}\n\n\t\tconst userProfile = componentResult.data as TrustMarketplaceComponentData;\n\n\t\t// Ensure recommendations array exists\n\t\tif (!Array.isArray(userProfile.recommendations)) {\n\t\t\tlogger.warn(\n\t\t\t\t`[calculateUserTrustScore] User ${userId} profile recommendations was not an array. Initializing.`,\n\t\t\t);\n\t\t\tuserProfile.recommendations = [];\n\t\t}\n\n\t\t// Re-evaluate metrics for recommendations that need it\n\t\tlet _metricsUpdated = false;\n\t\tfor (const rec of userProfile.recommendations) {\n\t\t\tif (!rec.tokenAddress || !rec.chain) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`[calculateUserTrustScore] Rec ${rec.id} for user ${userId} missing address/chain. Skipping metric evaluation.`,\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Check if metrics need re-evaluation\n\t\t\tconst needsReEval =\n\t\t\t\t!rec.metrics?.evaluationTimestamp ||\n\t\t\t\tDate.now() - rec.metrics.evaluationTimestamp >\n\t\t\t\t\tthis.METRIC_REFRESH_INTERVAL;\n\n\t\t\tif (needsReEval) {\n\t\t\t\ttry {\n\t\t\t\t\tconst tokenData = await this.getTokenAPIData(\n\t\t\t\t\t\trec.tokenAddress,\n\t\t\t\t\t\trec.chain as SupportedChain,\n\t\t\t\t\t);\n\t\t\t\t\tif (!tokenData) {\n\t\t\t\t\t\tlogger.warn(\n\t\t\t\t\t\t\t`[calculateUserTrustScore] No token data for ${rec.tokenAddress} (rec ${rec.id}, user ${userId}) to update metrics.`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst newMetric = await this.evaluateRecommendationPerformance(\n\t\t\t\t\t\trec,\n\t\t\t\t\t\ttokenData,\n\t\t\t\t\t);\n\t\t\t\t\trec.metrics = newMetric;\n\t\t\t\t\t_metricsUpdated = true;\n\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t`[calculateUserTrustScore] Updated metrics for rec ${rec.id}, user ${userId}: ${JSON.stringify(newMetric)}`,\n\t\t\t\t\t);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tlogger.error(\n\t\t\t\t\t\t`[calculateUserTrustScore] Error updating metrics for rec ${rec.id}, user ${userId}:`,\n\t\t\t\t\t\terror,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tlogger.debug(\n\t\t\t\t\t`[calculateUserTrustScore] Rec ${rec.id} for user ${userId} has fresh metrics, skipping re-evaluation.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (!rec.metrics) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`[calculateUserTrustScore] Rec ${rec.id} for user ${userId} still has no metrics. It will not contribute to score.`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Calculate new trust score from profile\n\t\tconst { trustScore: updatedScore } =\n\t\t\tthis.calculateNewScoreFromProfile(userProfile);\n\n\t\tuserProfile.trustScore = updatedScore;\n\t\tuserProfile.lastTrustScoreCalculationTimestamp = Date.now();\n\n\t\t// Update the component\n\t\tawait runtime.updateComponent({\n\t\t\t...componentResult,\n\t\t\tdata: userProfile,\n\t\t});\n\n\t\tthis.registerUser(userId);\n\t\tlogger.info(\n\t\t\t`[CommunityInvestorService] User ${userId} trust score is now: ${updatedScore.toFixed(2)}. Profile updated.`,\n\t\t);\n\n\t\treturn updatedScore;\n\t}\n\n\t/**\n\t * Calculate trust score from user profile recommendations\n\t */\n\tprivate calculateNewScoreFromProfile(userProfile: UserTrustProfile): {\n\t\ttrustScore: number;\n\t} {\n\t\tconst recommendations = userProfile.recommendations || [];\n\n\t\tif (recommendations.length === 0) {\n\t\t\treturn { trustScore: 0 };\n\t\t}\n\n\t\t// Aggregate metrics across all recommendations\n\t\tconst aggregatedMetrics = {\n\t\t\ttotalCalls: recommendations.length,\n\t\t\tprofitableCalls: 0,\n\t\t\ttotalProfit: 0,\n\t\t\ttotalWeightedProfit: 0,\n\t\t\ttotalWeight: 0,\n\t\t\tprofits: [] as number[],\n\t\t\trugPromotions: 0,\n\t\t\tgoodCalls: 0,\n\t\t};\n\n\t\t// Process each recommendation\n\t\tfor (const rec of recommendations) {\n\t\t\tif (!rec.metrics) continue;\n\n\t\t\t// Get performance value\n\t\t\tlet performance = 0;\n\t\t\tconst potentialProfit = rec.metrics.potentialProfitPercent || 0;\n\n\t\t\t// Detect rugs based on extreme price drops\n\t\t\tconst isLikelyRug = rec.metrics.isScamOrRug || potentialProfit <= -80;\n\n\t\t\tif (isLikelyRug) {\n\t\t\t\t// Count rug promotions\n\t\t\t\tif (rec.recommendationType === \"BUY\") {\n\t\t\t\t\taggregatedMetrics.rugPromotions++;\n\t\t\t\t\tperformance = -100; // Heavy penalty\n\t\t\t\t} else if (rec.recommendationType === \"SELL\") {\n\t\t\t\t\t// Good warning about a rug\n\t\t\t\t\taggregatedMetrics.goodCalls++;\n\t\t\t\t\tperformance = rec.metrics.avoidedLossPercent || 50;\n\t\t\t\t}\n\t\t\t} else if (rec.recommendationType === \"BUY\") {\n\t\t\t\tperformance = potentialProfit;\n\t\t\t\tif (performance > 20) {\n\t\t\t\t\taggregatedMetrics.goodCalls++;\n\t\t\t\t}\n\t\t\t} else if (rec.recommendationType === \"SELL\") {\n\t\t\t\tperformance = rec.metrics.avoidedLossPercent || 0;\n\t\t\t\t// Good warning if token subsequently dropped significantly\n\t\t\t\tif (potentialProfit < -30) {\n\t\t\t\t\taggregatedMetrics.goodCalls++;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Apply weights for weighted average\n\t\t\tconst recencyWeight = this.getRecencyWeight(rec.timestamp);\n\t\t\tconst convictionWeight = this.getConvictionWeight(rec.conviction);\n\t\t\tconst totalRecWeight = recencyWeight * convictionWeight;\n\n\t\t\taggregatedMetrics.totalWeightedProfit += performance * totalRecWeight;\n\t\t\taggregatedMetrics.totalWeight += totalRecWeight;\n\t\t\taggregatedMetrics.totalProfit += performance;\n\t\t\taggregatedMetrics.profits.push(performance);\n\n\t\t\tif (performance > 0) {\n\t\t\t\taggregatedMetrics.profitableCalls++;\n\t\t\t}\n\t\t}\n\n\t\t// Calculate metrics for balanced trust score\n\t\tconst winRate =\n\t\t\taggregatedMetrics.profitableCalls / aggregatedMetrics.totalCalls;\n\t\tconst averageProfit =\n\t\t\taggregatedMetrics.totalProfit / aggregatedMetrics.totalCalls;\n\n\t\t// Calculate consistency (standard deviation)\n\t\tconst profitMean = averageProfit;\n\t\tconst variance =\n\t\t\taggregatedMetrics.profits.reduce(\n\t\t\t\t(sum, p) => sum + (p - profitMean) ** 2,\n\t\t\t\t0,\n\t\t\t) / aggregatedMetrics.profits.length;\n\t\tconst stdDev = Math.sqrt(variance);\n\t\tconst consistency = stdDev > 0 ? Math.max(0, 1 - stdDev / 100) : 1;\n\n\t\t// Simple Sharpe ratio (return / risk)\n\t\tconst sharpeRatio = stdDev > 0 ? averageProfit / stdDev : 0;\n\n\t\t// Alpha (simplified - performance vs average)\n\t\tconst marketAverage = 0; // Assume market average is 0 for simplicity\n\t\tconst alpha = averageProfit - marketAverage;\n\n\t\t// Create metrics object compatible with TrustScoreResult\n\t\tconst metrics = {\n\t\t\ttotalCalls: aggregatedMetrics.totalCalls,\n\t\t\tprofitableCalls: aggregatedMetrics.profitableCalls,\n\t\t\taverageProfit,\n\t\t\twinRate,\n\t\t\tsharpeRatio,\n\t\t\talpha,\n\t\t\tvolumePenalty: 0, // Not used in balanced calculator\n\t\t\tconsistency,\n\t\t};\n\n\t\t// Classify the user's archetype from observed performance metrics.\n\t\tlet archetype = \"newbie\"; // Default\n\t\tif (winRate > 0.7 && averageProfit > 30) {\n\t\t\tarchetype = \"elite_analyst\";\n\t\t} else if (winRate > 0.6 && averageProfit > 15) {\n\t\t\tarchetype = \"skilled_trader\";\n\t\t} else if (winRate > 0.5) {\n\t\t\tarchetype = \"technical_analyst\";\n\t\t} else if (\n\t\t\taggregatedMetrics.rugPromotions >\n\t\t\taggregatedMetrics.totalCalls * 0.5\n\t\t) {\n\t\t\tarchetype = \"rug_promoter\";\n\t\t} else if (aggregatedMetrics.totalCalls > 50 && winRate < 0.3) {\n\t\t\tarchetype = \"bot_spammer\";\n\t\t}\n\n\t\t// Calculate balanced trust score\n\t\tconst trustScore = this.balancedTrustCalculator.calculateBalancedTrustScore(\n\t\t\tmetrics,\n\t\t\tarchetype,\n\t\t\taggregatedMetrics.rugPromotions,\n\t\t\taggregatedMetrics.goodCalls,\n\t\t\taggregatedMetrics.totalCalls,\n\t\t);\n\n\t\tlogger.debug(\n\t\t\t`[calculateNewScoreFromProfile] User ${userProfile.userId}: ` +\n\t\t\t\t`archetype=${archetype}, winRate=${(winRate * 100).toFixed(1)}%, ` +\n\t\t\t\t`avgProfit=${averageProfit.toFixed(1)}%, trustScore=${trustScore.toFixed(1)}`,\n\t\t);\n\n\t\treturn { trustScore };\n\t}\n\n\t// --- Task Worker Execution --- (Could be in a separate tasks.ts file)\n\tprivate async executeProcessTradeDecision(\n\t\toptions: { recommendationId: UUID; userId: UUID },\n\t\ttask: Task,\n\t): Promise<void> {\n\t\tlogger.info(\n\t\t\t`[CommunityInvestorService] Task Worker: Processing rec: ${options.recommendationId}, user: ${options.userId}`,\n\t\t);\n\t\tconst { recommendationId, userId } = options;\n\t\tconst runtime = this.runtime;\n\t\tconst userProfileWorldId = runtime.agentId as UUID;\n\t\tconst componentResult = await runtime.getComponent(\n\t\t\tuserId,\n\t\t\tTRUST_MARKETPLACE_COMPONENT_TYPE,\n\t\t\tuserProfileWorldId,\n\t\t\truntime.agentId,\n\t\t);\n\n\t\tif (!componentResult?.data) {\n\t\t\tlogger.error(\n\t\t\t\t`Task Worker: UserProfile component not found for user ${userId}. Deleting task.`,\n\t\t\t);\n\t\t\tawait runtime.deleteTask(task.id as UUID);\n\t\t\treturn;\n\t\t}\n\t\tconst userProfile = componentResult.data as TrustMarketplaceComponentData;\n\t\tlet recommendation = userProfile.recommendations.find(\n\t\t\t(r) => r.id === recommendationId,\n\t\t);\n\n\t\tif (!recommendation) {\n\t\t\tlogger.error(\n\t\t\t\t`Task Worker: Rec ${recommendationId} not found in profile for user ${userId}. Deleting task.`,\n\t\t\t);\n\t\t\tawait runtime.deleteTask(task.id as UUID);\n\t\t\treturn;\n\t\t}\n\n\t\t// If already fully processed (and not just for cooldown), delete task.\n\t\tif (\n\t\t\trecommendation.processedForTradeDecision &&\n\t\t\t!(\n\t\t\t\tuserProfile.lastTradeDecisionMadeTimestamp &&\n\t\t\t\tDate.now() - userProfile.lastTradeDecisionMadeTimestamp <\n\t\t\t\t\tthis.USER_TRADE_COOLDOWN_HOURS * 3600000\n\t\t\t)\n\t\t) {\n\t\t\tlogger.info(\n\t\t\t\t`Task Worker: Rec ${recommendationId} already fully processed & not in cooldown. Deleting task.`,\n\t\t\t);\n\t\t\tawait runtime.deleteTask(task.id as UUID);\n\t\t\treturn;\n\t\t}\n\n\t\t// Ensure trust score & recommendation metrics are up-to-date before making a decision.\n\t\t// This is important because new data might have come in since the task was created.\n\t\tawait this.calculateUserTrustScore(userId, runtime);\n\n\t\tconst updatedComponent = await runtime.getComponent(\n\t\t\tuserId,\n\t\t\tTRUST_MARKETPLACE_COMPONENT_TYPE,\n\t\t\tuserProfileWorldId,\n\t\t\truntime.agentId,\n\t\t);\n\t\tif (!updatedComponent?.data) {\n\t\t\tlogger.error(\n\t\t\t\t`Task Worker: Profile for ${userId} disappeared after score recalc. Deleting task.`,\n\t\t\t);\n\t\t\tawait runtime.deleteTask(task.id as UUID);\n\t\t\treturn;\n\t\t}\n\t\tconst updatedUserProfile =\n\t\t\tupdatedComponent.data as TrustMarketplaceComponentData;\n\t\tconst finalTrustScore = updatedUserProfile.trustScore;\n\t\t// Refresh recommendation from potentially updated profile data\n\t\trecommendation =\n\t\t\tupdatedUserProfile.recommendations.find(\n\t\t\t\t(r) => r.id === recommendationId,\n\t\t\t) || recommendation;\n\n\t\t// Check cooldown again, as calculateUserTrustScore might take time\n\t\tconst now = Date.now();\n\t\tif (\n\t\t\tupdatedUserProfile.lastTradeDecisionMadeTimestamp &&\n\t\t\tnow - updatedUserProfile.lastTradeDecisionMadeTimestamp <\n\t\t\t\tthis.USER_TRADE_COOLDOWN_HOURS * 3600000\n\t\t) {\n\t\t\tlogger.info(\n\t\t\t\t`Task Worker: User ${userId} on trade cooldown (post-score update). Holding on rec ${recommendationId}.`,\n\t\t\t);\n\t\t\tif (recommendation) {\n\t\t\t\trecommendation.processedForTradeDecision = false; // Keep it false so it can be picked for a real decision later\n\t\t\t} else {\n\t\t\t\tlogger.error(\n\t\t\t\t\t\"Task Worker: Rec null after profile refresh in cooldown check.\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tawait runtime.updateComponent({\n\t\t\t\t...updatedComponent,\n\t\t\t\tdata: updatedUserProfile,\n\t\t\t});\n\t\t\tawait runtime.deleteTask(task.id as UUID);\n\t\t\treturn;\n\t\t}\n\n\t\tlet decisionMade = false;\n\t\tif (recommendation.recommendationType === \"BUY\") {\n\t\t\tif (finalTrustScore > this.POSITIVE_TRADE_THRESHOLD) {\n\t\t\t\tlogger.info(\n\t\t\t\t\t`Task Worker: SIMULATING BUY for rec ${recommendationId}. User ${userId}, Score: ${finalTrustScore.toFixed(2)}`,\n\t\t\t\t);\n\t\t\t\tupdatedUserProfile.lastTradeDecisionMadeTimestamp = now;\n\t\t\t\tdecisionMade = true;\n\t\t\t} else {\n\t\t\t\tlogger.info(\n\t\t\t\t\t`Task Worker: HOLDING on BUY rec ${recommendationId}. User ${userId}, Score: ${finalTrustScore.toFixed(2)}, Threshold: >${this.POSITIVE_TRADE_THRESHOLD})`,\n\t\t\t\t);\n\t\t\t}\n\t\t} else {\n\t\t\t// SELL type\n\t\t\tif (finalTrustScore > this.POSITIVE_TRADE_THRESHOLD) {\n\t\t\t\tlogger.info(\n\t\t\t\t\t`Task Worker: ACKNOWLEDGING VALID SELL/CRITICISM for rec ${recommendationId}. User ${userId}, Score: ${finalTrustScore.toFixed(2)}`,\n\t\t\t\t);\n\t\t\t\tupdatedUserProfile.lastTradeDecisionMadeTimestamp = now;\n\t\t\t\tdecisionMade = true;\n\t\t\t} else if (finalTrustScore < -this.NEUTRAL_MARGIN) {\n\t\t\t\tlogger.info(\n\t\t\t\t\t`Task Worker: IGNORING POTENTIAL FUD SELL/CRITICISM for rec ${recommendationId}. User ${userId}, Score: ${finalTrustScore.toFixed(2)} (Threshold for FUD: <${-this.NEUTRAL_MARGIN})`,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tlogger.info(\n\t\t\t\t\t`Task Worker: NOTING SELL/CRITICISM for rec ${recommendationId}. User ${userId}, Score: ${finalTrustScore.toFixed(2)}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst recToUpdate = updatedUserProfile.recommendations.find(\n\t\t\t(r) => r.id === recommendationId,\n\t\t);\n\t\tif (recToUpdate) {\n\t\t\trecToUpdate.processedForTradeDecision = true; // Now it's fully processed for a trade decision cycle\n\t\t} else {\n\t\t\tlogger.error(\n\t\t\t\t`[CommunityInvestorService] Task Worker: Could not find rec ${recommendationId} in updated profile to mark as processed.`,\n\t\t\t);\n\t\t}\n\n\t\tawait runtime.updateComponent({\n\t\t\t...updatedComponent,\n\t\t\tdata: updatedUserProfile,\n\t\t});\n\t\tawait runtime.deleteTask(task.id as UUID);\n\t\tlogger.info(\n\t\t\t`Task Worker: Finished trade decision for rec ${recommendationId}. User: ${userId}. Made Sim Trade: ${decisionMade}`,\n\t\t);\n\t}\n\n\tprivate registerTaskWorkers(runtime: IAgentRuntime): void {\n\t\truntime.registerTaskWorker({\n\t\t\tname: \"PROCESS_TRADE_DECISION\",\n\t\t\texecute: async (_runtime, options, task) => {\n\t\t\t\tawait this.executeProcessTradeDecision(\n\t\t\t\t\toptions as { recommendationId: UUID; userId: UUID },\n\t\t\t\t\ttask,\n\t\t\t\t);\n\t\t\t\treturn undefined;\n\t\t\t},\n\t\t});\n\t\tlogger.info(\n\t\t\t\"[CommunityInvestorService] Registered PROCESS_TRADE_DECISION task worker.\",\n\t\t);\n\t}\n\n\tasync getLeaderboardData(\n\t\truntime: IAgentRuntime,\n\t): Promise<LeaderboardEntry[]> {\n\t\tlogger.info(\"[CommunityInvestorService] getLeaderboardData called\");\n\t\tconst leaderboardEntries: LeaderboardEntry[] = [];\n\t\t// Use the consistent componentWorldId for fetching profiles\n\t\tconst worldIdForComponents = this.componentWorldId;\n\n\t\t// Use the user registry to get all users who have made recommendations\n\t\tlogger.info(\n\t\t\t`[CommunityInvestorService] Preparing leaderboard from world ${worldIdForComponents}. Checking ${this.userRegistry.size} registered users from userRegistry: [${Array.from(this.userRegistry).join(\", \")}]`,\n\t\t);\n\n\t\tfor (const userId of this.userRegistry) {\n\t\t\tlogger.debug(\n\t\t\t\t`[CommunityInvestorService] Leaderboard: Processing registered user ${userId} from world ${worldIdForComponents}`,\n\t\t\t);\n\t\t\ttry {\n\t\t\t\tconst component = await runtime.getComponent(\n\t\t\t\t\tuserId,\n\t\t\t\t\tTRUST_MARKETPLACE_COMPONENT_TYPE,\n\t\t\t\t\tworldIdForComponents, // Use consistent worldId\n\t\t\t\t\truntime.agentId,\n\t\t\t\t);\n\n\t\t\t\tif (component?.data) {\n\t\t\t\t\tconst profileData = component.data as TrustMarketplaceComponentData;\n\t\t\t\t\tconst entityDetails = await runtime.getEntityById(component.entityId);\n\n\t\t\t\t\tconst recommendations = Array.isArray(profileData.recommendations)\n\t\t\t\t\t\t? profileData.recommendations\n\t\t\t\t\t\t: [];\n\n\t\t\t\t\tleaderboardEntries.push({\n\t\t\t\t\t\tuserId: component.entityId,\n\t\t\t\t\t\tusername:\n\t\t\t\t\t\t\tentityDetails?.names?.[0] || component.entityId.toString(),\n\t\t\t\t\t\ttrustScore: profileData.trustScore || 0,\n\t\t\t\t\t\trecommendations: recommendations,\n\t\t\t\t\t});\n\n\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t`[CommunityInvestorService] Added user ${userId} to leaderboard with score ${profileData.trustScore}`,\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tlogger.debug(\n\t\t\t\t\t\t`[CommunityInvestorService] Leaderboard: No profile component found for registered user ${userId}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(\n\t\t\t\t\t`[CommunityInvestorService] Leaderboard: Error fetching profile component for user ${userId}:`,\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tlogger.info(\n\t\t\t`[CommunityInvestorService] Leaderboard: Found ${leaderboardEntries.length} users with profiles to include.`,\n\t\t);\n\n\t\t// Sort by trust score and add ranks\n\t\tleaderboardEntries.sort((a, b) => b.trustScore - a.trustScore);\n\t\tconst rankedLeaderboard = leaderboardEntries.map((entry, index) => ({\n\t\t\t...entry,\n\t\t\trank: index + 1,\n\t\t}));\n\t\tlogger.info(\n\t\t\t`[CommunityInvestorService] Leaderboard generated with ${rankedLeaderboard.length} entries.`,\n\t\t);\n\t\treturn rankedLeaderboard;\n\t}\n\n\t// Add this method to register a user when they make a recommendation\n\tprivate registerUser(userId: UUID): void {\n\t\tconst originalSize = this.userRegistry.size;\n\t\tthis.userRegistry.add(userId);\n\t\tif (this.userRegistry.size > originalSize) {\n\t\t\tlogger.info(\n\t\t\t\t`[CommunityInvestorService] User ${userId} ADDED to registry. New size: ${this.userRegistry.size}. Registry now: [${Array.from(this.userRegistry).join(\", \")}]`,\n\t\t\t);\n\t\t} else {\n\t\t\tlogger.debug(\n\t\t\t\t`[CommunityInvestorService] User ${userId} already in registry. Size: ${this.userRegistry.size}`,\n\t\t\t);\n\t\t}\n\t\t// Persist this to a cache using a key namespaced by the plugin's world ID\n\t\tconst registryCacheKey = `community-investor:user-registry:${this.componentWorldId}`;\n\t\tthis.runtime\n\t\t\t.setCache(registryCacheKey, Array.from(this.userRegistry))\n\t\t\t.then(() =>\n\t\t\t\tlogger.debug(\n\t\t\t\t\t`[CommunityInvestorService] User registry cache updated for user ${userId} at key ${registryCacheKey}.`,\n\t\t\t\t),\n\t\t\t)\n\t\t\t.catch((err) =>\n\t\t\t\tlogger.error(\n\t\t\t\t\t`[CommunityInvestorService] FAILED to update user registry cache for ${userId} at key ${registryCacheKey}:`,\n\t\t\t\t\terr,\n\t\t\t\t),\n\t\t\t);\n\t}\n\n\t// Load user registry on initialization\n\tprivate async loadUserRegistry(): Promise<void> {\n\t\tconst registryCacheKey = `community-investor:user-registry:${this.componentWorldId}`;\n\t\ttry {\n\t\t\tconst cached = await this.runtime.getCache<UUID[]>(registryCacheKey);\n\t\t\tif (cached && Array.isArray(cached)) {\n\t\t\t\tthis.userRegistry = new Set(cached);\n\t\t\t\tlogger.info(\n\t\t\t\t\t`[CommunityInvestorService] Loaded ${this.userRegistry.size} users from registry cache at key ${registryCacheKey}. Users: [${Array.from(this.userRegistry).join(\", \")}]`,\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tlogger.info(\n\t\t\t\t\t`[CommunityInvestorService] No user registry found in cache at key ${registryCacheKey}, starting fresh.`,\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tlogger.warn(\n\t\t\t\t`[CommunityInvestorService] Failed to load user registry from cache at key ${registryCacheKey}:`,\n\t\t\t\terror,\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate async ensurePluginComponentContext(): Promise<void> {\n\t\ttry {\n\t\t\tawait this.runtime.ensureWorldExists({\n\t\t\t\tid: this.componentWorldId,\n\t\t\t\tname: `Social Alpha Global World (Agent: ${this.runtime.agentId})`,\n\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\tmetadata: {\n\t\t\t\t\tplugin_managed: true,\n\t\t\t\t\tdescription: \"World context for CommunityInvestor plugin components\",\n\t\t\t\t},\n\t\t\t});\n\t\t\tlogger.info(\n\t\t\t\t`[CommunityInvestorService] Ensured plugin component world ${this.componentWorldId} exists.`,\n\t\t\t);\n\n\t\t\tawait this.runtime.ensureRoomExists({\n\t\t\t\tid: this.componentRoomId,\n\t\t\t\tname: `Social Alpha Global Room (Agent: ${this.runtime.agentId})`,\n\t\t\t\tworldId: this.componentWorldId,\n\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\tchannelId: TRUST_LEADERBOARD_WORLD_SEED,\n\t\t\t\tsource: \"plugin_internal\",\n\t\t\t\ttype: ChannelType.API, // Use API as fallback channel type\n\t\t\t\tmetadata: {\n\t\t\t\t\tplugin_managed: true,\n\t\t\t\t\tdescription: \"Room context for CommunityInvestor plugin components\",\n\t\t\t\t},\n\t\t\t});\n\t\t\tlogger.info(\n\t\t\t\t`[CommunityInvestorService] Ensured plugin component room ${this.componentRoomId} in world ${this.componentWorldId} exists.`,\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tlogger.error(\n\t\t\t\t`[CommunityInvestorService] FAILED to ensure plugin component world/room context (ID: ${this.componentWorldId}):`,\n\t\t\t\terror,\n\t\t\t);\n\t\t\t// Depending on the severity, you might want to throw this error or handle it\n\t\t}\n\t}\n\n\t// ===================== NEW CORE PROCESSING LOGIC =====================\n\n\t/**\n\t * Processes a batch of historical messages, intended to be called from a script.\n\t */\n\tasync processHistoricalData(batch: {\n\t\tfileId: string;\n\t\tbatchIndex: number;\n\t\tmessages: HistoricalBatchMessage[];\n\t\tuserMap: Record<string, string>;\n\t}): Promise<NormalizedExtractedSignal[]> {\n\t\tlogger.info(\n\t\t\t`[Service] Processing historical batch ${batch.fileId}_${batch.batchIndex}`,\n\t\t);\n\t\tconst { messages, userMap } = batch;\n\n\t\tconst contextText = \"\"; // Historical data processing might not have sequential context in the same way\n\t\tconst messagesText = messages\n\t\t\t.map(\n\t\t\t\t(msg, idx) => `[${idx}] ${userMap[msg.uid] || msg.uid}: ${msg.content}`,\n\t\t\t)\n\t\t\t.join(\"\\n\");\n\n\t\tconst { systemPrompt, userPrompt } = this.buildExtractionPrompts(\n\t\t\tcontextText,\n\t\t\tmessagesText,\n\t\t\tmessages.length,\n\t\t\t\"Multiple Users\",\n\t\t);\n\n\t\ttry {\n\t\t\tconst response = await this.runtime.useModel(ModelType.TEXT_LARGE, {\n\t\t\t\tprompt: `${systemPrompt}\\n${userPrompt}`,\n\t\t\t});\n\n\t\t\tconst parsed = parseRecommendationExtraction(response);\n\n\t\t\tif (!parsed?.recommendations || parsed.recommendations.length === 0) {\n\t\t\t\tlogger.debug(\n\t\t\t\t\t`[Service] No recommendations extracted from historical batch ${batch.fileId}_${batch.batchIndex}.`,\n\t\t\t\t);\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tconst callsByUserId = new Map<\n\t\t\t\tUUID,\n\t\t\t\t{\n\t\t\t\t\tmessages: HistoricalBatchMessage[];\n\t\t\t\t\trecommendations: NormalizedExtractedSignal[];\n\t\t\t\t}\n\t\t\t>();\n\n\t\t\tfor (const rec of parsed.recommendations) {\n\t\t\t\tconst message = messages[rec.messageIndex];\n\t\t\t\tif (!message) continue;\n\t\t\t\tconst userId = asUUID(createUniqueUuid(this.runtime, message.uid));\n\t\t\t\tif (!callsByUserId.has(userId)) {\n\t\t\t\t\tcallsByUserId.set(userId, { messages: [], recommendations: [] });\n\t\t\t\t}\n\t\t\t\tcallsByUserId.get(userId)?.messages.push(message);\n\t\t\t\tcallsByUserId.get(userId)?.recommendations.push(rec);\n\t\t\t}\n\n\t\t\tfor (const [userId, data] of callsByUserId.entries()) {\n\t\t\t\tawait this.updateProfileWithRecommendations(\n\t\t\t\t\tuserId,\n\t\t\t\t\tdata.messages,\n\t\t\t\t\tdata.recommendations,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn parsed.recommendations;\n\t\t} catch (error) {\n\t\t\tlogger.error(\n\t\t\t\t`[Service] Error processing historical batch ${batch.fileId}_${batch.batchIndex}:`,\n\t\t\t\terror,\n\t\t\t);\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Main entry point for processing a single, real-time message from the event handler.\n\t */\n\tasync processIncomingMessage(message: {\n\t\tid?: UUID;\n\t\tuserId: UUID;\n\t\troomId: UUID;\n\t\ttext: string;\n\t\ttimestamp: number;\n\t\tusername?: string;\n\t}): Promise<void> {\n\t\tconst { userId, roomId, text, timestamp, id, username } = message;\n\t\tconst messageId =\n\t\t\tid || asUUID(createUniqueUuid(this.runtime, `${userId}-${timestamp}`));\n\n\t\t// 1. Get context\n\t\tconst recentMessages = await this.runtime.getMemories({\n\t\t\ttableName: \"messages\",\n\t\t\troomId: roomId,\n\t\t\tcount: 10, // Fetch recent messages for context\n\t\t\tunique: false,\n\t\t});\n\n\t\tconst contextText = recentMessages\n\t\t\t.map(\n\t\t\t\t(msg) =>\n\t\t\t\t\t`${msg.content?.name || msg.entityId.toString()}: ${msg.content?.text || \"\"}`,\n\t\t\t)\n\t\t\t.join(\"\\n\");\n\t\tconst messagesText = `[0] ${username || userId}: ${text}`;\n\n\t\t// 2. Call LLM for analysis\n\t\tconst { systemPrompt, userPrompt } = this.buildExtractionPrompts(\n\t\t\tcontextText,\n\t\t\tmessagesText,\n\t\t\t1,\n\t\t\tusername || userId.toString(),\n\t\t);\n\n\t\ttry {\n\t\t\tconst response = await this.runtime.useModel(ModelType.TEXT_LARGE, {\n\t\t\t\tprompt: `${systemPrompt}\\n${userPrompt}`,\n\t\t\t});\n\n\t\t\tconst parsed = parseRecommendationExtraction(response);\n\n\t\t\tif (!parsed?.recommendations || parsed.recommendations.length === 0) {\n\t\t\t\tlogger.debug(\n\t\t\t\t\t`[Service] No recommendations extracted from message ${messageId}.`,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst messageForUpdate = {\n\t\t\t\tid: messageId,\n\t\t\t\tcontent: text,\n\t\t\t\tuid: userId,\n\t\t\t\tts: new Date(timestamp).toISOString(),\n\t\t\t};\n\t\t\t// 3. Process the extracted recommendations and update user profile\n\t\t\tawait this.updateProfileWithRecommendations(\n\t\t\t\tuserId,\n\t\t\t\t[messageForUpdate],\n\t\t\t\tparsed.recommendations,\n\t\t\t);\n\t\t} catch (e) {\n\t\t\tlogger.error(\n\t\t\t\t`[Service] Error processing incoming message for user ${userId}:`,\n\t\t\t\te,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Builds the system and user prompts for the recommendation extraction LLM call.\n\t */\n\tprivate buildExtractionPrompts(\n\t\tcontextText: string,\n\t\tmessagesText: string,\n\t\tbatchSize: number,\n\t\t_senderName: string,\n\t) {\n\t\tconst systemPrompt = `Extract crypto trading signals, calls, recommendations, and sentiment from Discord messages.\n\n🎯 WHAT COUNTS AS A TRADING SIGNAL:\n• Direct trading advice: \"buy X\", \"sell Y\", \"hold Z\"\n• Token mentions with $ symbol: $SOL, $PEPE, $DOGE, etc.\n• Contract addresses posted for token discovery\n• Price predictions: \"X going to moon\", \"Y will dump\"\n• Market sentiment: \"bullish on X\", \"bearish on Y\"\n• Technical analysis mentions\n• FUD or criticism about specific tokens/projects\n• Trading intent: \"I'm buying X\", \"waiting for dip\"\n• Token performance discussion\n\n🚫 WHAT TO EXCLUDE:\n• Rick bot automated messages (User ID: 1081815963990761542)\n• Generic DAO/protocol discussion without specific tokens\n• Users with \"*bot\" in username\n• Messages about \"mintable\" (it's a property, not a token)\n• General crypto news without specific token focus\n\n🔤 CRYPTO SLANG DICTIONARY:\n• fsh = full stack hitler (derogatory)\n• dca = dollar cost averaging\n• ath = all time high\n• atl = all time low\n• mcap = market cap\n• ser = sir\n• ngmi = not gonna make it\n• wagmi = we're all gonna make it\n• wen = when\n• gm = good morning\n• ser = sir\n\n⚠️ CRITICAL OUTPUT REQUIREMENTS:\n- Respond with JSON only.\n- EXACTLY ${batchSize} recommendations entries (messageIndex 0 to ${batchSize - 1})\n- Every entry MUST include ALL required fields\n\nREQUIRED JSON SHAPE:\n{\"recommendations\":[{\"messageIndex\":0,\"isCall\":true,\"tokenMentioned\":\"SOL\",\"nameMentioned\":\"\",\"caMentioned\":\"\",\"chain\":\"solana\",\"sentiment\":\"positive\",\"conviction\":\"medium\",\"llmReasoning\":\"User mentioned buying $SOL with medium confidence\"}]}\n\nFIELD REQUIREMENTS:\n• messageIndex: 0 to ${batchSize - 1}\n• isCall: true/false\n• tokenMentioned: ticker without $ (or \"\" if none)\n• nameMentioned: full token name (or \"\" if none) \n• caMentioned: contract address (or \"\" if none)\n• chain: \"solana\", \"ethereum\", \"bitcoin\", \"base\", \"unknown\"\n• sentiment: \"positive\", \"negative\", \"neutral\"\n• conviction: \"high\", \"medium\", \"low\", \"neutral\"\n• llmReasoning: 1-2 sentence explanation\n\nVALIDATION RULES:\n• If isCall=true: At least ONE of tokenMentioned, nameMentioned, or caMentioned must be non-empty\n• If isCall=false: ALL three can be empty\n• sentiment \"bullish\"→\"positive\", \"bearish\"→\"negative\"\n• Be VERY generous with extraction - include borderline cases`;\n\n\t\tconst userPrompt = `\nRECENT CONTEXT:\n${contextText}\n\n🔍 ANALYZE THESE ${batchSize} MESSAGES FOR TRADING SIGNALS:\n${messagesText}\n\n📋 EXTRACTION RULES:\n1. Look for contract addresses: long alphanumeric strings (32-44 chars for Solana, 0x+40 chars for ETH/Base)\n2. Extract ANY token mentions: $BTC, $ETH, $SOL, $PEPE, etc.\n3. Capture trading sentiment and conviction level\n4. Include FUD, criticism, or warnings about tokens\n5. Be generous - include subtle references\n6. MUST return EXACTLY ${batchSize} results\n\nExamples that SHOULD be extracted:\n- \"the dev is a CA spammer so we dont know where it could go from here\" → negative sentiment about a project\n- \"Dqyrmg6y7QFhsbCgpkNwnp8wFMs81z3ToPACYAipump is this legit?\" → contract address inquiry\n- \"I will wait for retrace to enter\" → trading intent\n- \"$SOL looking good\" → positive sentiment\n- \"most ai stuff are getting a dump\" → negative sentiment on AI tokens\n- Contract addresses without context → neutral discovery\n\nRESPOND WITH JSON CONTAINING EXACTLY ${batchSize} RECOMMENDATION ENTRIES:`;\n\t\treturn { systemPrompt, userPrompt };\n\t}\n\n\t/**\n\t * Updates a user's profile with new recommendations extracted from a message batch.\n\t */\n\tprivate async updateProfileWithRecommendations(\n\t\tuserId: UUID,\n\t\tmessagesInBatch: HistoricalBatchMessage[], // The original messages that were analyzed\n\t\trecommendationsFromLlm: NormalizedExtractedSignal[], // The raw recommendations from the LLM\n\t) {\n\t\tconst component = await this.runtime.getComponent(\n\t\t\tuserId,\n\t\t\tTRUST_MARKETPLACE_COMPONENT_TYPE,\n\t\t\tthis.componentWorldId,\n\t\t\tthis.runtime.agentId,\n\t\t);\n\n\t\tlet userProfile: UserTrustProfile;\n\t\tif (!component?.data) {\n\t\t\tuserProfile = {\n\t\t\t\tversion: \"1.0.0\",\n\t\t\t\tuserId,\n\t\t\t\ttrustScore: 0,\n\t\t\t\tlastTrustScoreCalculationTimestamp: Date.now(),\n\t\t\t\trecommendations: [],\n\t\t\t};\n\t\t} else {\n\t\t\tuserProfile = component.data as TrustMarketplaceComponentData;\n\t\t\tif (!Array.isArray(userProfile.recommendations))\n\t\t\t\tuserProfile.recommendations = [];\n\t\t}\n\n\t\tlet profileUpdated = false;\n\t\tfor (const rec of recommendationsFromLlm) {\n\t\t\t// Skip if not a call or no token information provided\n\t\t\tif (!rec.isCall || !rec.sentiment || rec.sentiment === \"neutral\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst sentiment = rec.sentiment;\n\n\t\t\t// Check if we have at least one token identifier\n\t\t\tconst tokenMentioned =\n\t\t\t\trec.tokenMentioned?.trim() && rec.tokenMentioned !== \"N/A\"\n\t\t\t\t\t? rec.tokenMentioned\n\t\t\t\t\t: undefined;\n\t\t\tconst nameMentioned = rec.nameMentioned?.trim()\n\t\t\t\t? rec.nameMentioned\n\t\t\t\t: undefined;\n\t\t\tconst caMentioned = rec.caMentioned?.trim() ? rec.caMentioned : undefined;\n\n\t\t\tif (!tokenMentioned && !nameMentioned && !caMentioned) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst originalMessage = messagesInBatch[rec.messageIndex];\n\t\t\tif (!originalMessage) continue;\n\n\t\t\t// Try to resolve token, preferring contract address, then ticker, then name\n\t\t\tlet resolvedToken: {\n\t\t\t\taddress: string;\n\t\t\t\tchain: SupportedChain;\n\t\t\t\tticker?: string;\n\t\t\t} | null = null;\n\n\t\t\tif (caMentioned) {\n\t\t\t\t// For contract addresses, use them directly\n\t\t\t\tresolvedToken = {\n\t\t\t\t\taddress: caMentioned,\n\t\t\t\t\tchain: (rec.chain as SupportedChain) || SupportedChain.SOLANA,\n\t\t\t\t\tticker: tokenMentioned || nameMentioned || caMentioned.slice(0, 8),\n\t\t\t\t};\n\t\t\t} else if (tokenMentioned) {\n\t\t\t\tresolvedToken = await this.resolveTicker(\n\t\t\t\t\ttokenMentioned,\n\t\t\t\t\t(rec.chain as SupportedChain) || SupportedChain.SOLANA,\n\t\t\t\t);\n\t\t\t} else if (nameMentioned) {\n\t\t\t\tresolvedToken = await this.resolveTicker(\n\t\t\t\t\tnameMentioned,\n\t\t\t\t\t(rec.chain as SupportedChain) || SupportedChain.SOLANA,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (!resolvedToken) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`[Service] Could not resolve token for: \"${tokenMentioned || nameMentioned || caMentioned}\". Skipping.`,\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst newRecommendation: Recommendation = {\n\t\t\t\tid: asUUID(uuidv4()),\n\t\t\t\tuserId: userId,\n\t\t\t\tmessageId: originalMessage.id,\n\t\t\t\ttimestamp: new Date(originalMessage.ts).getTime(),\n\t\t\t\ttokenTicker: resolvedToken.ticker,\n\t\t\t\ttokenAddress: resolvedToken.address,\n\t\t\t\tchain: resolvedToken.chain,\n\t\t\t\trecommendationType: sentiment === \"positive\" ? \"BUY\" : \"SELL\",\n\t\t\t\tconviction: rec.conviction as Conviction,\n\t\t\t\trawMessageQuote: originalMessage.content,\n\t\t\t\tpriceAtRecommendation: 0,\n\t\t\t\tprocessedForTradeDecision: false,\n\t\t\t};\n\n\t\t\tuserProfile.recommendations.unshift(newRecommendation);\n\t\t\tprofileUpdated = true;\n\n\t\t\tlogger.info(\n\t\t\t\t`[Service] Added ${sentiment.toUpperCase()} recommendation for ${resolvedToken.ticker} from user ${userId}`,\n\t\t\t);\n\n\t\t\tawait this.runtime.createTask({\n\t\t\t\tname: \"PROCESS_TRADE_DECISION\",\n\t\t\t\tdescription: `Process trade decision for rec ${newRecommendation.id}`,\n\t\t\t\tmetadata: { recommendationId: newRecommendation.id, userId },\n\t\t\t\ttags: [\"socialAlpha\", \"tradeDecision\"],\n\t\t\t\troomId: this.componentRoomId,\n\t\t\t\tworldId: this.componentWorldId,\n\t\t\t\tentityId: userId,\n\t\t\t});\n\t\t}\n\n\t\tif (profileUpdated) {\n\t\t\tif (component) {\n\t\t\t\tawait this.runtime.updateComponent({\n\t\t\t\t\t...component,\n\t\t\t\t\tdata: userProfile,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tconst newComponentId = asUUID(\n\t\t\t\t\tcreateUniqueUuid(this.runtime, userId.toString()),\n\t\t\t\t);\n\t\t\t\tawait this.runtime.createComponent({\n\t\t\t\t\tid: newComponentId,\n\t\t\t\t\tentityId: userId,\n\t\t\t\t\tagentId: this.runtime.agentId,\n\t\t\t\t\tworldId: this.componentWorldId,\n\t\t\t\t\troomId: this.componentRoomId,\n\t\t\t\t\tsourceEntityId: this.runtime.agentId,\n\t\t\t\t\ttype: TRUST_MARKETPLACE_COMPONENT_TYPE,\n\t\t\t\t\tcreatedAt: Date.now(),\n\t\t\t\t\tdata: userProfile,\n\t\t\t\t});\n\t\t\t}\n\t\t\tawait this.calculateUserTrustScore(userId, this.runtime);\n\t\t}\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { TrustScoreResult } from "./trustScoreOptimizer";
|
|
2
|
+
export interface BalancedTrustScoreParams {
|
|
3
|
+
profitWeight: number;
|
|
4
|
+
winRateWeight: number;
|
|
5
|
+
sharpeWeight: number;
|
|
6
|
+
alphaWeight: number;
|
|
7
|
+
consistencyWeight: number;
|
|
8
|
+
qualityWeight: number;
|
|
9
|
+
normalVolumeThreshold: number;
|
|
10
|
+
highVolumeThreshold: number;
|
|
11
|
+
extremeVolumeThreshold: number;
|
|
12
|
+
volumeToleranceByArchetype: Record<string, number>;
|
|
13
|
+
}
|
|
14
|
+
export declare class BalancedTrustScoreCalculator {
|
|
15
|
+
private params;
|
|
16
|
+
/**
|
|
17
|
+
* Calculate balanced trust score
|
|
18
|
+
*/
|
|
19
|
+
calculateBalancedTrustScore(metrics: TrustScoreResult["metrics"], archetype: string, rugPromotions: number, goodCalls: number, totalCalls: number): number;
|
|
20
|
+
/**
|
|
21
|
+
* Archetype base scores with good spread
|
|
22
|
+
*/
|
|
23
|
+
private getArchetypeBase;
|
|
24
|
+
/**
|
|
25
|
+
* Profit component with better scaling
|
|
26
|
+
*/
|
|
27
|
+
private calculateProfitComponent;
|
|
28
|
+
/**
|
|
29
|
+
* Win rate component with smooth curve
|
|
30
|
+
*/
|
|
31
|
+
private calculateWinRateComponent;
|
|
32
|
+
/**
|
|
33
|
+
* Sharpe ratio component
|
|
34
|
+
*/
|
|
35
|
+
private calculateSharpeComponent;
|
|
36
|
+
/**
|
|
37
|
+
* Alpha component - fixed to handle negative values better
|
|
38
|
+
*/
|
|
39
|
+
private calculateAlphaComponent;
|
|
40
|
+
/**
|
|
41
|
+
* Quality component based on call quality
|
|
42
|
+
*/
|
|
43
|
+
private calculateQualityComponent;
|
|
44
|
+
/**
|
|
45
|
+
* Volume adjustment - multiplicative factor instead of penalty
|
|
46
|
+
*/
|
|
47
|
+
private calculateVolumeAdjustment;
|
|
48
|
+
/**
|
|
49
|
+
* Archetype performance scaling
|
|
50
|
+
*/
|
|
51
|
+
private getArchetypeScaling;
|
|
52
|
+
/**
|
|
53
|
+
* Set custom parameters
|
|
54
|
+
*/
|
|
55
|
+
setParameters(params: Partial<BalancedTrustScoreParams>): void;
|
|
56
|
+
/**
|
|
57
|
+
* Get current parameters
|
|
58
|
+
*/
|
|
59
|
+
getParameters(): BalancedTrustScoreParams;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=balancedTrustScoreCalculator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"balancedTrustScoreCalculator.d.ts","sourceRoot":"","sources":["../../src/services/balancedTrustScoreCalculator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D,MAAM,WAAW,wBAAwB;IAExC,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IAGtB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,sBAAsB,EAAE,MAAM,CAAC;IAG/B,0BAA0B,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnD;AAED,qBAAa,4BAA4B;IACxC,OAAO,CAAC,MAAM,CAqBZ;IAEF;;OAEG;IACH,2BAA2B,CAC1B,OAAO,EAAE,gBAAgB,CAAC,SAAS,CAAC,EACpC,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GAChB,MAAM;IAsDT;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAexB;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAchC;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAejC;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAchC;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAc/B;;OAEG;IACH,OAAO,CAAC,yBAAyB;IA0BjC;;OAEG;IACH,OAAO,CAAC,yBAAyB;IA+BjC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAe3B;;OAEG;IACH,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,wBAAwB,CAAC,GAAG,IAAI;IAI9D;;OAEG;IACH,aAAa,IAAI,wBAAwB;CAGzC"}
|