@elizaos/plugin-social-alpha 2.0.3-beta.5 → 2.0.3-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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,771 @@
|
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { v4 as uuidv4 } from "uuid";
|
|
4
|
+
import { Conviction, SupportedChain } from "../types.js";
|
|
5
|
+
import {
|
|
6
|
+
TokenSimulationService
|
|
7
|
+
} from "./tokenSimulationService.js";
|
|
8
|
+
var TokenScenario = /* @__PURE__ */ ((TokenScenario2) => {
|
|
9
|
+
TokenScenario2["RUG_PULL_FAST"] = "rug_fast";
|
|
10
|
+
TokenScenario2["RUG_PULL_SLOW"] = "rug_slow";
|
|
11
|
+
TokenScenario2["SCAM_TOKEN"] = "scam";
|
|
12
|
+
TokenScenario2["RUNNER_MOON"] = "runner_moon";
|
|
13
|
+
TokenScenario2["RUNNER_STEADY"] = "runner_steady";
|
|
14
|
+
TokenScenario2["SUCCESSFUL"] = "successful";
|
|
15
|
+
TokenScenario2["MEDIOCRE"] = "mediocre";
|
|
16
|
+
TokenScenario2["STAGNANT"] = "stagnant";
|
|
17
|
+
TokenScenario2["BLUE_CHIP"] = "bluechip";
|
|
18
|
+
TokenScenario2["PUMP_AND_DUMP"] = "pump_dump";
|
|
19
|
+
TokenScenario2["SLOW_BLEED"] = "slow_bleed";
|
|
20
|
+
return TokenScenario2;
|
|
21
|
+
})(TokenScenario || {});
|
|
22
|
+
class SimulationRunner {
|
|
23
|
+
tokenService;
|
|
24
|
+
constructor() {
|
|
25
|
+
this.tokenService = new TokenSimulationService();
|
|
26
|
+
}
|
|
27
|
+
async runSimulation(config) {
|
|
28
|
+
console.log("\u{1F680} Starting comprehensive market simulation...");
|
|
29
|
+
const calls = [];
|
|
30
|
+
const tokens = /* @__PURE__ */ new Map();
|
|
31
|
+
const priceHistory = /* @__PURE__ */ new Map();
|
|
32
|
+
const actorPerformance = /* @__PURE__ */ new Map();
|
|
33
|
+
for (const actor of config.actors) {
|
|
34
|
+
const _simulatedActor = {
|
|
35
|
+
id: actor.id,
|
|
36
|
+
username: actor.username,
|
|
37
|
+
archetype: actor.archetype,
|
|
38
|
+
trustScore: actor.expectedTrustScore,
|
|
39
|
+
callHistory: [],
|
|
40
|
+
preferences: {
|
|
41
|
+
favoriteTokenTypes: this.mapScenarioToTypes(actor.tokenPreferences),
|
|
42
|
+
callFrequency: actor.callFrequency,
|
|
43
|
+
timingBias: actor.timingBias
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
actorPerformance.set(actor.id, {
|
|
47
|
+
totalCalls: 0,
|
|
48
|
+
profitableCalls: 0,
|
|
49
|
+
totalProfit: 0,
|
|
50
|
+
averageProfit: 0
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
const generatedTokens = this.generateTokens(config);
|
|
54
|
+
for (const token of generatedTokens) {
|
|
55
|
+
tokens.set(token.address, token);
|
|
56
|
+
priceHistory.set(token.address, []);
|
|
57
|
+
}
|
|
58
|
+
let currentTime = new Date(config.startTime);
|
|
59
|
+
const endTime = new Date(config.endTime);
|
|
60
|
+
let stepCount = 0;
|
|
61
|
+
while (currentTime <= endTime) {
|
|
62
|
+
stepCount++;
|
|
63
|
+
for (const [address, token] of tokens) {
|
|
64
|
+
const timeSinceLaunch = (currentTime.getTime() - token.launchTime.getTime()) / (1e3 * 60 * 60);
|
|
65
|
+
if (timeSinceLaunch >= 0) {
|
|
66
|
+
const price = this.calculateTokenPrice(token, timeSinceLaunch);
|
|
67
|
+
priceHistory.get(address)?.push({
|
|
68
|
+
timestamp: currentTime,
|
|
69
|
+
price: price.price,
|
|
70
|
+
volume: price.volume,
|
|
71
|
+
liquidity: price.liquidity,
|
|
72
|
+
marketCap: price.marketCap
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const stepCalls = this.generateCallsForTimeStep(
|
|
77
|
+
currentTime,
|
|
78
|
+
tokens,
|
|
79
|
+
priceHistory,
|
|
80
|
+
config
|
|
81
|
+
);
|
|
82
|
+
calls.push(...stepCalls);
|
|
83
|
+
currentTime = new Date(
|
|
84
|
+
currentTime.getTime() + config.timeStepMinutes * 60 * 1e3
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
console.log(
|
|
88
|
+
`\u2705 Simulation complete: ${stepCount} time steps, ${calls.length} calls generated`
|
|
89
|
+
);
|
|
90
|
+
await this.calculateActualProfits(calls, tokens, priceHistory);
|
|
91
|
+
for (const call of calls) {
|
|
92
|
+
const perf = actorPerformance.get(call.userId);
|
|
93
|
+
if (!perf) continue;
|
|
94
|
+
perf.totalCalls++;
|
|
95
|
+
if (call.simulationMetadata.actualProfit && call.simulationMetadata.actualProfit > 0) {
|
|
96
|
+
perf.profitableCalls++;
|
|
97
|
+
}
|
|
98
|
+
perf.totalProfit += call.simulationMetadata.actualProfit || 0;
|
|
99
|
+
}
|
|
100
|
+
for (const [_actorId, perf] of actorPerformance) {
|
|
101
|
+
perf.averageProfit = perf.totalCalls > 0 ? perf.totalProfit / perf.totalCalls : 0;
|
|
102
|
+
}
|
|
103
|
+
const result = {
|
|
104
|
+
calls,
|
|
105
|
+
tokens,
|
|
106
|
+
priceHistory,
|
|
107
|
+
actorPerformance
|
|
108
|
+
};
|
|
109
|
+
if (config.cacheResults) {
|
|
110
|
+
await this.cacheResults(result, config);
|
|
111
|
+
}
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
114
|
+
calculateTokenPrice(token, hoursSinceLaunch) {
|
|
115
|
+
let price = token.initialPrice;
|
|
116
|
+
if (token.priceTrajectory) {
|
|
117
|
+
const step = Math.floor(hoursSinceLaunch / 24);
|
|
118
|
+
price = token.priceTrajectory(step);
|
|
119
|
+
}
|
|
120
|
+
const priceRatio = price / token.initialPrice;
|
|
121
|
+
const marketCap = token.initialMarketCap * priceRatio;
|
|
122
|
+
const liquidity = token.initialLiquidity * Math.sqrt(priceRatio);
|
|
123
|
+
const volume = marketCap * 0.1 * (1 + Math.random() * 0.5);
|
|
124
|
+
return {
|
|
125
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
126
|
+
price,
|
|
127
|
+
volume,
|
|
128
|
+
liquidity,
|
|
129
|
+
marketCap
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
mapScenarioToTypes(scenarios) {
|
|
133
|
+
const mapping = {
|
|
134
|
+
["rug_fast" /* RUG_PULL_FAST */]: "rug",
|
|
135
|
+
["rug_slow" /* RUG_PULL_SLOW */]: "rug",
|
|
136
|
+
["scam" /* SCAM_TOKEN */]: "scam",
|
|
137
|
+
["runner_moon" /* RUNNER_MOON */]: "runner",
|
|
138
|
+
["runner_steady" /* RUNNER_STEADY */]: "runner",
|
|
139
|
+
["successful" /* SUCCESSFUL */]: "successful",
|
|
140
|
+
["mediocre" /* MEDIOCRE */]: "mediocre",
|
|
141
|
+
["stagnant" /* STAGNANT */]: "stagnant",
|
|
142
|
+
["bluechip" /* BLUE_CHIP */]: "bluechip",
|
|
143
|
+
["pump_dump" /* PUMP_AND_DUMP */]: "pump_dump",
|
|
144
|
+
["slow_bleed" /* SLOW_BLEED */]: "slow_bleed"
|
|
145
|
+
};
|
|
146
|
+
return scenarios.map((s) => mapping[s]);
|
|
147
|
+
}
|
|
148
|
+
generateTokens(config) {
|
|
149
|
+
const tokens = [];
|
|
150
|
+
const distribution = config.tokenScenarioDistribution || {
|
|
151
|
+
["rug_fast" /* RUG_PULL_FAST */]: 0.15,
|
|
152
|
+
["rug_slow" /* RUG_PULL_SLOW */]: 0.1,
|
|
153
|
+
["scam" /* SCAM_TOKEN */]: 0.1,
|
|
154
|
+
["pump_dump" /* PUMP_AND_DUMP */]: 0.15,
|
|
155
|
+
["mediocre" /* MEDIOCRE */]: 0.2,
|
|
156
|
+
["successful" /* SUCCESSFUL */]: 0.15,
|
|
157
|
+
["runner_moon" /* RUNNER_MOON */]: 0.05,
|
|
158
|
+
["bluechip" /* BLUE_CHIP */]: 0.05,
|
|
159
|
+
["slow_bleed" /* SLOW_BLEED */]: 0.05
|
|
160
|
+
};
|
|
161
|
+
for (let i = 0; i < config.tokenCount; i++) {
|
|
162
|
+
const scenario = this.selectScenarioByWeight(distribution);
|
|
163
|
+
const token = this.createToken(scenario, i, config);
|
|
164
|
+
tokens.push(token);
|
|
165
|
+
}
|
|
166
|
+
console.log(
|
|
167
|
+
`\u{1F4CA} Generated ${tokens.length} tokens with scenarios:`,
|
|
168
|
+
tokens.reduce(
|
|
169
|
+
(acc, t) => {
|
|
170
|
+
acc[t.scenario] = (acc[t.scenario] || 0) + 1;
|
|
171
|
+
return acc;
|
|
172
|
+
},
|
|
173
|
+
{}
|
|
174
|
+
)
|
|
175
|
+
);
|
|
176
|
+
return tokens;
|
|
177
|
+
}
|
|
178
|
+
selectScenarioByWeight(distribution) {
|
|
179
|
+
const entries = Object.entries(distribution);
|
|
180
|
+
const totalWeight = entries.reduce((sum, [_, weight]) => sum + weight, 0);
|
|
181
|
+
let random = Math.random() * totalWeight;
|
|
182
|
+
for (const [scenario, weight] of entries) {
|
|
183
|
+
random -= weight;
|
|
184
|
+
if (random <= 0) {
|
|
185
|
+
return scenario;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return "mediocre" /* MEDIOCRE */;
|
|
189
|
+
}
|
|
190
|
+
createToken(scenario, index, config) {
|
|
191
|
+
const launchSpread = config.endTime.getTime() - config.startTime.getTime();
|
|
192
|
+
const launchOffset = Math.random() * launchSpread * 0.8;
|
|
193
|
+
const scenarioConfig = this.getScenarioConfig(scenario);
|
|
194
|
+
const tokenFromService = this.tokenService.createTokenFromScenario(scenarioConfig);
|
|
195
|
+
return {
|
|
196
|
+
address: `0x${uuidv4().replace(/-/g, "")}${index.toString().padStart(8, "0")}`,
|
|
197
|
+
symbol: `SIM${scenario.substring(0, 3).toUpperCase()}${index}`,
|
|
198
|
+
name: `Simulated ${scenario.replace(/_/g, " ")} Token ${index}`,
|
|
199
|
+
scenario,
|
|
200
|
+
launchTime: new Date(config.startTime.getTime() + launchOffset),
|
|
201
|
+
initialPrice: 1e-5 + Math.random() * 1e-4,
|
|
202
|
+
// $0.00001 - $0.0001
|
|
203
|
+
initialMarketCap: 1e4 + Math.random() * 9e4,
|
|
204
|
+
// $10k - $100k
|
|
205
|
+
initialLiquidity: 5e3 + Math.random() * 45e3,
|
|
206
|
+
// $5k - $50k
|
|
207
|
+
priceTrajectory: tokenFromService.priceTrajectory
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
getScenarioConfig(scenario) {
|
|
211
|
+
const configs = {
|
|
212
|
+
["rug_fast" /* RUG_PULL_FAST */]: {
|
|
213
|
+
type: "rug",
|
|
214
|
+
name: "FastRug Token",
|
|
215
|
+
symbol: "FRUG",
|
|
216
|
+
description: "Rugs within 2 days",
|
|
217
|
+
initialPrice: 1e-5,
|
|
218
|
+
initialLiquidity: 5e3,
|
|
219
|
+
initialMarketCap: 1e4,
|
|
220
|
+
rugTiming: 2
|
|
221
|
+
},
|
|
222
|
+
["rug_slow" /* RUG_PULL_SLOW */]: {
|
|
223
|
+
type: "rug",
|
|
224
|
+
name: "SlowRug Token",
|
|
225
|
+
symbol: "SRUG",
|
|
226
|
+
description: "Builds trust then rugs",
|
|
227
|
+
initialPrice: 5e-5,
|
|
228
|
+
initialLiquidity: 2e4,
|
|
229
|
+
initialMarketCap: 5e4,
|
|
230
|
+
rugTiming: 10
|
|
231
|
+
},
|
|
232
|
+
["scam" /* SCAM_TOKEN */]: {
|
|
233
|
+
type: "scam",
|
|
234
|
+
name: "Scam Token",
|
|
235
|
+
symbol: "SCAM",
|
|
236
|
+
description: "Low liquidity scam",
|
|
237
|
+
initialPrice: 1e-3,
|
|
238
|
+
initialLiquidity: 500,
|
|
239
|
+
initialMarketCap: 5e3
|
|
240
|
+
},
|
|
241
|
+
["runner_moon" /* RUNNER_MOON */]: {
|
|
242
|
+
type: "runner",
|
|
243
|
+
name: "MoonShot Token",
|
|
244
|
+
symbol: "MOON",
|
|
245
|
+
description: "50x growth potential",
|
|
246
|
+
initialPrice: 1e-5,
|
|
247
|
+
initialLiquidity: 5e4,
|
|
248
|
+
initialMarketCap: 1e5
|
|
249
|
+
},
|
|
250
|
+
["runner_steady" /* RUNNER_STEADY */]: {
|
|
251
|
+
type: "runner",
|
|
252
|
+
name: "SteadyGains Token",
|
|
253
|
+
symbol: "GAIN",
|
|
254
|
+
description: "10x steady growth",
|
|
255
|
+
initialPrice: 1e-4,
|
|
256
|
+
initialLiquidity: 3e4,
|
|
257
|
+
initialMarketCap: 2e5
|
|
258
|
+
},
|
|
259
|
+
["successful" /* SUCCESSFUL */]: {
|
|
260
|
+
type: "successful",
|
|
261
|
+
name: "Solid Project",
|
|
262
|
+
symbol: "SOLID",
|
|
263
|
+
description: "3x growth",
|
|
264
|
+
initialPrice: 1e-3,
|
|
265
|
+
initialLiquidity: 1e5,
|
|
266
|
+
initialMarketCap: 5e5
|
|
267
|
+
},
|
|
268
|
+
["mediocre" /* MEDIOCRE */]: {
|
|
269
|
+
type: "mediocre",
|
|
270
|
+
name: "Crabwalk Token",
|
|
271
|
+
symbol: "CRAB",
|
|
272
|
+
description: "Sideways movement",
|
|
273
|
+
initialPrice: 0.01,
|
|
274
|
+
initialLiquidity: 5e4,
|
|
275
|
+
initialMarketCap: 3e5
|
|
276
|
+
},
|
|
277
|
+
["stagnant" /* STAGNANT */]: {
|
|
278
|
+
type: "stagnant",
|
|
279
|
+
name: "Dead Project",
|
|
280
|
+
symbol: "DEAD",
|
|
281
|
+
description: "No volume",
|
|
282
|
+
initialPrice: 5e-3,
|
|
283
|
+
initialLiquidity: 1e4,
|
|
284
|
+
initialMarketCap: 5e4
|
|
285
|
+
},
|
|
286
|
+
["bluechip" /* BLUE_CHIP */]: {
|
|
287
|
+
type: "bluechip",
|
|
288
|
+
name: "Established Token",
|
|
289
|
+
symbol: "BLUE",
|
|
290
|
+
description: "Stable growth",
|
|
291
|
+
initialPrice: 10,
|
|
292
|
+
initialLiquidity: 5e6,
|
|
293
|
+
initialMarketCap: 1e8
|
|
294
|
+
},
|
|
295
|
+
["pump_dump" /* PUMP_AND_DUMP */]: {
|
|
296
|
+
type: "pump_dump",
|
|
297
|
+
name: "PumpDump Token",
|
|
298
|
+
symbol: "PUMP",
|
|
299
|
+
description: "20x then dump",
|
|
300
|
+
initialPrice: 1e-5,
|
|
301
|
+
initialLiquidity: 15e3,
|
|
302
|
+
initialMarketCap: 2e4,
|
|
303
|
+
pumpTiming: 3,
|
|
304
|
+
dumpTiming: 5
|
|
305
|
+
},
|
|
306
|
+
["slow_bleed" /* SLOW_BLEED */]: {
|
|
307
|
+
type: "slow_bleed",
|
|
308
|
+
name: "BleedOut Token",
|
|
309
|
+
symbol: "BLEED",
|
|
310
|
+
description: "Slow decline",
|
|
311
|
+
initialPrice: 0.01,
|
|
312
|
+
initialLiquidity: 4e4,
|
|
313
|
+
initialMarketCap: 2e5
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
return configs[scenario];
|
|
317
|
+
}
|
|
318
|
+
generateCallsForTimeStep(currentTime, tokens, priceHistory, config) {
|
|
319
|
+
const calls = [];
|
|
320
|
+
const activeTokens = Array.from(tokens.values()).filter((token) => {
|
|
321
|
+
const isLaunched = token.launchTime <= currentTime;
|
|
322
|
+
const history = priceHistory.get(token.address) || [];
|
|
323
|
+
const latestPrice = history[history.length - 1];
|
|
324
|
+
const isDead = latestPrice && latestPrice.price < token.initialPrice * 0.01;
|
|
325
|
+
return isLaunched && !isDead;
|
|
326
|
+
});
|
|
327
|
+
if (activeTokens.length === 0) return calls;
|
|
328
|
+
for (const actor of config.actors) {
|
|
329
|
+
const shouldCall = this.shouldActorCall(actor, currentTime);
|
|
330
|
+
if (!shouldCall) continue;
|
|
331
|
+
const targetTokens = this.selectTokensForActor(
|
|
332
|
+
actor,
|
|
333
|
+
activeTokens,
|
|
334
|
+
priceHistory,
|
|
335
|
+
currentTime
|
|
336
|
+
);
|
|
337
|
+
for (const token of targetTokens) {
|
|
338
|
+
const tokenPriceHistory = priceHistory.get(token.address);
|
|
339
|
+
if (!tokenPriceHistory) continue;
|
|
340
|
+
const call = this.generateActorCall(
|
|
341
|
+
actor,
|
|
342
|
+
token,
|
|
343
|
+
tokenPriceHistory,
|
|
344
|
+
currentTime
|
|
345
|
+
);
|
|
346
|
+
if (call) {
|
|
347
|
+
calls.push(call);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return calls;
|
|
352
|
+
}
|
|
353
|
+
shouldActorCall(actor, _currentTime) {
|
|
354
|
+
const frequencyMultiplier = {
|
|
355
|
+
high: 0.7,
|
|
356
|
+
// Increased from 0.3
|
|
357
|
+
medium: 0.4,
|
|
358
|
+
// Increased from 0.1
|
|
359
|
+
low: 0.15
|
|
360
|
+
// Increased from 0.03
|
|
361
|
+
}[actor.callFrequency];
|
|
362
|
+
return Math.random() < frequencyMultiplier;
|
|
363
|
+
}
|
|
364
|
+
selectTokensForActor(actor, activeTokens, priceHistory, currentTime) {
|
|
365
|
+
let candidateTokens = activeTokens;
|
|
366
|
+
if (["elite_analyst", "skilled_trader"].includes(actor.archetype)) {
|
|
367
|
+
candidateTokens = activeTokens.filter((token) => {
|
|
368
|
+
const _history = priceHistory.get(token.address) || [];
|
|
369
|
+
const timeSinceLaunch = (currentTime.getTime() - token.launchTime.getTime()) / (1e3 * 60 * 60);
|
|
370
|
+
if (actor.archetype === "elite_analyst") {
|
|
371
|
+
if ([
|
|
372
|
+
"rug_fast" /* RUG_PULL_FAST */,
|
|
373
|
+
"rug_slow" /* RUG_PULL_SLOW */,
|
|
374
|
+
"scam" /* SCAM_TOKEN */
|
|
375
|
+
].includes(token.scenario)) {
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
378
|
+
return timeSinceLaunch < 48 && // Within 2 days of launch
|
|
379
|
+
[
|
|
380
|
+
"successful" /* SUCCESSFUL */,
|
|
381
|
+
"runner_moon" /* RUNNER_MOON */,
|
|
382
|
+
"runner_steady" /* RUNNER_STEADY */,
|
|
383
|
+
"bluechip" /* BLUE_CHIP */
|
|
384
|
+
].includes(token.scenario);
|
|
385
|
+
} else {
|
|
386
|
+
return timeSinceLaunch < 72 && // Within 3 days
|
|
387
|
+
!["scam" /* SCAM_TOKEN */].includes(token.scenario);
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
} else if (actor.archetype === "fomo_trader") {
|
|
391
|
+
candidateTokens = activeTokens.filter((token) => {
|
|
392
|
+
const history = priceHistory.get(token.address) || [];
|
|
393
|
+
if (history.length < 10) return false;
|
|
394
|
+
const recentGain = history[history.length - 1].price / history[history.length - 10].price - 1;
|
|
395
|
+
return recentGain > 0.5;
|
|
396
|
+
});
|
|
397
|
+
} else if (actor.archetype === "pump_chaser") {
|
|
398
|
+
candidateTokens = activeTokens.filter((token) => {
|
|
399
|
+
const history = priceHistory.get(token.address) || [];
|
|
400
|
+
if (history.length < 5) return false;
|
|
401
|
+
const recentGain = history[history.length - 1].price / history[history.length - 5].price - 1;
|
|
402
|
+
return recentGain > 0.3;
|
|
403
|
+
});
|
|
404
|
+
} else if (actor.archetype === "rug_promoter") {
|
|
405
|
+
candidateTokens = activeTokens.filter(
|
|
406
|
+
(token) => actor.tokenPreferences.includes(token.scenario) && token.scenario !== "bluechip" /* BLUE_CHIP */
|
|
407
|
+
// Even rug promoters avoid obvious blue chips
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
if (candidateTokens.length === 0) {
|
|
411
|
+
candidateTokens = activeTokens.filter(
|
|
412
|
+
(token) => actor.tokenPreferences.includes(token.scenario)
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
if (candidateTokens.length === 0) return [];
|
|
416
|
+
const timedTokens = candidateTokens.filter((token) => {
|
|
417
|
+
const _history = priceHistory.get(token.address) || [];
|
|
418
|
+
const timeSinceLaunch = (currentTime.getTime() - token.launchTime.getTime()) / (1e3 * 60 * 60);
|
|
419
|
+
switch (actor.timingBias) {
|
|
420
|
+
case "early":
|
|
421
|
+
return timeSinceLaunch < 24;
|
|
422
|
+
// First day only
|
|
423
|
+
case "middle":
|
|
424
|
+
return timeSinceLaunch >= 24 && timeSinceLaunch < 120;
|
|
425
|
+
// Day 1-5
|
|
426
|
+
case "late":
|
|
427
|
+
return timeSinceLaunch >= 72;
|
|
428
|
+
// After 3 days
|
|
429
|
+
default:
|
|
430
|
+
return true;
|
|
431
|
+
}
|
|
432
|
+
});
|
|
433
|
+
const finalTokens = timedTokens.length > 0 ? timedTokens : candidateTokens;
|
|
434
|
+
const maxTokens = actor.archetype === "elite_analyst" ? 1 : 2;
|
|
435
|
+
const numTokens = Math.min(
|
|
436
|
+
finalTokens.length,
|
|
437
|
+
Math.floor(Math.random() * maxTokens) + 1
|
|
438
|
+
);
|
|
439
|
+
return finalTokens.sort(() => Math.random() - 0.5).slice(0, numTokens);
|
|
440
|
+
}
|
|
441
|
+
generateActorCall(actor, token, priceHistory, currentTime) {
|
|
442
|
+
const latestPrice = priceHistory[priceHistory.length - 1];
|
|
443
|
+
if (!latestPrice) return null;
|
|
444
|
+
const message = this.generateMessage(
|
|
445
|
+
actor,
|
|
446
|
+
token.symbol,
|
|
447
|
+
token.scenario,
|
|
448
|
+
"positive"
|
|
449
|
+
// Will be overridden based on actor logic
|
|
450
|
+
);
|
|
451
|
+
if (!message) return null;
|
|
452
|
+
const sentiment = this.determineActorSentiment(actor, token, priceHistory);
|
|
453
|
+
const conviction = this.determineActorConviction(
|
|
454
|
+
actor,
|
|
455
|
+
token,
|
|
456
|
+
priceHistory
|
|
457
|
+
);
|
|
458
|
+
return {
|
|
459
|
+
callId: uuidv4(),
|
|
460
|
+
originalMessageId: `sim_msg_${uuidv4()}`,
|
|
461
|
+
userId: actor.id,
|
|
462
|
+
username: actor.username,
|
|
463
|
+
timestamp: currentTime.getTime(),
|
|
464
|
+
content: message,
|
|
465
|
+
tokenMentioned: token.symbol,
|
|
466
|
+
nameMentioned: token.name,
|
|
467
|
+
caMentioned: token.address,
|
|
468
|
+
chain: SupportedChain.SOLANA,
|
|
469
|
+
sentiment,
|
|
470
|
+
conviction,
|
|
471
|
+
llmReasoning: `${actor.username} (${actor.archetype}) analyzing ${token.symbol}`,
|
|
472
|
+
certainty: "high",
|
|
473
|
+
fileSource: "simulation",
|
|
474
|
+
simulationMetadata: {
|
|
475
|
+
tokenScenario: token.scenario,
|
|
476
|
+
actorArchetype: actor.archetype,
|
|
477
|
+
priceAtCall: latestPrice.price,
|
|
478
|
+
marketCapAtCall: latestPrice.marketCap,
|
|
479
|
+
liquidityAtCall: latestPrice.liquidity,
|
|
480
|
+
expectedOutcome: this.predictOutcome(actor, token)
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
generateMessage(actor, tokenSymbol, _scenario, _sentiment) {
|
|
485
|
+
const templates = {
|
|
486
|
+
elite_analyst: [
|
|
487
|
+
`$${tokenSymbol} showing strong fundamentals. This is a long-term hold.`,
|
|
488
|
+
`Been researching $${tokenSymbol} - solid team and execution plan. Accumulating here.`
|
|
489
|
+
],
|
|
490
|
+
skilled_trader: [
|
|
491
|
+
`$${tokenSymbol} looking strong here. Adding to position.`,
|
|
492
|
+
`Good entry point for $${tokenSymbol}. Risk/reward favorable.`
|
|
493
|
+
],
|
|
494
|
+
pump_chaser: [
|
|
495
|
+
`$${tokenSymbol} is pumping hard! Just aped in!`,
|
|
496
|
+
`Holy shit $${tokenSymbol} is flying! This is going to $1!`
|
|
497
|
+
],
|
|
498
|
+
rug_promoter: [
|
|
499
|
+
`\u{1F680}\u{1F680} $${tokenSymbol} TO THE MOON! 1000X GEM! GET IN NOW! \u{1F680}\u{1F680}`,
|
|
500
|
+
`$${tokenSymbol} NEXT 100X!!! DEV DOXXED! LIQUIDITY LOCKED! SAFU! \u{1F48E}\u{1F48E}`
|
|
501
|
+
],
|
|
502
|
+
fomo_trader: [
|
|
503
|
+
`Everyone buying $${tokenSymbol}! I'm in!`,
|
|
504
|
+
`$${tokenSymbol} trending everywhere! Don't want to miss this!`
|
|
505
|
+
],
|
|
506
|
+
contrarian: [
|
|
507
|
+
`$${tokenSymbol} overhyped. Taking opposite position.`,
|
|
508
|
+
`While everyone's bullish on $${tokenSymbol}, I see weakness.`
|
|
509
|
+
],
|
|
510
|
+
technical_analyst: [
|
|
511
|
+
`$${tokenSymbol} breaking key resistance. Chart looks bullish.`,
|
|
512
|
+
`RSI oversold on $${tokenSymbol}. Bounce incoming.`
|
|
513
|
+
],
|
|
514
|
+
newbie: [
|
|
515
|
+
`Is $${tokenSymbol} a good buy? Thinking about getting some.`,
|
|
516
|
+
`Just bought my first $${tokenSymbol}! Hope it goes up!`
|
|
517
|
+
],
|
|
518
|
+
bot_spammer: [
|
|
519
|
+
`\u{1F48E} $${tokenSymbol} \u{1F48E} BUY NOW \u{1F48E}`,
|
|
520
|
+
`$${tokenSymbol} $${tokenSymbol} $${tokenSymbol} \u{1F680}\u{1F680}\u{1F680}`
|
|
521
|
+
]
|
|
522
|
+
};
|
|
523
|
+
const archetypeTemplates = templates[actor.archetype];
|
|
524
|
+
return archetypeTemplates[Math.floor(Math.random() * archetypeTemplates.length)];
|
|
525
|
+
}
|
|
526
|
+
determineActorSentiment(actor, token, priceHistory) {
|
|
527
|
+
if (actor.archetype === "elite_analyst") {
|
|
528
|
+
if ([
|
|
529
|
+
"successful" /* SUCCESSFUL */,
|
|
530
|
+
"runner_moon" /* RUNNER_MOON */,
|
|
531
|
+
"runner_steady" /* RUNNER_STEADY */,
|
|
532
|
+
"bluechip" /* BLUE_CHIP */
|
|
533
|
+
].includes(token.scenario)) {
|
|
534
|
+
return "positive";
|
|
535
|
+
}
|
|
536
|
+
if ([
|
|
537
|
+
"rug_fast" /* RUG_PULL_FAST */,
|
|
538
|
+
"rug_slow" /* RUG_PULL_SLOW */,
|
|
539
|
+
"scam" /* SCAM_TOKEN */
|
|
540
|
+
].includes(token.scenario)) {
|
|
541
|
+
return "negative";
|
|
542
|
+
}
|
|
543
|
+
return "neutral";
|
|
544
|
+
}
|
|
545
|
+
if (actor.archetype === "skilled_trader") {
|
|
546
|
+
if ([
|
|
547
|
+
"successful" /* SUCCESSFUL */,
|
|
548
|
+
"runner_moon" /* RUNNER_MOON */,
|
|
549
|
+
"runner_steady" /* RUNNER_STEADY */
|
|
550
|
+
].includes(token.scenario)) {
|
|
551
|
+
return "positive";
|
|
552
|
+
}
|
|
553
|
+
if (["rug_fast" /* RUG_PULL_FAST */, "scam" /* SCAM_TOKEN */].includes(
|
|
554
|
+
token.scenario
|
|
555
|
+
)) {
|
|
556
|
+
return Math.random() < 0.7 ? "negative" : "positive";
|
|
557
|
+
}
|
|
558
|
+
if (token.scenario === "pump_dump" /* PUMP_AND_DUMP */) {
|
|
559
|
+
const timeSinceLaunch = priceHistory.length;
|
|
560
|
+
return timeSinceLaunch < 5 ? "positive" : "negative";
|
|
561
|
+
}
|
|
562
|
+
return "neutral";
|
|
563
|
+
}
|
|
564
|
+
if (actor.archetype === "rug_promoter") {
|
|
565
|
+
if ([
|
|
566
|
+
"rug_fast" /* RUG_PULL_FAST */,
|
|
567
|
+
"rug_slow" /* RUG_PULL_SLOW */,
|
|
568
|
+
"scam" /* SCAM_TOKEN */
|
|
569
|
+
].includes(token.scenario)) {
|
|
570
|
+
return "positive";
|
|
571
|
+
}
|
|
572
|
+
return "neutral";
|
|
573
|
+
}
|
|
574
|
+
if (actor.archetype === "fomo_trader") {
|
|
575
|
+
return "positive";
|
|
576
|
+
}
|
|
577
|
+
if (actor.archetype === "pump_chaser") {
|
|
578
|
+
const priceChange = priceHistory.length > 5 ? priceHistory[priceHistory.length - 1].price / priceHistory[priceHistory.length - 5].price - 1 : 0;
|
|
579
|
+
return priceChange > 0.1 ? "positive" : "neutral";
|
|
580
|
+
}
|
|
581
|
+
if (actor.archetype === "contrarian") {
|
|
582
|
+
const priceChange = priceHistory.length > 10 ? priceHistory[priceHistory.length - 1].price / priceHistory[priceHistory.length - 10].price - 1 : 0;
|
|
583
|
+
return priceChange > 0.2 ? "negative" : "positive";
|
|
584
|
+
}
|
|
585
|
+
return actor.tokenPreferences.includes(token.scenario) ? "positive" : "neutral";
|
|
586
|
+
}
|
|
587
|
+
determineActorConviction(actor, _token, priceHistory) {
|
|
588
|
+
const baseConviction = {
|
|
589
|
+
elite_analyst: Conviction.HIGH,
|
|
590
|
+
skilled_trader: Conviction.MEDIUM,
|
|
591
|
+
pump_chaser: Conviction.HIGH,
|
|
592
|
+
rug_promoter: Conviction.VERY_HIGH,
|
|
593
|
+
fomo_trader: Conviction.MEDIUM,
|
|
594
|
+
contrarian: Conviction.MEDIUM,
|
|
595
|
+
technical_analyst: Conviction.MEDIUM,
|
|
596
|
+
newbie: Conviction.LOW,
|
|
597
|
+
bot_spammer: Conviction.LOW
|
|
598
|
+
}[actor.archetype] || Conviction.MEDIUM;
|
|
599
|
+
if (["pump_chaser", "fomo_trader"].includes(actor.archetype)) {
|
|
600
|
+
const recentGain = priceHistory.length > 5 ? priceHistory[priceHistory.length - 1].price / priceHistory[priceHistory.length - 5].price - 1 : 0;
|
|
601
|
+
if (recentGain > 0.5) {
|
|
602
|
+
return Conviction.VERY_HIGH;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
return baseConviction;
|
|
606
|
+
}
|
|
607
|
+
predictOutcome(actor, token) {
|
|
608
|
+
const successfulScenarios = [
|
|
609
|
+
"successful" /* SUCCESSFUL */,
|
|
610
|
+
"runner_moon" /* RUNNER_MOON */,
|
|
611
|
+
"bluechip" /* BLUE_CHIP */
|
|
612
|
+
];
|
|
613
|
+
const isGoodToken = successfulScenarios.includes(token.scenario);
|
|
614
|
+
const isSkilled = [
|
|
615
|
+
"elite_analyst",
|
|
616
|
+
"skilled_trader",
|
|
617
|
+
"contrarian"
|
|
618
|
+
].includes(actor.archetype);
|
|
619
|
+
if (isSkilled && isGoodToken) return "profit";
|
|
620
|
+
if (!isSkilled && !isGoodToken) return "loss";
|
|
621
|
+
if (actor.archetype === "rug_promoter" && !isGoodToken) return "loss";
|
|
622
|
+
return "neutral";
|
|
623
|
+
}
|
|
624
|
+
calculateActualProfits(calls, tokens, priceHistory) {
|
|
625
|
+
for (const call of calls) {
|
|
626
|
+
const token = tokens.get(call.caMentioned || "");
|
|
627
|
+
if (!token) continue;
|
|
628
|
+
const history = priceHistory.get(token.address) || [];
|
|
629
|
+
const callIndex = history.findIndex(
|
|
630
|
+
(p) => p.timestamp.getTime() === call.timestamp
|
|
631
|
+
);
|
|
632
|
+
if (callIndex === -1 || callIndex === history.length - 1) {
|
|
633
|
+
call.simulationMetadata.actualProfit = 0;
|
|
634
|
+
continue;
|
|
635
|
+
}
|
|
636
|
+
if (call.sentiment === "negative") {
|
|
637
|
+
const entryPrice2 = history[callIndex].price;
|
|
638
|
+
const exitIndex2 = Math.min(callIndex + 24, history.length - 1);
|
|
639
|
+
const exitPrice2 = history[exitIndex2].price;
|
|
640
|
+
const priceDropPercent = (entryPrice2 - exitPrice2) / entryPrice2 * 100;
|
|
641
|
+
call.simulationMetadata.actualProfit = priceDropPercent;
|
|
642
|
+
continue;
|
|
643
|
+
}
|
|
644
|
+
let exitIndex;
|
|
645
|
+
let forcedExit = false;
|
|
646
|
+
if ([
|
|
647
|
+
"rug_fast" /* RUG_PULL_FAST */,
|
|
648
|
+
"rug_slow" /* RUG_PULL_SLOW */,
|
|
649
|
+
"scam" /* SCAM_TOKEN */
|
|
650
|
+
].includes(token.scenario)) {
|
|
651
|
+
let rugIndex = callIndex;
|
|
652
|
+
for (let i = callIndex + 1; i < history.length; i++) {
|
|
653
|
+
if (history[i].price < history[callIndex].price * 0.1) {
|
|
654
|
+
rugIndex = i;
|
|
655
|
+
forcedExit = true;
|
|
656
|
+
break;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
exitIndex = rugIndex;
|
|
660
|
+
} else if (token.scenario === "pump_dump" /* PUMP_AND_DUMP */) {
|
|
661
|
+
let dumpIndex = callIndex;
|
|
662
|
+
let peakPrice = history[callIndex].price;
|
|
663
|
+
for (let i = callIndex + 1; i < history.length; i++) {
|
|
664
|
+
if (history[i].price > peakPrice) {
|
|
665
|
+
peakPrice = history[i].price;
|
|
666
|
+
} else if (history[i].price < peakPrice * 0.3) {
|
|
667
|
+
dumpIndex = i;
|
|
668
|
+
forcedExit = true;
|
|
669
|
+
break;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
exitIndex = dumpIndex;
|
|
673
|
+
} else {
|
|
674
|
+
if (call.simulationMetadata.actorArchetype === "elite_analyst") {
|
|
675
|
+
exitIndex = Math.min(callIndex + 72, history.length - 1);
|
|
676
|
+
} else if (call.simulationMetadata.actorArchetype === "skilled_trader") {
|
|
677
|
+
exitIndex = Math.min(callIndex + 48, history.length - 1);
|
|
678
|
+
} else if (["pump_chaser", "fomo_trader"].includes(
|
|
679
|
+
call.simulationMetadata.actorArchetype
|
|
680
|
+
)) {
|
|
681
|
+
exitIndex = Math.min(callIndex + 24, history.length - 1);
|
|
682
|
+
} else {
|
|
683
|
+
exitIndex = Math.min(callIndex + 24, history.length - 1);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
const entryPrice = history[callIndex].price;
|
|
687
|
+
const exitPrice = history[exitIndex].price;
|
|
688
|
+
let effectiveEntryPrice = entryPrice;
|
|
689
|
+
if (["fomo_trader", "pump_chaser"].includes(
|
|
690
|
+
call.simulationMetadata.actorArchetype
|
|
691
|
+
)) {
|
|
692
|
+
effectiveEntryPrice = entryPrice * 1.15;
|
|
693
|
+
} else if (call.simulationMetadata.actorArchetype === "rug_promoter") {
|
|
694
|
+
if ([
|
|
695
|
+
"rug_fast" /* RUG_PULL_FAST */,
|
|
696
|
+
"rug_slow" /* RUG_PULL_SLOW */,
|
|
697
|
+
"scam" /* SCAM_TOKEN */
|
|
698
|
+
].includes(token.scenario)) {
|
|
699
|
+
effectiveEntryPrice = entryPrice * 1.2;
|
|
700
|
+
}
|
|
701
|
+
} else if (call.simulationMetadata.actorArchetype === "elite_analyst") {
|
|
702
|
+
effectiveEntryPrice = entryPrice * 0.98;
|
|
703
|
+
}
|
|
704
|
+
let effectiveExitPrice = exitPrice;
|
|
705
|
+
if (forcedExit && ["pump_chaser", "fomo_trader", "rug_promoter"].includes(
|
|
706
|
+
call.simulationMetadata.actorArchetype
|
|
707
|
+
)) {
|
|
708
|
+
effectiveExitPrice = exitPrice * 0.9;
|
|
709
|
+
}
|
|
710
|
+
const profitPercent = (effectiveExitPrice - effectiveEntryPrice) / effectiveEntryPrice * 100;
|
|
711
|
+
call.simulationMetadata.actualProfit = profitPercent;
|
|
712
|
+
}
|
|
713
|
+
return Promise.resolve();
|
|
714
|
+
}
|
|
715
|
+
async cacheResults(result, config) {
|
|
716
|
+
const outputDir = config.outputDir;
|
|
717
|
+
await fs.mkdir(outputDir, { recursive: true });
|
|
718
|
+
const callsPath = path.join(outputDir, "simulated_calls.json");
|
|
719
|
+
await fs.writeFile(callsPath, JSON.stringify(result.calls, null, 2));
|
|
720
|
+
const tokensPath = path.join(outputDir, "simulated_tokens.json");
|
|
721
|
+
await fs.writeFile(
|
|
722
|
+
tokensPath,
|
|
723
|
+
JSON.stringify(Array.from(result.tokens.entries()), null, 2)
|
|
724
|
+
);
|
|
725
|
+
const pricesPath = path.join(outputDir, "price_history.json");
|
|
726
|
+
await fs.writeFile(
|
|
727
|
+
pricesPath,
|
|
728
|
+
JSON.stringify(Array.from(result.priceHistory.entries()), null, 2)
|
|
729
|
+
);
|
|
730
|
+
const perfPath = path.join(outputDir, "actor_performance.json");
|
|
731
|
+
await fs.writeFile(
|
|
732
|
+
perfPath,
|
|
733
|
+
JSON.stringify(Array.from(result.actorPerformance.entries()), null, 2)
|
|
734
|
+
);
|
|
735
|
+
console.log(`\u{1F4C1} Results cached to ${outputDir}`);
|
|
736
|
+
}
|
|
737
|
+
async loadCachedSimulation(outputDir) {
|
|
738
|
+
try {
|
|
739
|
+
const callsData = await fs.readFile(
|
|
740
|
+
path.join(outputDir, "simulated_calls.json"),
|
|
741
|
+
"utf-8"
|
|
742
|
+
);
|
|
743
|
+
const tokensData = await fs.readFile(
|
|
744
|
+
path.join(outputDir, "simulated_tokens.json"),
|
|
745
|
+
"utf-8"
|
|
746
|
+
);
|
|
747
|
+
const pricesData = await fs.readFile(
|
|
748
|
+
path.join(outputDir, "price_history.json"),
|
|
749
|
+
"utf-8"
|
|
750
|
+
);
|
|
751
|
+
const perfData = await fs.readFile(
|
|
752
|
+
path.join(outputDir, "actor_performance.json"),
|
|
753
|
+
"utf-8"
|
|
754
|
+
);
|
|
755
|
+
return {
|
|
756
|
+
calls: JSON.parse(callsData),
|
|
757
|
+
tokens: new Map(JSON.parse(tokensData)),
|
|
758
|
+
priceHistory: new Map(JSON.parse(pricesData)),
|
|
759
|
+
actorPerformance: new Map(JSON.parse(perfData))
|
|
760
|
+
};
|
|
761
|
+
} catch (error) {
|
|
762
|
+
console.error("Failed to load cached simulation:", error);
|
|
763
|
+
return null;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
export {
|
|
768
|
+
SimulationRunner,
|
|
769
|
+
TokenScenario
|
|
770
|
+
};
|
|
771
|
+
//# sourceMappingURL=simulationRunner.js.map
|