@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,283 @@
1
+ /**
2
+ * ONE SDK - AI Quant Console Hook
3
+ * Combined hook for all AI trading console data
4
+ */
5
+
6
+ import { useState, useEffect, useCallback, useMemo } from 'react';
7
+ import { useBotSimulation } from './useBotSimulation';
8
+ import { useAIPositions } from './useAIPositions';
9
+ import { useAIDecisions } from './useAIDecisions';
10
+ import { useAIRiskStatus } from './useAIRiskStatus';
11
+ import type {
12
+ AIAgent,
13
+ AIPosition,
14
+ AIDecision,
15
+ RiskStatus,
16
+ ConsoleMetrics,
17
+ AIQuantConsoleOptions,
18
+ } from '../types/console';
19
+ import { DEFAULT_CONSOLE_METRICS, AGENT_STATUS_COLORS } from '../types/console';
20
+ import type { BotLogEntry, BotState, StrategyPersonality } from '../services/forex/BotSimulationEngine';
21
+
22
+ // ── Types ─────────────────────────────────────────────────────────────────────
23
+
24
+ export interface UseAIQuantConsoleResult {
25
+ // Strategies and Agents
26
+ strategies: StrategyPersonality[];
27
+ agents: AIAgent[];
28
+ // Logs
29
+ logs: BotLogEntry[];
30
+ logsByStrategy: Map<string, BotLogEntry[]>;
31
+ // Bot States
32
+ botStates: Map<string, BotState>;
33
+ // Positions
34
+ positions: AIPosition[];
35
+ openPositions: AIPosition[];
36
+ // Decisions
37
+ decisions: AIDecision[];
38
+ recentDecisions: AIDecision[];
39
+ // Risk
40
+ riskStatus: RiskStatus | null;
41
+ // Metrics
42
+ metrics: ConsoleMetrics;
43
+ // State
44
+ isRunning: boolean;
45
+ isLoading: boolean;
46
+ error: string | null;
47
+ // Controls
48
+ start: (strategyIds?: string[]) => void;
49
+ stop: (strategyIds?: string[]) => void;
50
+ clearLogs: () => void;
51
+ emitBootSequence: () => void;
52
+ refresh: () => Promise<void>;
53
+ // Agent helpers
54
+ getAgent: (strategyId: string) => AIAgent | undefined;
55
+ getAgentLogs: (strategyId: string) => BotLogEntry[];
56
+ getAgentPositions: (strategyId: string) => AIPosition[];
57
+ getAgentDecisions: (strategyId: string) => AIDecision[];
58
+ }
59
+
60
+ // ── Hook Implementation ───────────────────────────────────────────────────────
61
+
62
+ export function useAIQuantConsole(options?: AIQuantConsoleOptions): UseAIQuantConsoleResult {
63
+ const simulation = options?.simulation ?? true;
64
+ const pollInterval = options?.pollInterval ?? 5000;
65
+ const maxLogs = options?.maxLogs ?? 500;
66
+ const strategyIds = options?.strategyIds;
67
+
68
+ // Sub-hooks
69
+ const botSim = useBotSimulation({
70
+ maxLogs,
71
+ strategyIds,
72
+ autoStart: false,
73
+ });
74
+
75
+ const positionsHook = useAIPositions({
76
+ strategyIds,
77
+ pollInterval,
78
+ simulation,
79
+ });
80
+
81
+ const decisionsHook = useAIDecisions({
82
+ strategyIds,
83
+ pollInterval,
84
+ simulation,
85
+ limit: 100,
86
+ });
87
+
88
+ const riskHook = useAIRiskStatus({
89
+ pollInterval,
90
+ simulation,
91
+ });
92
+
93
+ // Transform bot states to agents
94
+ const agents = useMemo((): AIAgent[] => {
95
+ const agentList: AIAgent[] = [];
96
+
97
+ for (const strategy of botSim.strategies) {
98
+ const state = botSim.botStates.get(strategy.id);
99
+ const strategyPositions = positionsHook.positions.filter(p => p.strategyId === strategy.id);
100
+ const strategyDecisions = decisionsHook.decisions.filter(d => d.strategyId === strategy.id);
101
+ const openPos = strategyPositions.filter(p => p.status === 'open');
102
+
103
+ const totalPnl = strategyPositions.reduce((sum, p) => sum + p.pnl, 0);
104
+ const exposure = openPos.reduce((sum, p) => sum + p.size, 0);
105
+ const winCount = strategyPositions.filter(p => p.pnl > 0).length;
106
+ const totalTrades = strategyPositions.length;
107
+
108
+ // Determine agent risk level based on exposure and drawdown
109
+ let riskLevel: 'low' | 'medium' | 'high' | 'critical' = 'low';
110
+ if (strategy.riskTolerance === 'high') riskLevel = 'medium';
111
+ if (exposure > 50000) riskLevel = 'high';
112
+ if (state && state.openPositions.length >= 3) riskLevel = 'high';
113
+
114
+ agentList.push({
115
+ id: strategy.id,
116
+ strategyId: strategy.id,
117
+ name: strategy.name,
118
+ shortName: strategy.shortName,
119
+ color: strategy.color,
120
+ status: state?.isRunning ? 'active' : botSim.isRunning ? 'idle' : 'paused',
121
+ totalPnl: state?.totalPnl ?? totalPnl,
122
+ pnlToday: totalPnl * 0.3, // Simulated
123
+ winRate: state?.winRate ?? (totalTrades > 0 ? winCount / totalTrades : 0.5),
124
+ totalTrades: state?.totalTrades ?? totalTrades,
125
+ tradesToday: Math.floor((state?.totalTrades ?? totalTrades) * 0.2),
126
+ currentPair: state?.currentPair,
127
+ currentPrice: state?.currentPrice,
128
+ lastSignal: state?.lastSignal,
129
+ lastSignalConfidence: state?.lastSignalConfidence,
130
+ lastActivity: state ? Date.now() : undefined,
131
+ openPositions: openPos.length,
132
+ totalExposure: exposure,
133
+ riskLevel,
134
+ drawdown: Math.random() * 5, // Simulated
135
+ riskTolerance: strategy.riskTolerance,
136
+ primaryIndicators: strategy.primaryIndicators,
137
+ preferredPairs: strategy.preferredPairs,
138
+ leverageRange: [strategy.leverageMin, strategy.leverageMax],
139
+ });
140
+ }
141
+
142
+ return agentList;
143
+ }, [botSim.strategies, botSim.botStates, botSim.isRunning, positionsHook.positions, decisionsHook.decisions]);
144
+
145
+ // Calculate combined metrics
146
+ const metrics = useMemo((): ConsoleMetrics => {
147
+ const positions = positionsHook.positions;
148
+ const openPos = positions.filter(p => p.status === 'open');
149
+ const closedPos = positions.filter(p => p.status === 'closed');
150
+
151
+ const totalPnl = positions.reduce((sum, p) => sum + p.pnl, 0);
152
+ const unrealizedPnl = openPos.reduce((sum, p) => sum + p.pnl, 0);
153
+ const realizedPnl = closedPos.reduce((sum, p) => sum + p.pnl, 0);
154
+
155
+ const wins = positions.filter(p => p.pnl > 0);
156
+ const losses = positions.filter(p => p.pnl < 0);
157
+
158
+ const avgWin = wins.length > 0
159
+ ? wins.reduce((sum, p) => sum + p.pnl, 0) / wins.length
160
+ : 0;
161
+ const avgLoss = losses.length > 0
162
+ ? Math.abs(losses.reduce((sum, p) => sum + p.pnl, 0)) / losses.length
163
+ : 0;
164
+
165
+ const profitFactor = avgLoss > 0 ? avgWin / avgLoss : avgWin > 0 ? Infinity : 0;
166
+
167
+ const totalExposure = openPos.reduce((sum, p) => sum + p.size, 0);
168
+ const avgLeverage = openPos.length > 0
169
+ ? openPos.reduce((sum, p) => sum + p.leverage, 0) / openPos.length
170
+ : 0;
171
+
172
+ // Strategy metrics
173
+ const strategyMetrics: Record<string, { pnl: number; trades: number; winRate: number; exposure: number }> = {};
174
+ for (const agent of agents) {
175
+ const strategyPos = positions.filter(p => p.strategyId === agent.strategyId);
176
+ const strategyOpen = strategyPos.filter(p => p.status === 'open');
177
+ const strategyWins = strategyPos.filter(p => p.pnl > 0);
178
+
179
+ strategyMetrics[agent.strategyId] = {
180
+ pnl: strategyPos.reduce((sum, p) => sum + p.pnl, 0),
181
+ trades: strategyPos.length,
182
+ winRate: strategyPos.length > 0 ? strategyWins.length / strategyPos.length : 0,
183
+ exposure: strategyOpen.reduce((sum, p) => sum + p.size, 0),
184
+ };
185
+ }
186
+
187
+ return {
188
+ nav: 100000 + totalPnl,
189
+ navChange24h: totalPnl * 0.3,
190
+ navChangePercent24h: (totalPnl * 0.3) / 100000 * 100,
191
+ totalPnl,
192
+ realizedPnl,
193
+ unrealizedPnl,
194
+ pnlToday: totalPnl * 0.3,
195
+ pnl7d: totalPnl * 0.7,
196
+ pnl30d: totalPnl,
197
+ totalTrades: positions.length,
198
+ tradesToday: Math.floor(positions.length * 0.2),
199
+ winRate: positions.length > 0 ? wins.length / positions.length : 0,
200
+ winCount: wins.length,
201
+ lossCount: losses.length,
202
+ avgWin,
203
+ avgLoss,
204
+ profitFactor,
205
+ openPositions: openPos.length,
206
+ totalExposure,
207
+ avgLeverage,
208
+ strategyMetrics,
209
+ };
210
+ }, [positionsHook.positions, agents]);
211
+
212
+ // Controls
213
+ const start = useCallback((ids?: string[]) => {
214
+ botSim.start(ids || strategyIds);
215
+ }, [botSim, strategyIds]);
216
+
217
+ const stop = useCallback((ids?: string[]) => {
218
+ botSim.stop(ids);
219
+ }, [botSim]);
220
+
221
+ const clearLogs = useCallback(() => {
222
+ botSim.clearLogs();
223
+ }, [botSim]);
224
+
225
+ const refresh = useCallback(async () => {
226
+ await Promise.all([
227
+ positionsHook.refresh(),
228
+ decisionsHook.refresh(),
229
+ riskHook.refresh(),
230
+ ]);
231
+ }, [positionsHook, decisionsHook, riskHook]);
232
+
233
+ // Agent helpers
234
+ const getAgent = useCallback((strategyId: string) =>
235
+ agents.find(a => a.strategyId === strategyId),
236
+ [agents]
237
+ );
238
+
239
+ const getAgentLogs = useCallback((strategyId: string) =>
240
+ botSim.logsByStrategy.get(strategyId) || [],
241
+ [botSim.logsByStrategy]
242
+ );
243
+
244
+ const getAgentPositions = useCallback((strategyId: string) =>
245
+ positionsHook.positions.filter(p => p.strategyId === strategyId),
246
+ [positionsHook.positions]
247
+ );
248
+
249
+ const getAgentDecisions = useCallback((strategyId: string) =>
250
+ decisionsHook.decisions.filter(d => d.strategyId === strategyId),
251
+ [decisionsHook.decisions]
252
+ );
253
+
254
+ // Combined loading/error state
255
+ const isLoading = positionsHook.isLoading || decisionsHook.isLoading || riskHook.isLoading;
256
+ const error = positionsHook.error || decisionsHook.error || riskHook.error;
257
+
258
+ return {
259
+ strategies: botSim.strategies,
260
+ agents,
261
+ logs: botSim.logs,
262
+ logsByStrategy: botSim.logsByStrategy,
263
+ botStates: botSim.botStates,
264
+ positions: positionsHook.positions,
265
+ openPositions: positionsHook.openPositions,
266
+ decisions: decisionsHook.decisions,
267
+ recentDecisions: decisionsHook.recentDecisions,
268
+ riskStatus: riskHook.riskStatus,
269
+ metrics,
270
+ isRunning: botSim.isRunning,
271
+ isLoading,
272
+ error,
273
+ start,
274
+ stop,
275
+ clearLogs,
276
+ emitBootSequence: botSim.emitBootSequence,
277
+ refresh,
278
+ getAgent,
279
+ getAgentLogs,
280
+ getAgentPositions,
281
+ getAgentDecisions,
282
+ };
283
+ }
@@ -0,0 +1,276 @@
1
+ /**
2
+ * ONE SDK - AI Risk Status Hook
3
+ * Provides React hook for monitoring trading risk status and limits
4
+ */
5
+
6
+ import { useState, useEffect, useCallback, useMemo } from 'react';
7
+ import type { RiskStatus, RiskLevel, TradingStatus } from '../types/console';
8
+ import { DEFAULT_RISK_STATUS, calculateRiskLevel } from '../types/console';
9
+
10
+ // ── Types ─────────────────────────────────────────────────────────────────────
11
+
12
+ export interface UseAIRiskStatusOptions {
13
+ strategyId?: string;
14
+ pollInterval?: number;
15
+ simulation?: boolean;
16
+ }
17
+
18
+ export interface UseAIRiskStatusResult {
19
+ riskStatus: RiskStatus;
20
+ isLoading: boolean;
21
+ error: string | null;
22
+ refresh: () => Promise<void>;
23
+ // Computed values
24
+ isWithinLimits: boolean;
25
+ canTrade: boolean;
26
+ riskLevel: RiskLevel;
27
+ tradingStatus: TradingStatus;
28
+ warnings: string[];
29
+ // Actions
30
+ pauseTrading: () => Promise<boolean>;
31
+ resumeTrading: () => Promise<boolean>;
32
+ resetDailyLimits: () => Promise<boolean>;
33
+ }
34
+
35
+ // ── Simulation Data Generator ─────────────────────────────────────────────────
36
+
37
+ function generateSimulatedRiskStatus(): RiskStatus {
38
+ const totalExposure = Math.random() * 80000 + 10000;
39
+ const maxExposure = 100000;
40
+ const exposurePercent = (totalExposure / maxExposure) * 100;
41
+
42
+ const dailyPnl = (Math.random() - 0.3) * 3000;
43
+ const dailyPnlLimit = 5000;
44
+ const dailyPnlPercent = (Math.abs(dailyPnl) / dailyPnlLimit) * 100;
45
+
46
+ const currentDrawdown = Math.random() * 10;
47
+ const maxDrawdown = 15;
48
+ const drawdownPercent = (currentDrawdown / maxDrawdown) * 100;
49
+
50
+ const openPositions = Math.floor(Math.random() * 8);
51
+ const maxPositions = 10;
52
+
53
+ const dailyTradeCount = Math.floor(Math.random() * 30);
54
+ const dailyTradeLimit = 50;
55
+
56
+ const riskLevel = calculateRiskLevel(exposurePercent, drawdownPercent, dailyPnlPercent);
57
+
58
+ const warnings: string[] = [];
59
+ if (exposurePercent > 70) warnings.push('High portfolio exposure');
60
+ if (drawdownPercent > 60) warnings.push('Approaching max drawdown');
61
+ if (dailyPnlPercent > 80 && dailyPnl < 0) warnings.push('Daily loss limit warning');
62
+ if (openPositions >= maxPositions - 1) warnings.push('Position limit nearly reached');
63
+
64
+ let tradingStatus: TradingStatus = 'active';
65
+ if (riskLevel === 'critical') tradingStatus = 'stopped';
66
+ else if (riskLevel === 'high' && warnings.length > 1) tradingStatus = 'paused';
67
+
68
+ const strategyRisks: Record<string, { exposure: number; drawdown: number; riskLevel: RiskLevel }> = {
69
+ 'balanced-01': {
70
+ exposure: Math.random() * 30000,
71
+ drawdown: Math.random() * 5,
72
+ riskLevel: Math.random() > 0.7 ? 'medium' : 'low',
73
+ },
74
+ 'conservative-01': {
75
+ exposure: Math.random() * 20000,
76
+ drawdown: Math.random() * 3,
77
+ riskLevel: 'low',
78
+ },
79
+ 'aggressive-01': {
80
+ exposure: Math.random() * 40000,
81
+ drawdown: Math.random() * 8,
82
+ riskLevel: Math.random() > 0.5 ? 'high' : 'medium',
83
+ },
84
+ };
85
+
86
+ return {
87
+ timestamp: Date.now(),
88
+ totalExposure,
89
+ maxExposure,
90
+ exposurePercent,
91
+ dailyPnl,
92
+ dailyPnlLimit,
93
+ dailyPnlPercent,
94
+ dailyTradeCount,
95
+ dailyTradeLimit,
96
+ currentDrawdown,
97
+ maxDrawdown,
98
+ drawdownPercent,
99
+ openPositions,
100
+ maxPositions,
101
+ riskLevel,
102
+ tradingStatus,
103
+ warnings,
104
+ strategyRisks,
105
+ };
106
+ }
107
+
108
+ // ── Hook Implementation ───────────────────────────────────────────────────────
109
+
110
+ export function useAIRiskStatus(options?: UseAIRiskStatusOptions): UseAIRiskStatusResult {
111
+ const [riskStatus, setRiskStatus] = useState<RiskStatus>(DEFAULT_RISK_STATUS);
112
+ const [isLoading, setIsLoading] = useState(false);
113
+ const [error, setError] = useState<string | null>(null);
114
+
115
+ const fetchRiskStatus = useCallback(async () => {
116
+ setIsLoading(true);
117
+ setError(null);
118
+
119
+ try {
120
+ if (options?.simulation) {
121
+ const simulated = generateSimulatedRiskStatus();
122
+ setRiskStatus(simulated);
123
+ } else {
124
+ let path = '/api/v1/ai-quant/risk-status';
125
+ if (options?.strategyId) {
126
+ path += `?strategyId=${options.strategyId}`;
127
+ }
128
+
129
+ const res = await fetch(`https://api.one23.io${path}`);
130
+ if (res.ok) {
131
+ const data = await res.json();
132
+ if (data) {
133
+ setRiskStatus(mapRiskStatus(data));
134
+ }
135
+ }
136
+ }
137
+ } catch (err) {
138
+ setError(err instanceof Error ? err.message : 'Failed to fetch risk status');
139
+ } finally {
140
+ setIsLoading(false);
141
+ }
142
+ }, [options?.simulation, options?.strategyId]);
143
+
144
+ // Initial fetch and polling
145
+ useEffect(() => {
146
+ fetchRiskStatus();
147
+ if (options?.pollInterval) {
148
+ const timer = setInterval(fetchRiskStatus, options.pollInterval);
149
+ return () => clearInterval(timer);
150
+ }
151
+ }, [fetchRiskStatus, options?.pollInterval]);
152
+
153
+ // Actions
154
+ const pauseTrading = useCallback(async (): Promise<boolean> => {
155
+ try {
156
+ if (options?.simulation) {
157
+ setRiskStatus(prev => ({ ...prev, tradingStatus: 'paused' }));
158
+ return true;
159
+ }
160
+ const res = await fetch('https://api.one23.io/api/v1/ai-quant/trading/pause', {
161
+ method: 'POST',
162
+ });
163
+ if (res.ok) {
164
+ setRiskStatus(prev => ({ ...prev, tradingStatus: 'paused' }));
165
+ return true;
166
+ }
167
+ return false;
168
+ } catch {
169
+ return false;
170
+ }
171
+ }, [options?.simulation]);
172
+
173
+ const resumeTrading = useCallback(async (): Promise<boolean> => {
174
+ try {
175
+ if (options?.simulation) {
176
+ setRiskStatus(prev => ({ ...prev, tradingStatus: 'active' }));
177
+ return true;
178
+ }
179
+ const res = await fetch('https://api.one23.io/api/v1/ai-quant/trading/resume', {
180
+ method: 'POST',
181
+ });
182
+ if (res.ok) {
183
+ setRiskStatus(prev => ({ ...prev, tradingStatus: 'active' }));
184
+ return true;
185
+ }
186
+ return false;
187
+ } catch {
188
+ return false;
189
+ }
190
+ }, [options?.simulation]);
191
+
192
+ const resetDailyLimits = useCallback(async (): Promise<boolean> => {
193
+ try {
194
+ if (options?.simulation) {
195
+ setRiskStatus(prev => ({
196
+ ...prev,
197
+ dailyPnl: 0,
198
+ dailyPnlPercent: 0,
199
+ dailyTradeCount: 0,
200
+ warnings: prev.warnings.filter(w => !w.includes('Daily')),
201
+ }));
202
+ return true;
203
+ }
204
+ const res = await fetch('https://api.one23.io/api/v1/ai-quant/risk/reset-daily', {
205
+ method: 'POST',
206
+ });
207
+ return res.ok;
208
+ } catch {
209
+ return false;
210
+ }
211
+ }, [options?.simulation]);
212
+
213
+ // Computed values
214
+ const isWithinLimits = useMemo(() => {
215
+ return (
216
+ riskStatus.exposurePercent < 90 &&
217
+ riskStatus.drawdownPercent < 90 &&
218
+ riskStatus.dailyPnlPercent < 90 &&
219
+ riskStatus.openPositions < riskStatus.maxPositions
220
+ );
221
+ }, [riskStatus]);
222
+
223
+ const canTrade = useMemo(() => {
224
+ return riskStatus.tradingStatus === 'active' && isWithinLimits;
225
+ }, [riskStatus.tradingStatus, isWithinLimits]);
226
+
227
+ return {
228
+ riskStatus,
229
+ isLoading,
230
+ error,
231
+ refresh: fetchRiskStatus,
232
+ isWithinLimits,
233
+ canTrade,
234
+ riskLevel: riskStatus.riskLevel,
235
+ tradingStatus: riskStatus.tradingStatus,
236
+ warnings: riskStatus.warnings,
237
+ pauseTrading,
238
+ resumeTrading,
239
+ resetDailyLimits,
240
+ };
241
+ }
242
+
243
+ // ── Helper Functions ──────────────────────────────────────────────────────────
244
+
245
+ function mapRiskStatus(raw: any): RiskStatus {
246
+ const exposurePercent = raw.maxExposure > 0
247
+ ? (raw.totalExposure / raw.maxExposure) * 100
248
+ : 0;
249
+ const dailyPnlPercent = raw.dailyPnlLimit > 0
250
+ ? (Math.abs(raw.dailyPnl) / raw.dailyPnlLimit) * 100
251
+ : 0;
252
+ const drawdownPercent = raw.maxDrawdown > 0
253
+ ? (raw.currentDrawdown / raw.maxDrawdown) * 100
254
+ : 0;
255
+
256
+ return {
257
+ timestamp: raw.timestamp ?? Date.now(),
258
+ totalExposure: raw.totalExposure ?? raw.total_exposure ?? 0,
259
+ maxExposure: raw.maxExposure ?? raw.max_exposure ?? 100000,
260
+ exposurePercent,
261
+ dailyPnl: raw.dailyPnl ?? raw.daily_pnl ?? 0,
262
+ dailyPnlLimit: raw.dailyPnlLimit ?? raw.daily_pnl_limit ?? 5000,
263
+ dailyPnlPercent,
264
+ dailyTradeCount: raw.dailyTradeCount ?? raw.daily_trade_count ?? 0,
265
+ dailyTradeLimit: raw.dailyTradeLimit ?? raw.daily_trade_limit ?? 50,
266
+ currentDrawdown: raw.currentDrawdown ?? raw.current_drawdown ?? 0,
267
+ maxDrawdown: raw.maxDrawdown ?? raw.max_drawdown ?? 15,
268
+ drawdownPercent,
269
+ openPositions: raw.openPositions ?? raw.open_positions ?? 0,
270
+ maxPositions: raw.maxPositions ?? raw.max_positions ?? 10,
271
+ riskLevel: raw.riskLevel ?? raw.risk_level ?? calculateRiskLevel(exposurePercent, drawdownPercent, dailyPnlPercent),
272
+ tradingStatus: raw.tradingStatus ?? raw.trading_status ?? 'active',
273
+ warnings: raw.warnings ?? [],
274
+ strategyRisks: raw.strategyRisks ?? raw.strategy_risks,
275
+ };
276
+ }