@one_deploy/sdk 1.0.7 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/README.md +339 -0
  2. package/dist/ForexPoolDataGenerator--__twRwl.d.mts +76 -0
  3. package/dist/ForexPoolDataGenerator-eUgwsU_B.d.ts +76 -0
  4. package/dist/OneForexTradeHistory-TlKxjbFF.d.ts +250 -0
  5. package/dist/OneForexTradeHistory-iDySMcw0.d.mts +250 -0
  6. package/dist/components/index.d.mts +539 -0
  7. package/dist/components/index.d.ts +539 -0
  8. package/dist/components/index.js +7295 -0
  9. package/dist/components/index.js.map +1 -0
  10. package/dist/components/index.mjs +7243 -0
  11. package/dist/components/index.mjs.map +1 -0
  12. package/dist/config/index.d.mts +1 -0
  13. package/dist/config/index.d.ts +1 -0
  14. package/dist/console-BfTMA7ah.d.mts +504 -0
  15. package/dist/console-BfTMA7ah.d.ts +504 -0
  16. package/dist/hooks/index.d.mts +323 -1
  17. package/dist/hooks/index.d.ts +323 -1
  18. package/dist/hooks/index.js +3223 -0
  19. package/dist/hooks/index.js.map +1 -1
  20. package/dist/hooks/index.mjs +3204 -1
  21. package/dist/hooks/index.mjs.map +1 -1
  22. package/dist/index.d.mts +18 -352
  23. package/dist/index.d.ts +18 -352
  24. package/dist/index.js +8646 -574
  25. package/dist/index.js.map +1 -1
  26. package/dist/index.mjs +8449 -432
  27. package/dist/index.mjs.map +1 -1
  28. package/dist/providers/index.d.mts +31 -31
  29. package/dist/providers/index.d.ts +31 -31
  30. package/dist/providers/index.js +140 -153
  31. package/dist/providers/index.js.map +1 -1
  32. package/dist/providers/index.mjs +100 -109
  33. package/dist/providers/index.mjs.map +1 -1
  34. package/dist/react-native.d.mts +8 -144
  35. package/dist/react-native.d.ts +8 -144
  36. package/dist/react-native.js +2640 -689
  37. package/dist/react-native.js.map +1 -1
  38. package/dist/react-native.mjs +2610 -691
  39. package/dist/react-native.mjs.map +1 -1
  40. package/dist/services/index.d.mts +85 -4
  41. package/dist/services/index.d.ts +85 -4
  42. package/dist/services/index.js +1621 -0
  43. package/dist/services/index.js.map +1 -1
  44. package/dist/services/index.mjs +1619 -1
  45. package/dist/services/index.mjs.map +1 -1
  46. package/dist/types/index.d.mts +203 -1
  47. package/dist/types/index.d.ts +203 -1
  48. package/dist/types/index.js +275 -0
  49. package/dist/types/index.js.map +1 -1
  50. package/dist/types/index.mjs +251 -0
  51. package/dist/types/index.mjs.map +1 -1
  52. package/dist/useForexTrading-BleeSor8.d.mts +80 -0
  53. package/dist/useForexTrading-ZgW_G40Q.d.ts +80 -0
  54. package/package.json +9 -2
  55. package/src/components/OneConnectButton.tsx +24 -1
  56. package/src/components/OneNFTGallery.tsx +13 -7
  57. package/src/components/OneOfframpWidget.tsx +4 -3
  58. package/src/components/OnePayWidget.tsx +10 -1
  59. package/src/components/OneSendWidget.tsx +3 -3
  60. package/src/components/OneSwapWidget.tsx +4 -4
  61. package/src/components/OneTransactionButton.tsx +28 -3
  62. package/src/components/OneWalletBalance.tsx +1 -1
  63. package/src/components/ai/OneChainSelector.tsx +63 -336
  64. package/src/components/ai/OneForexCapitalSplit.tsx +112 -0
  65. package/src/components/ai/OneForexConsoleView.tsx +90 -0
  66. package/src/components/ai/OneForexPairSelector.tsx +101 -0
  67. package/src/components/ai/OneForexPoolCard.tsx +105 -0
  68. package/src/components/ai/OneForexTradeHistory.tsx +107 -0
  69. package/src/components/ai/OnePairSelector.tsx +77 -434
  70. package/src/components/ai/console/OneAIQuantConsole.tsx +423 -0
  71. package/src/components/ai/console/OneAgentCard.tsx +383 -0
  72. package/src/components/ai/console/OneAgentConsole.tsx +469 -0
  73. package/src/components/ai/console/OneDecisionTimeline.tsx +433 -0
  74. package/src/components/ai/console/OneMetricsDashboard.tsx +493 -0
  75. package/src/components/ai/console/OnePositionCard.tsx +406 -0
  76. package/src/components/ai/console/OnePositionDetail.tsx +600 -0
  77. package/src/components/ai/console/OneRiskIndicator.tsx +464 -0
  78. package/src/components/ai/console/OneTradingConsole.tsx +660 -0
  79. package/src/components/ai/console/index.ts +17 -0
  80. package/src/components/ai/index.ts +10 -0
  81. package/src/hooks/index.ts +46 -0
  82. package/src/hooks/useAIDecisions.ts +280 -0
  83. package/src/hooks/useAIPositions.ts +349 -0
  84. package/src/hooks/useAIQuantConsole.ts +283 -0
  85. package/src/hooks/useAIRiskStatus.ts +276 -0
  86. package/src/hooks/useAITrading.ts +190 -0
  87. package/src/hooks/useBotSimulation.ts +201 -0
  88. package/src/hooks/useForexTrading.ts +430 -0
  89. package/src/hooks/useTradingConsole.ts +243 -0
  90. package/src/index.ts +123 -5
  91. package/src/providers/OneProvider.tsx +181 -5
  92. package/src/providers/index.ts +22 -8
  93. package/src/react-native.ts +41 -0
  94. package/src/services/forex/BotSimulationEngine.ts +968 -0
  95. package/src/services/forex/ForexPoolDataGenerator.ts +542 -0
  96. package/src/services/forex/ForexSimulationEngine.ts +482 -0
  97. package/src/services/forex/index.ts +21 -0
  98. package/src/services/index.ts +16 -0
  99. package/src/types/aiTrading.ts +151 -0
  100. package/src/types/console.ts +380 -0
  101. package/src/types/forex.ts +282 -0
  102. package/src/types/index.ts +106 -0
  103. package/dist/price-CgqXPnT3.d.ts +0 -13
  104. package/dist/price-ClbLHHjv.d.mts +0 -13
  105. package/dist/supabase-BT0c7q9e.d.mts +0 -82
  106. package/dist/supabase-BT0c7q9e.d.ts +0 -82
@@ -0,0 +1,968 @@
1
+ // BotSimulationEngine.ts - Core simulation engine for trading bot console
2
+ // Runs independent simulation loops for each strategy bot with distinct personalities
3
+
4
+ // ── Types ──────────────────────────────────────────────────────────────────────
5
+
6
+ export type BotLogType =
7
+ | 'SCAN'
8
+ | 'INDICATOR'
9
+ | 'NEWS'
10
+ | 'SIGNAL'
11
+ | 'ANALYSIS'
12
+ | 'DECISION'
13
+ | 'ORDER'
14
+ | 'FILLED'
15
+ | 'PNL'
16
+ | 'RISK'
17
+ | 'SYSTEM'
18
+ | 'STRATEGY' // Strategy reasoning and context
19
+ | 'THINKING'; // AI thinking process
20
+
21
+ export interface BotLogEntry {
22
+ id: string;
23
+ timestamp: number;
24
+ strategyId: string;
25
+ strategyName: string;
26
+ type: BotLogType;
27
+ message: string;
28
+ data?: Record<string, any>;
29
+ importance: 'low' | 'medium' | 'high';
30
+ }
31
+
32
+ export interface IndicatorSnapshot {
33
+ rsi: number;
34
+ macd: { value: number; signal: number; histogram: number };
35
+ ema: { short: number; long: number; crossover: 'golden' | 'death' | 'none' };
36
+ bollinger: { upper: number; middle: number; lower: number; width: number; position: number };
37
+ volume: { current: number; average: number; ratio: number };
38
+ }
39
+
40
+ export interface BotState {
41
+ strategyId: string;
42
+ strategyName: string;
43
+ isRunning: boolean;
44
+ currentPair: string;
45
+ currentPrice: number;
46
+ indicators: IndicatorSnapshot;
47
+ openPositions: OpenPosition[];
48
+ totalPnl: number;
49
+ totalTrades: number;
50
+ winRate: number;
51
+ lastSignal: string;
52
+ lastSignalConfidence: number;
53
+ }
54
+
55
+ interface OpenPosition {
56
+ id: string;
57
+ pair: string;
58
+ side: 'LONG' | 'SHORT';
59
+ entryPrice: number;
60
+ currentPrice: number;
61
+ size: number;
62
+ leverage: number;
63
+ pnl: number;
64
+ pnlPercent: number;
65
+ }
66
+
67
+ export interface StrategyPersonality {
68
+ id: string;
69
+ name: string;
70
+ shortName: string;
71
+ color: string;
72
+ scanIntervalMin: number;
73
+ scanIntervalMax: number;
74
+ tradeFrequency: number;
75
+ positionSizeMin: number;
76
+ positionSizeMax: number;
77
+ leverageMin: number;
78
+ leverageMax: number;
79
+ primaryIndicators: string[];
80
+ riskTolerance: 'low' | 'medium' | 'high';
81
+ preferredPairs: string[];
82
+ rsiBias: number;
83
+ }
84
+
85
+ type LogCallback = (entry: BotLogEntry) => void;
86
+
87
+ // ── Strategy Personalities ─────────────────────────────────────────────────────
88
+
89
+ export const STRATEGY_PERSONALITIES: StrategyPersonality[] = [
90
+ {
91
+ id: 'balanced-01',
92
+ name: 'Balanced Alpha',
93
+ shortName: 'BAL',
94
+ color: '#3B82F6',
95
+ scanIntervalMin: 25000, // Slower: 25-40s between cycles
96
+ scanIntervalMax: 40000,
97
+ tradeFrequency: 0.4,
98
+ positionSizeMin: 15,
99
+ positionSizeMax: 35,
100
+ leverageMin: 3,
101
+ leverageMax: 10,
102
+ primaryIndicators: ['RSI', 'MACD'],
103
+ riskTolerance: 'medium',
104
+ preferredPairs: ['BTC/USDT', 'ETH/USDT', 'SOL/USDT'],
105
+ rsiBias: 50,
106
+ },
107
+ {
108
+ id: 'conservative-01',
109
+ name: 'Conservative Shield',
110
+ shortName: 'CON',
111
+ color: '#10B981',
112
+ scanIntervalMin: 35000, // Slower: 35-55s between cycles
113
+ scanIntervalMax: 55000,
114
+ tradeFrequency: 0.25,
115
+ positionSizeMin: 10,
116
+ positionSizeMax: 20,
117
+ leverageMin: 2,
118
+ leverageMax: 5,
119
+ primaryIndicators: ['Bollinger', 'Volume'],
120
+ riskTolerance: 'low',
121
+ preferredPairs: ['BTC/USDT', 'ETH/USDT'],
122
+ rsiBias: 45,
123
+ },
124
+ {
125
+ id: 'aggressive-01',
126
+ name: 'Aggressive Momentum',
127
+ shortName: 'AGG',
128
+ color: '#EF4444',
129
+ scanIntervalMin: 18000, // Slower: 18-30s between cycles
130
+ scanIntervalMax: 30000,
131
+ tradeFrequency: 0.5,
132
+ positionSizeMin: 25,
133
+ positionSizeMax: 50,
134
+ leverageMin: 5,
135
+ leverageMax: 20,
136
+ primaryIndicators: ['RSI', 'MACD', 'EMA', 'Volume'],
137
+ riskTolerance: 'high',
138
+ preferredPairs: ['BTC/USDT', 'ETH/USDT', 'SOL/USDT', 'DOGE/USDT', 'AVAX/USDT'],
139
+ rsiBias: 55,
140
+ },
141
+ ];
142
+
143
+ // ── Trading pairs with base prices ─────────────────────────────────────────────
144
+
145
+ const PAIR_PRICES: Record<string, number> = {
146
+ 'BTC/USDT': 67500,
147
+ 'ETH/USDT': 3450,
148
+ 'BNB/USDT': 605,
149
+ 'SOL/USDT': 178,
150
+ 'XRP/USDT': 0.62,
151
+ 'DOGE/USDT': 0.165,
152
+ 'ADA/USDT': 0.45,
153
+ 'AVAX/USDT': 38.5,
154
+ 'ARB/USDT': 1.18,
155
+ 'MATIC/USDT': 0.72,
156
+ 'LINK/USDT': 14.5,
157
+ 'UNI/USDT': 7.8,
158
+ 'AAVE/USDT': 92,
159
+ 'OP/USDT': 2.45,
160
+ 'APT/USDT': 8.9,
161
+ 'INJ/USDT': 24.5,
162
+ 'TIA/USDT': 11.2,
163
+ 'SUI/USDT': 1.65,
164
+ 'DOT/USDT': 7.2,
165
+ 'ATOM/USDT': 9.8,
166
+ 'FIL/USDT': 5.6,
167
+ 'LTC/USDT': 72,
168
+ 'NEAR/USDT': 5.1,
169
+ 'FTM/USDT': 0.42,
170
+ };
171
+
172
+ const CHAIN_INFO: Record<string, { name: string; shortName: string; icon: string }> = {
173
+ ethereum: { name: 'Ethereum', shortName: 'ETH', icon: 'Ξ' },
174
+ arbitrum: { name: 'Arbitrum', shortName: 'ARB', icon: '◆' },
175
+ bsc: { name: 'BSC', shortName: 'BSC', icon: '◆' },
176
+ base: { name: 'Base', shortName: 'BASE', icon: '●' },
177
+ polygon: { name: 'Polygon', shortName: 'POLY', icon: '⬡' },
178
+ optimism: { name: 'Optimism', shortName: 'OP', icon: '◉' },
179
+ avalanche: { name: 'Avalanche', shortName: 'AVAX', icon: '▲' },
180
+ linea: { name: 'Linea', shortName: 'LINEA', icon: '═' },
181
+ zksync: { name: 'zkSync', shortName: 'ZK', icon: '⬢' },
182
+ scroll: { name: 'Scroll', shortName: 'SCRL', icon: '◎' },
183
+ };
184
+
185
+ const NEWS_HEADLINES = [
186
+ 'Fed signals potential rate pause, crypto markets react positively',
187
+ 'Major institutional investor increases BTC allocation by 15%',
188
+ 'On-chain data shows whale accumulation pattern forming',
189
+ 'DeFi TVL reaches new monthly high across major protocols',
190
+ 'Exchange outflows surge as holders move to cold storage',
191
+ 'Options market signals increased volatility expected this week',
192
+ 'Mining difficulty adjustment approaching, hash rate stable',
193
+ 'Regulatory clarity in EU boosts market sentiment',
194
+ 'Stablecoin supply expanding, potential bullish indicator',
195
+ 'Social sentiment score shifts to extreme greed zone',
196
+ 'Cross-chain bridge volume hits record daily high',
197
+ 'Layer 2 adoption metrics show 40% MoM growth',
198
+ ];
199
+
200
+ // ── Helpers ────────────────────────────────────────────────────────────────────
201
+
202
+ let idCounter = 0;
203
+ function genId(): string {
204
+ return `log_${Date.now()}_${++idCounter}`;
205
+ }
206
+
207
+ function rand(min: number, max: number): number {
208
+ return min + Math.random() * (max - min);
209
+ }
210
+
211
+ function randInt(min: number, max: number): number {
212
+ return Math.floor(rand(min, max + 1));
213
+ }
214
+
215
+ function pick<T>(arr: T[]): T {
216
+ return arr[Math.floor(Math.random() * arr.length)];
217
+ }
218
+
219
+ function clamp(val: number, min: number, max: number): number {
220
+ return Math.max(min, Math.min(max, val));
221
+ }
222
+
223
+ function formatPrice(price: number): string {
224
+ if (price >= 1000) return price.toFixed(2);
225
+ if (price >= 1) return price.toFixed(3);
226
+ return price.toFixed(5);
227
+ }
228
+
229
+ // ── Engine ─────────────────────────────────────────────────────────────────────
230
+
231
+ class BotSimulationEngine {
232
+ private listeners: LogCallback[] = [];
233
+ private botTimers: Map<string, ReturnType<typeof setTimeout>> = new Map();
234
+ private botStates: Map<string, BotState> = new Map();
235
+ private priceState: Map<string, number> = new Map();
236
+ private indicatorState: Map<string, IndicatorSnapshot> = new Map();
237
+ private running = false;
238
+ private userPairs: string[] = [];
239
+ private userChains: string[] = [];
240
+
241
+ constructor() {
242
+ // Initialize prices with some jitter
243
+ for (const [pair, base] of Object.entries(PAIR_PRICES)) {
244
+ this.priceState.set(pair, base * (1 + rand(-0.02, 0.02)));
245
+ }
246
+ }
247
+
248
+ // ── Public API ─────────────────────────────────────────────────────────────
249
+
250
+ getStrategies(): StrategyPersonality[] {
251
+ return STRATEGY_PERSONALITIES;
252
+ }
253
+
254
+ start(strategyIds?: string[], userPairs?: string[], userChains?: string[]): void {
255
+ this.running = true;
256
+ // Convert pair IDs (e.g. 'BTC') to full symbols (e.g. 'BTC/USDT') if needed
257
+ this.userPairs = (userPairs || [])
258
+ .map(p => p.includes('/') ? p : `${p}/USDT`)
259
+ .filter(p => p in PAIR_PRICES);
260
+ this.userChains = (userChains || []).filter(c => c in CHAIN_INFO);
261
+ const strategies = strategyIds
262
+ ? STRATEGY_PERSONALITIES.filter(s => strategyIds.includes(s.id))
263
+ : STRATEGY_PERSONALITIES;
264
+
265
+ for (const strategy of strategies) {
266
+ this.initBotState(strategy);
267
+ this.scheduleCycle(strategy);
268
+ }
269
+ }
270
+
271
+ stop(strategyIds?: string[]): void {
272
+ const ids = strategyIds || Array.from(this.botTimers.keys());
273
+ for (const id of ids) {
274
+ const timer = this.botTimers.get(id);
275
+ if (timer) {
276
+ clearTimeout(timer);
277
+ this.botTimers.delete(id);
278
+ }
279
+ }
280
+ if (!strategyIds) {
281
+ this.running = false;
282
+ }
283
+ }
284
+
285
+ onLog(callback: LogCallback): () => void {
286
+ this.listeners.push(callback);
287
+ return () => {
288
+ this.listeners = this.listeners.filter(l => l !== callback);
289
+ };
290
+ }
291
+
292
+ getBotState(strategyId: string): BotState | undefined {
293
+ return this.botStates.get(strategyId);
294
+ }
295
+
296
+ getAllBotStates(): Map<string, BotState> {
297
+ return this.botStates;
298
+ }
299
+
300
+ isRunning(): boolean {
301
+ return this.running;
302
+ }
303
+
304
+ emitBootSequence(): void {
305
+ const bootMessages: Array<{ msg: string; delay: number }> = [
306
+ { msg: 'Initializing ONE Trading Engine v3.2.1...', delay: 0 },
307
+ { msg: 'Loading market data feeds...', delay: 500 },
308
+ { msg: 'Connecting to exchange WebSocket streams...', delay: 1200 },
309
+ { msg: 'Calibrating indicator engines (RSI, MACD, EMA, Bollinger)...', delay: 2000 },
310
+ { msg: 'Loading strategy personalities: balanced-01, conservative-01, aggressive-01', delay: 2800 },
311
+ { msg: 'Risk management module initialized (max drawdown: 15%)', delay: 3600 },
312
+ { msg: 'Portfolio allocation engine ready', delay: 4200 },
313
+ { msg: '=== All systems online. Starting trading cycles ===', delay: 5000 },
314
+ ];
315
+
316
+ for (const { msg, delay } of bootMessages) {
317
+ setTimeout(() => {
318
+ this.emit({
319
+ id: genId(),
320
+ timestamp: Date.now(),
321
+ strategyId: 'system',
322
+ strategyName: 'SYSTEM',
323
+ type: 'SYSTEM',
324
+ message: msg,
325
+ importance: 'medium',
326
+ });
327
+ }, delay);
328
+ }
329
+ }
330
+
331
+ destroy(): void {
332
+ this.stop();
333
+ this.listeners = [];
334
+ this.botStates.clear();
335
+ this.priceState.clear();
336
+ this.indicatorState.clear();
337
+ this.userPairs = [];
338
+ this.userChains = [];
339
+ }
340
+
341
+ // ── Private Methods ────────────────────────────────────────────────────────
342
+
343
+ private emit(entry: BotLogEntry): void {
344
+ for (const listener of this.listeners) {
345
+ listener(entry);
346
+ }
347
+ }
348
+
349
+ private getActivePairs(strategy: StrategyPersonality): string[] {
350
+ return this.userPairs.length > 0 ? this.userPairs : strategy.preferredPairs;
351
+ }
352
+
353
+ private getActiveChain(): string {
354
+ const chains = this.userChains.length > 0
355
+ ? this.userChains
356
+ : ['ethereum', 'arbitrum', 'bsc'];
357
+ return pick(chains);
358
+ }
359
+
360
+ private getChainLabel(chainId: string): string {
361
+ const info = CHAIN_INFO[chainId];
362
+ return info ? info.shortName : chainId;
363
+ }
364
+
365
+ private initBotState(strategy: StrategyPersonality): void {
366
+ const activePairs = this.getActivePairs(strategy);
367
+ const pair = pick(activePairs);
368
+ const price = this.priceState.get(pair) || PAIR_PRICES[pair] || 50000;
369
+ const indicators = this.generateIndicators(strategy, price);
370
+ this.indicatorState.set(strategy.id, indicators);
371
+
372
+ this.botStates.set(strategy.id, {
373
+ strategyId: strategy.id,
374
+ strategyName: strategy.name,
375
+ isRunning: true,
376
+ currentPair: pair,
377
+ currentPrice: price,
378
+ indicators,
379
+ openPositions: [],
380
+ totalPnl: rand(-50, 200),
381
+ totalTrades: randInt(5, 25),
382
+ winRate: rand(0.48, 0.68),
383
+ lastSignal: 'HOLD',
384
+ lastSignalConfidence: 0,
385
+ });
386
+ }
387
+
388
+ private scheduleCycle(strategy: StrategyPersonality): void {
389
+ if (!this.running) return;
390
+ const interval = rand(strategy.scanIntervalMin, strategy.scanIntervalMax);
391
+ const timer = setTimeout(() => {
392
+ if (this.running) {
393
+ this.runBotCycle(strategy);
394
+ this.scheduleCycle(strategy);
395
+ }
396
+ }, interval);
397
+ this.botTimers.set(strategy.id, timer);
398
+ }
399
+
400
+ private async runBotCycle(strategy: StrategyPersonality): Promise<void> {
401
+ const state = this.botStates.get(strategy.id);
402
+ if (!state) return;
403
+
404
+ const activePairs = this.getActivePairs(strategy);
405
+ const pair = pick(activePairs);
406
+ const price = this.simulatePrice(pair);
407
+ const indicators = this.generateIndicators(strategy, price);
408
+ this.indicatorState.set(strategy.id, indicators);
409
+
410
+ state.currentPair = pair;
411
+ state.currentPrice = price;
412
+ state.indicators = indicators;
413
+
414
+ const entries: Array<{ entry: Omit<BotLogEntry, 'id' | 'timestamp'>; delay: number }> = [];
415
+ let delay = 0;
416
+
417
+ // 1. SCAN - Always (slower delay)
418
+ const chain = this.getActiveChain();
419
+ const chainLabel = this.getChainLabel(chain);
420
+ entries.push({
421
+ entry: {
422
+ strategyId: strategy.id,
423
+ strategyName: strategy.shortName,
424
+ type: 'SCAN',
425
+ message: `Scanning ${pair} on ${chainLabel} | Price: $${formatPrice(price)}`,
426
+ data: { pair, chain, chainLabel },
427
+ importance: 'low',
428
+ },
429
+ delay,
430
+ });
431
+ delay += rand(800, 1500); // Slower: 0.8-1.5s
432
+
433
+ // 2. THINKING - AI reasoning process (new)
434
+ const thinkingMessages = this.generateThinkingProcess(strategy, pair, price);
435
+ for (const thinking of thinkingMessages) {
436
+ entries.push({
437
+ entry: {
438
+ strategyId: strategy.id,
439
+ strategyName: strategy.shortName,
440
+ type: 'THINKING',
441
+ message: thinking,
442
+ importance: 'low',
443
+ },
444
+ delay,
445
+ });
446
+ delay += rand(600, 1200); // 0.6-1.2s between thoughts
447
+ }
448
+
449
+ // 3. INDICATOR - Always (slower delay)
450
+ const indicatorParts = [];
451
+ if (strategy.primaryIndicators.includes('RSI') || strategy.primaryIndicators.includes('MACD')) {
452
+ indicatorParts.push(`RSI: ${indicators.rsi.toFixed(1)}`);
453
+ }
454
+ if (strategy.primaryIndicators.includes('MACD') || strategy.primaryIndicators.includes('RSI')) {
455
+ indicatorParts.push(`MACD: ${indicators.macd.histogram > 0 ? '+' : ''}${indicators.macd.histogram.toFixed(3)}`);
456
+ }
457
+ if (strategy.primaryIndicators.includes('EMA')) {
458
+ indicatorParts.push(`EMA: ${indicators.ema.short.toFixed(1)}/${indicators.ema.long.toFixed(1)}`);
459
+ if (indicators.ema.crossover !== 'none') {
460
+ indicatorParts.push(`[${indicators.ema.crossover.toUpperCase()} CROSS]`);
461
+ }
462
+ }
463
+ if (strategy.primaryIndicators.includes('Bollinger')) {
464
+ indicatorParts.push(`BB: ${indicators.bollinger.position.toFixed(1)}% width=${indicators.bollinger.width.toFixed(2)}`);
465
+ }
466
+ if (strategy.primaryIndicators.includes('Volume')) {
467
+ indicatorParts.push(`Vol: ${indicators.volume.ratio.toFixed(2)}x avg`);
468
+ }
469
+
470
+ entries.push({
471
+ entry: {
472
+ strategyId: strategy.id,
473
+ strategyName: strategy.shortName,
474
+ type: 'INDICATOR',
475
+ message: indicatorParts.join(' | '),
476
+ data: { indicators },
477
+ importance: 'low',
478
+ },
479
+ delay,
480
+ });
481
+ delay += rand(800, 1500); // Slower: 0.8-1.5s
482
+
483
+ // 4. NEWS - 12% chance (slower)
484
+ if (Math.random() < 0.12) {
485
+ const sentiment = Math.random() > 0.4 ? 'Bullish' : 'Bearish';
486
+ entries.push({
487
+ entry: {
488
+ strategyId: strategy.id,
489
+ strategyName: strategy.shortName,
490
+ type: 'NEWS',
491
+ message: `[${sentiment}] ${pick(NEWS_HEADLINES)}`,
492
+ importance: 'medium',
493
+ },
494
+ delay,
495
+ });
496
+ delay += rand(1000, 1800); // Slower: 1-1.8s
497
+ }
498
+
499
+ // 5. ANALYSIS - 40% chance (slower)
500
+ if (Math.random() < 0.4) {
501
+ const analysis = this.generateAnalysis(strategy, indicators, pair);
502
+ entries.push({
503
+ entry: {
504
+ strategyId: strategy.id,
505
+ strategyName: strategy.shortName,
506
+ type: 'ANALYSIS',
507
+ message: analysis,
508
+ importance: 'medium',
509
+ },
510
+ delay,
511
+ });
512
+ delay += rand(1000, 2000); // Slower: 1-2s
513
+ }
514
+
515
+ // 6-10. Signal evaluation and potential trade
516
+ const signal = this.evaluateSignal(strategy, indicators);
517
+ state.lastSignal = signal.direction;
518
+ state.lastSignalConfidence = signal.confidence;
519
+
520
+ if (signal.direction !== 'HOLD') {
521
+ // 6. STRATEGY - Show strategy reasoning context (NEW)
522
+ const strategyContext = this.generateStrategyContext(strategy, signal, indicators, pair);
523
+ entries.push({
524
+ entry: {
525
+ strategyId: strategy.id,
526
+ strategyName: strategy.shortName,
527
+ type: 'STRATEGY',
528
+ message: strategyContext,
529
+ data: {
530
+ strategy: strategy.name,
531
+ riskTolerance: strategy.riskTolerance,
532
+ primaryIndicators: strategy.primaryIndicators,
533
+ signal: signal.direction,
534
+ confidence: signal.confidence,
535
+ },
536
+ importance: 'high',
537
+ },
538
+ delay,
539
+ });
540
+ delay += rand(1500, 2500); // Slower: 1.5-2.5s
541
+
542
+ // 7. SIGNAL
543
+ entries.push({
544
+ entry: {
545
+ strategyId: strategy.id,
546
+ strategyName: strategy.shortName,
547
+ type: 'SIGNAL',
548
+ message: `${signal.direction} signal detected | Confidence: ${(signal.confidence * 100).toFixed(1)}% | ${signal.reason}`,
549
+ data: { signal },
550
+ importance: 'high',
551
+ },
552
+ delay,
553
+ });
554
+ delay += rand(1200, 2000); // Slower: 1.2-2s
555
+
556
+ // 8. DECISION
557
+ const decision = this.makeTradeDecision(strategy, signal, state);
558
+ if (decision.execute) {
559
+ entries.push({
560
+ entry: {
561
+ strategyId: strategy.id,
562
+ strategyName: strategy.shortName,
563
+ type: 'DECISION',
564
+ message: `Execute ${signal.direction} | Size: ${decision.positionSize.toFixed(1)}% | Leverage: ${decision.leverage}x | Risk/Reward: 1:${decision.riskReward.toFixed(1)}`,
565
+ data: {
566
+ strategyName: strategy.name,
567
+ strategyId: strategy.id,
568
+ riskTolerance: strategy.riskTolerance,
569
+ signalReason: signal.reason,
570
+ confidence: signal.confidence,
571
+ },
572
+ importance: 'high',
573
+ },
574
+ delay,
575
+ });
576
+ delay += rand(1000, 1800); // Slower: 1-1.8s
577
+
578
+ // 9. ORDER
579
+ const orderId = `ORD_${Date.now().toString(36).toUpperCase()}`;
580
+ const orderPrice = signal.direction === 'LONG'
581
+ ? price * (1 - rand(0.0001, 0.0005))
582
+ : price * (1 + rand(0.0001, 0.0005));
583
+
584
+ entries.push({
585
+ entry: {
586
+ strategyId: strategy.id,
587
+ strategyName: strategy.shortName,
588
+ type: 'ORDER',
589
+ message: `Submitting ${signal.direction} order | ${pair} @ $${formatPrice(orderPrice)} on ${chainLabel} | ID: ${orderId}`,
590
+ data: {
591
+ orderId,
592
+ pair,
593
+ side: signal.direction,
594
+ price: orderPrice,
595
+ leverage: decision.leverage,
596
+ chain,
597
+ chainLabel,
598
+ strategyName: strategy.name,
599
+ strategyContext: strategyContext,
600
+ signalReason: signal.reason,
601
+ },
602
+ importance: 'high',
603
+ },
604
+ delay,
605
+ });
606
+ delay += rand(2000, 4000); // Slower: 2-4s for order execution
607
+
608
+ // 10. FILLED
609
+ const fillPrice = orderPrice * (1 + rand(-0.0003, 0.0003));
610
+ const slippage = Math.abs(fillPrice - orderPrice) / orderPrice * 100;
611
+ entries.push({
612
+ entry: {
613
+ strategyId: strategy.id,
614
+ strategyName: strategy.shortName,
615
+ type: 'FILLED',
616
+ message: `Order FILLED | ${pair} ${signal.direction} @ $${formatPrice(fillPrice)} on ${chainLabel} | Slippage: ${slippage.toFixed(4)}% | ID: ${orderId}`,
617
+ data: {
618
+ orderId,
619
+ fillPrice,
620
+ slippage,
621
+ chain,
622
+ chainLabel,
623
+ strategyName: strategy.name,
624
+ executedBy: strategy.id,
625
+ },
626
+ importance: 'high',
627
+ },
628
+ delay,
629
+ });
630
+
631
+ // Update state with new position
632
+ const position: OpenPosition = {
633
+ id: orderId,
634
+ pair,
635
+ side: signal.direction as 'LONG' | 'SHORT',
636
+ entryPrice: fillPrice,
637
+ currentPrice: price,
638
+ size: decision.positionSize,
639
+ leverage: decision.leverage,
640
+ pnl: 0,
641
+ pnlPercent: 0,
642
+ };
643
+ state.openPositions = [...state.openPositions.slice(-2), position];
644
+ state.totalTrades++;
645
+ } else {
646
+ entries.push({
647
+ entry: {
648
+ strategyId: strategy.id,
649
+ strategyName: strategy.shortName,
650
+ type: 'DECISION',
651
+ message: `SKIP - ${decision.reason}`,
652
+ importance: 'medium',
653
+ },
654
+ delay,
655
+ });
656
+ }
657
+ }
658
+
659
+ // 11. PNL - Update open positions (slower)
660
+ if (state.openPositions.length > 0 && Math.random() < 0.5) {
661
+ delay += rand(1500, 2500); // Slower: 1.5-2.5s
662
+ let totalPositionPnl = 0;
663
+ const pnlParts: string[] = [];
664
+
665
+ for (const pos of state.openPositions) {
666
+ pos.currentPrice = this.simulatePrice(pos.pair);
667
+ const priceDiff = pos.side === 'LONG'
668
+ ? (pos.currentPrice - pos.entryPrice) / pos.entryPrice
669
+ : (pos.entryPrice - pos.currentPrice) / pos.entryPrice;
670
+ pos.pnlPercent = priceDiff * pos.leverage * 100;
671
+ pos.pnl = priceDiff * pos.leverage * pos.size;
672
+ totalPositionPnl += pos.pnl;
673
+ pnlParts.push(`${pos.pair} ${pos.side}: ${pos.pnlPercent >= 0 ? '+' : ''}${pos.pnlPercent.toFixed(2)}%`);
674
+ }
675
+
676
+ state.totalPnl += totalPositionPnl * rand(0.01, 0.05);
677
+
678
+ // Sometimes close a position
679
+ if (state.openPositions.length > 1 && Math.random() < 0.3) {
680
+ const closed = state.openPositions.shift()!;
681
+ const finalPnl = closed.pnlPercent;
682
+ if (finalPnl > 0) {
683
+ state.winRate = state.winRate * 0.95 + 0.05;
684
+ } else {
685
+ state.winRate = state.winRate * 0.95;
686
+ }
687
+ state.winRate = clamp(state.winRate, 0.35, 0.75);
688
+ pnlParts.push(`CLOSED ${closed.pair}: ${finalPnl >= 0 ? '+' : ''}${finalPnl.toFixed(2)}%`);
689
+ }
690
+
691
+ entries.push({
692
+ entry: {
693
+ strategyId: strategy.id,
694
+ strategyName: strategy.shortName,
695
+ type: 'PNL',
696
+ message: pnlParts.join(' | '),
697
+ data: { totalPnl: state.totalPnl, positions: state.openPositions.length },
698
+ importance: 'medium',
699
+ },
700
+ delay,
701
+ });
702
+ }
703
+
704
+ // 12. RISK - Periodic check (slower)
705
+ if (Math.random() < 0.2) {
706
+ delay += rand(1000, 2000); // Slower: 1-2s
707
+ const exposure = state.openPositions.reduce((sum, p) => sum + p.size * p.leverage, 0);
708
+ const maxDrawdown = rand(2, 12);
709
+ entries.push({
710
+ entry: {
711
+ strategyId: strategy.id,
712
+ strategyName: strategy.shortName,
713
+ type: 'RISK',
714
+ message: `Portfolio exposure: ${exposure.toFixed(1)}% | Max drawdown: ${maxDrawdown.toFixed(1)}% | Open positions: ${state.openPositions.length} | Win rate: ${(state.winRate * 100).toFixed(1)}%`,
715
+ importance: exposure > 80 ? 'high' : 'low',
716
+ },
717
+ delay,
718
+ });
719
+ }
720
+
721
+ // Emit entries with delays
722
+ for (const { entry, delay: d } of entries) {
723
+ setTimeout(() => {
724
+ if (this.running) {
725
+ this.emit({
726
+ ...entry,
727
+ id: genId(),
728
+ timestamp: Date.now(),
729
+ });
730
+ }
731
+ }, d);
732
+ }
733
+ }
734
+
735
+ private simulatePrice(pair: string): number {
736
+ const current = this.priceState.get(pair) || PAIR_PRICES[pair] || 50000;
737
+ const volatility = pair.includes('DOGE') ? 0.005 : pair.includes('BTC') ? 0.002 : 0.003;
738
+ const drift = rand(-volatility, volatility);
739
+ const newPrice = current * (1 + drift);
740
+ this.priceState.set(pair, newPrice);
741
+ return newPrice;
742
+ }
743
+
744
+ private generateIndicators(strategy: StrategyPersonality, price: number): IndicatorSnapshot {
745
+ const prev = this.indicatorState.get(strategy.id);
746
+ const prevRsi = prev?.rsi ?? strategy.rsiBias;
747
+
748
+ // RSI: mean-reverting around bias
749
+ const rsiMean = strategy.rsiBias;
750
+ const rsiDrift = rand(-8, 8);
751
+ const rsiReversion = (rsiMean - prevRsi) * 0.15;
752
+ const rsi = clamp(prevRsi + rsiDrift + rsiReversion, 8, 95);
753
+
754
+ // MACD: correlated with RSI
755
+ const macdBias = rsi > 65 ? 0.3 : rsi < 35 ? -0.3 : 0;
756
+ const prevHist = prev?.macd.histogram ?? 0;
757
+ const histogram = clamp(prevHist * 0.7 + rand(-0.5, 0.5) + macdBias, -2, 2);
758
+ const macdValue = histogram * rand(0.8, 1.5);
759
+ const macdSignal = macdValue - histogram;
760
+
761
+ // EMA: occasional crossovers
762
+ const prevShort = prev?.ema.short ?? price;
763
+ const prevLong = prev?.ema.long ?? price;
764
+ const emaShort = prevShort * 0.9 + price * 0.1;
765
+ const emaLong = prevLong * 0.95 + price * 0.05;
766
+ let crossover: 'golden' | 'death' | 'none' = 'none';
767
+ if (prevShort <= prevLong && emaShort > emaLong) crossover = 'golden';
768
+ else if (prevShort >= prevLong && emaShort < emaLong) crossover = 'death';
769
+
770
+ // Bollinger
771
+ const bbMiddle = price;
772
+ const bbWidth = price * rand(0.01, 0.04);
773
+ const bbUpper = bbMiddle + bbWidth;
774
+ const bbLower = bbMiddle - bbWidth;
775
+ const bbPosition = ((price - bbLower) / (bbUpper - bbLower)) * 100;
776
+
777
+ // Volume
778
+ const volRatio = rand(0.3, 2.5);
779
+ const volCurrent = rand(100000, 5000000);
780
+
781
+ return {
782
+ rsi,
783
+ macd: { value: macdValue, signal: macdSignal, histogram },
784
+ ema: { short: emaShort, long: emaLong, crossover },
785
+ bollinger: { upper: bbUpper, middle: bbMiddle, lower: bbLower, width: bbWidth / price, position: bbPosition },
786
+ volume: { current: volCurrent, average: volCurrent / volRatio, ratio: volRatio },
787
+ };
788
+ }
789
+
790
+ private generateThinkingProcess(strategy: StrategyPersonality, pair: string, price: number): string[] {
791
+ const thoughts: string[] = [];
792
+ const pairBase = pair.split('/')[0];
793
+
794
+ // Generate 1-3 thinking steps
795
+ const thinkingTemplates = [
796
+ `Analyzing ${pairBase} market structure...`,
797
+ `Checking ${strategy.primaryIndicators.join(', ')} confluence...`,
798
+ `Evaluating risk parameters for ${strategy.riskTolerance} tolerance...`,
799
+ `Scanning order book depth at $${formatPrice(price)}...`,
800
+ `Cross-referencing with historical patterns...`,
801
+ `Calculating optimal entry zone...`,
802
+ `Assessing market sentiment indicators...`,
803
+ `Monitoring whale activity on ${pairBase}...`,
804
+ `Comparing momentum across timeframes...`,
805
+ `Validating support/resistance levels...`,
806
+ ];
807
+
808
+ const numThoughts = randInt(1, 3);
809
+ const shuffled = [...thinkingTemplates].sort(() => Math.random() - 0.5);
810
+
811
+ for (let i = 0; i < numThoughts; i++) {
812
+ thoughts.push(shuffled[i]);
813
+ }
814
+
815
+ return thoughts;
816
+ }
817
+
818
+ private generateStrategyContext(
819
+ strategy: StrategyPersonality,
820
+ signal: { direction: string; confidence: number; reason: string },
821
+ indicators: IndicatorSnapshot,
822
+ pair: string
823
+ ): string {
824
+ const contexts = [];
825
+
826
+ // Strategy personality context
827
+ contexts.push(`[${strategy.name}]`);
828
+
829
+ // Risk context
830
+ const riskLevel = strategy.riskTolerance === 'high' ? 'aggressive' : strategy.riskTolerance === 'low' ? 'conservative' : 'balanced';
831
+ contexts.push(`Risk: ${riskLevel}`);
832
+
833
+ // Key indicator that triggered
834
+ if (indicators.rsi < 35 || indicators.rsi > 65) {
835
+ contexts.push(`RSI ${indicators.rsi < 35 ? 'oversold' : 'overbought'} (${indicators.rsi.toFixed(1)})`);
836
+ }
837
+ if (indicators.ema.crossover !== 'none') {
838
+ contexts.push(`EMA ${indicators.ema.crossover} cross`);
839
+ }
840
+ if (Math.abs(indicators.macd.histogram) > 0.3) {
841
+ contexts.push(`MACD ${indicators.macd.histogram > 0 ? 'bullish' : 'bearish'} momentum`);
842
+ }
843
+
844
+ // Confidence interpretation
845
+ const confLevel = signal.confidence > 0.7 ? 'HIGH' : signal.confidence > 0.5 ? 'MEDIUM' : 'LOW';
846
+ contexts.push(`Confidence: ${confLevel}`);
847
+
848
+ return contexts.join(' | ');
849
+ }
850
+
851
+ private generateAnalysis(strategy: StrategyPersonality, indicators: IndicatorSnapshot, pair: string): string {
852
+ const analyses = [];
853
+
854
+ if (indicators.rsi > 70) {
855
+ analyses.push(`RSI at ${indicators.rsi.toFixed(1)} - overbought territory, watching for reversal`);
856
+ } else if (indicators.rsi < 30) {
857
+ analyses.push(`RSI at ${indicators.rsi.toFixed(1)} - oversold, potential bounce setup`);
858
+ } else if (indicators.rsi > 55) {
859
+ analyses.push(`RSI trending bullish at ${indicators.rsi.toFixed(1)}`);
860
+ } else {
861
+ analyses.push(`RSI neutral at ${indicators.rsi.toFixed(1)}, no clear direction`);
862
+ }
863
+
864
+ if (indicators.macd.histogram > 0.5) {
865
+ analyses.push('MACD histogram expanding positive - momentum building');
866
+ } else if (indicators.macd.histogram < -0.5) {
867
+ analyses.push('MACD histogram expanding negative - bearish pressure');
868
+ }
869
+
870
+ if (indicators.ema.crossover === 'golden') {
871
+ analyses.push('EMA golden cross detected - strong bullish signal');
872
+ } else if (indicators.ema.crossover === 'death') {
873
+ analyses.push('EMA death cross detected - bearish warning');
874
+ }
875
+
876
+ if (indicators.bollinger.position > 90) {
877
+ analyses.push(`Price near upper Bollinger band (${indicators.bollinger.position.toFixed(0)}%) - potential resistance`);
878
+ } else if (indicators.bollinger.position < 10) {
879
+ analyses.push(`Price near lower Bollinger band (${indicators.bollinger.position.toFixed(0)}%) - potential support`);
880
+ }
881
+
882
+ if (indicators.volume.ratio > 1.8) {
883
+ analyses.push(`Volume spike ${indicators.volume.ratio.toFixed(1)}x average - high activity`);
884
+ }
885
+
886
+ return analyses.length > 0 ? analyses.join(' | ') : `${pair} consolidating - waiting for clearer setup`;
887
+ }
888
+
889
+ private evaluateSignal(
890
+ strategy: StrategyPersonality,
891
+ indicators: IndicatorSnapshot,
892
+ ): { direction: 'LONG' | 'SHORT' | 'HOLD'; confidence: number; reason: string } {
893
+ let bullScore = 0;
894
+ let bearScore = 0;
895
+ const reasons: string[] = [];
896
+
897
+ // RSI
898
+ if (indicators.rsi < 30) { bullScore += 2; reasons.push('RSI oversold'); }
899
+ else if (indicators.rsi < 40) { bullScore += 1; reasons.push('RSI low'); }
900
+ else if (indicators.rsi > 70) { bearScore += 2; reasons.push('RSI overbought'); }
901
+ else if (indicators.rsi > 60) { bearScore += 1; reasons.push('RSI high'); }
902
+
903
+ // MACD
904
+ if (indicators.macd.histogram > 0.3) { bullScore += 1.5; reasons.push('MACD bullish'); }
905
+ else if (indicators.macd.histogram < -0.3) { bearScore += 1.5; reasons.push('MACD bearish'); }
906
+
907
+ // EMA
908
+ if (indicators.ema.crossover === 'golden') { bullScore += 2.5; reasons.push('Golden cross'); }
909
+ else if (indicators.ema.crossover === 'death') { bearScore += 2.5; reasons.push('Death cross'); }
910
+ else if (indicators.ema.short > indicators.ema.long) { bullScore += 0.5; }
911
+ else { bearScore += 0.5; }
912
+
913
+ // Bollinger
914
+ if (indicators.bollinger.position < 15) { bullScore += 1; reasons.push('BB support'); }
915
+ else if (indicators.bollinger.position > 85) { bearScore += 1; reasons.push('BB resistance'); }
916
+
917
+ // Volume confirmation
918
+ if (indicators.volume.ratio > 1.5) {
919
+ if (bullScore > bearScore) bullScore += 1;
920
+ else bearScore += 1;
921
+ reasons.push('Volume confirms');
922
+ }
923
+
924
+ const netScore = bullScore - bearScore;
925
+ const confidence = Math.min(Math.abs(netScore) / 6, 0.95);
926
+ const threshold = strategy.riskTolerance === 'high' ? 1.5 : strategy.riskTolerance === 'medium' ? 2.0 : 2.5;
927
+
928
+ // Apply trade frequency filter
929
+ if (Math.random() > strategy.tradeFrequency) {
930
+ return { direction: 'HOLD', confidence: 0, reason: 'Cycle skip' };
931
+ }
932
+
933
+ if (netScore > threshold) {
934
+ return { direction: 'LONG', confidence, reason: reasons.slice(0, 3).join(', ') };
935
+ } else if (netScore < -threshold) {
936
+ return { direction: 'SHORT', confidence, reason: reasons.slice(0, 3).join(', ') };
937
+ }
938
+
939
+ return { direction: 'HOLD', confidence: 0, reason: 'No clear signal' };
940
+ }
941
+
942
+ private makeTradeDecision(
943
+ strategy: StrategyPersonality,
944
+ signal: { direction: string; confidence: number },
945
+ state: BotState,
946
+ ): { execute: boolean; positionSize: number; leverage: number; riskReward: number; reason: string } {
947
+ // Check position limits
948
+ if (state.openPositions.length >= 3) {
949
+ return { execute: false, positionSize: 0, leverage: 0, riskReward: 0, reason: 'Max positions reached (3)' };
950
+ }
951
+
952
+ // Confidence threshold
953
+ const minConfidence = strategy.riskTolerance === 'high' ? 0.3 : strategy.riskTolerance === 'medium' ? 0.45 : 0.6;
954
+ if (signal.confidence < minConfidence) {
955
+ return { execute: false, positionSize: 0, leverage: 0, riskReward: 0, reason: `Confidence too low (${(signal.confidence * 100).toFixed(0)}% < ${(minConfidence * 100).toFixed(0)}%)` };
956
+ }
957
+
958
+ const positionSize = rand(strategy.positionSizeMin, strategy.positionSizeMax);
959
+ const leverage = randInt(strategy.leverageMin, strategy.leverageMax);
960
+ const riskReward = rand(1.2, 3.5);
961
+
962
+ return { execute: true, positionSize, leverage, riskReward, reason: '' };
963
+ }
964
+ }
965
+
966
+ // ── Singleton Export ────────────────────────────────────────────────────────────
967
+
968
+ export const botSimulationEngine = new BotSimulationEngine();