@autonomaai/api-schemas 1.0.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.
- package/dist/index.d.ts +77 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +109 -0
- package/dist/index.js.map +1 -0
- package/dist/validators.d.ts +65 -0
- package/dist/validators.d.ts.map +1 -0
- package/dist/validators.js +541 -0
- package/dist/validators.js.map +1 -0
- package/openapi.yaml +1434 -0
- package/package.json +47 -0
- package/src/global.d.ts +12 -0
- package/src/index.ts +159 -0
- package/src/validators.ts +723 -0
|
@@ -0,0 +1,723 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema validation utilities for autonoma API schemas.
|
|
3
|
+
*
|
|
4
|
+
* Provides runtime validation for API requests and responses using JSON Schema.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import Ajv, { JSONSchemaType, ValidateFunction } from 'ajv';
|
|
8
|
+
import addFormats from 'ajv-formats';
|
|
9
|
+
|
|
10
|
+
// Initialize AJV with formats
|
|
11
|
+
const ajv = new Ajv({ allErrors: true, verbose: true });
|
|
12
|
+
addFormats(ajv);
|
|
13
|
+
|
|
14
|
+
// =============================================================================
|
|
15
|
+
// Schema Validation Results
|
|
16
|
+
// =============================================================================
|
|
17
|
+
|
|
18
|
+
export interface ValidationResult {
|
|
19
|
+
valid: boolean;
|
|
20
|
+
errors: ValidationError[];
|
|
21
|
+
data?: any;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface ValidationError {
|
|
25
|
+
field: string;
|
|
26
|
+
message: string;
|
|
27
|
+
value?: any;
|
|
28
|
+
code: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// =============================================================================
|
|
32
|
+
// Common Response Schemas
|
|
33
|
+
// =============================================================================
|
|
34
|
+
|
|
35
|
+
const successResponseSchema: JSONSchemaType<{
|
|
36
|
+
success: boolean;
|
|
37
|
+
message: string;
|
|
38
|
+
timestamp: string;
|
|
39
|
+
data?: any;
|
|
40
|
+
}> = {
|
|
41
|
+
type: 'object',
|
|
42
|
+
properties: {
|
|
43
|
+
success: { type: 'boolean' },
|
|
44
|
+
message: { type: 'string' },
|
|
45
|
+
timestamp: { type: 'string', format: 'date-time' },
|
|
46
|
+
data: { type: 'object', nullable: true }
|
|
47
|
+
},
|
|
48
|
+
required: ['success', 'message', 'timestamp'],
|
|
49
|
+
additionalProperties: false
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const errorResponseSchema: JSONSchemaType<{
|
|
53
|
+
success: boolean;
|
|
54
|
+
error: string;
|
|
55
|
+
detail?: string;
|
|
56
|
+
error_code?: string;
|
|
57
|
+
timestamp: string;
|
|
58
|
+
}> = {
|
|
59
|
+
type: 'object',
|
|
60
|
+
properties: {
|
|
61
|
+
success: { type: 'boolean' },
|
|
62
|
+
error: { type: 'string' },
|
|
63
|
+
detail: { type: 'string', nullable: true },
|
|
64
|
+
error_code: { type: 'string', nullable: true },
|
|
65
|
+
timestamp: { type: 'string', format: 'date-time' }
|
|
66
|
+
},
|
|
67
|
+
required: ['success', 'error', 'timestamp'],
|
|
68
|
+
additionalProperties: false
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// =============================================================================
|
|
72
|
+
// Controller Schemas
|
|
73
|
+
// =============================================================================
|
|
74
|
+
|
|
75
|
+
const createControllerRequestSchema: JSONSchemaType<{
|
|
76
|
+
name: string;
|
|
77
|
+
strategy: string;
|
|
78
|
+
trading_pair: string;
|
|
79
|
+
config: Record<string, any>;
|
|
80
|
+
metadata?: Record<string, any>;
|
|
81
|
+
}> = {
|
|
82
|
+
type: 'object',
|
|
83
|
+
properties: {
|
|
84
|
+
name: { type: 'string', minLength: 1, maxLength: 100 },
|
|
85
|
+
strategy: {
|
|
86
|
+
type: 'string',
|
|
87
|
+
enum: ['pure_market_making', 'avellaneda_market_making', 'twap', 'grid_trading', 'arbitrage']
|
|
88
|
+
},
|
|
89
|
+
trading_pair: { type: 'string', pattern: '^[A-Z0-9]+-[A-Z0-9]+$' },
|
|
90
|
+
config: { type: 'object' },
|
|
91
|
+
metadata: { type: 'object', nullable: true }
|
|
92
|
+
},
|
|
93
|
+
required: ['name', 'strategy', 'trading_pair', 'config'],
|
|
94
|
+
additionalProperties: false
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const controllerSchema: JSONSchemaType<{
|
|
98
|
+
id: string;
|
|
99
|
+
name: string;
|
|
100
|
+
strategy: string;
|
|
101
|
+
trading_pair: string;
|
|
102
|
+
status: string;
|
|
103
|
+
config: Record<string, any>;
|
|
104
|
+
metadata?: Record<string, any>;
|
|
105
|
+
created_at: string;
|
|
106
|
+
updated_at: string;
|
|
107
|
+
}> = {
|
|
108
|
+
type: 'object',
|
|
109
|
+
properties: {
|
|
110
|
+
id: { type: 'string', pattern: '^[a-zA-Z0-9_-]+$' },
|
|
111
|
+
name: { type: 'string' },
|
|
112
|
+
strategy: { type: 'string' },
|
|
113
|
+
trading_pair: { type: 'string' },
|
|
114
|
+
status: {
|
|
115
|
+
type: 'string',
|
|
116
|
+
enum: ['active', 'inactive', 'error', 'starting', 'stopping']
|
|
117
|
+
},
|
|
118
|
+
config: { type: 'object' },
|
|
119
|
+
metadata: { type: 'object', nullable: true },
|
|
120
|
+
created_at: { type: 'string', format: 'date-time' },
|
|
121
|
+
updated_at: { type: 'string', format: 'date-time' }
|
|
122
|
+
},
|
|
123
|
+
required: ['id', 'name', 'strategy', 'trading_pair', 'status', 'config', 'created_at', 'updated_at'],
|
|
124
|
+
additionalProperties: false
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// =============================================================================
|
|
128
|
+
// Trading Signal Schemas (Unified)
|
|
129
|
+
// =============================================================================
|
|
130
|
+
|
|
131
|
+
const tradingSignalSchema: JSONSchemaType<{
|
|
132
|
+
type: string;
|
|
133
|
+
side: string;
|
|
134
|
+
amount: number;
|
|
135
|
+
price?: number;
|
|
136
|
+
order_type: string;
|
|
137
|
+
time_in_force?: string;
|
|
138
|
+
metadata?: Record<string, any>;
|
|
139
|
+
}> = {
|
|
140
|
+
type: 'object',
|
|
141
|
+
properties: {
|
|
142
|
+
type: {
|
|
143
|
+
type: 'string',
|
|
144
|
+
enum: ['place_order', 'cancel_order', 'modify_order', 'close_position']
|
|
145
|
+
},
|
|
146
|
+
side: {
|
|
147
|
+
type: 'string',
|
|
148
|
+
enum: ['buy', 'sell']
|
|
149
|
+
},
|
|
150
|
+
amount: { type: 'number', minimum: 0, exclusiveMinimum: 0 },
|
|
151
|
+
price: { type: 'number', minimum: 0, exclusiveMinimum: 0, nullable: true },
|
|
152
|
+
order_type: {
|
|
153
|
+
type: 'string',
|
|
154
|
+
enum: ['market', 'limit', 'stop_loss', 'take_profit']
|
|
155
|
+
},
|
|
156
|
+
time_in_force: {
|
|
157
|
+
type: 'string',
|
|
158
|
+
enum: ['GTC', 'IOC', 'FOK'],
|
|
159
|
+
nullable: true
|
|
160
|
+
},
|
|
161
|
+
metadata: { type: 'object', nullable: true }
|
|
162
|
+
},
|
|
163
|
+
required: ['type', 'side', 'amount', 'order_type'],
|
|
164
|
+
additionalProperties: false
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const executeSignalRequestSchema: JSONSchemaType<{
|
|
168
|
+
controller_id: string;
|
|
169
|
+
signal: {
|
|
170
|
+
type: string;
|
|
171
|
+
side: string;
|
|
172
|
+
amount: number;
|
|
173
|
+
price?: number;
|
|
174
|
+
order_type: string;
|
|
175
|
+
time_in_force?: string;
|
|
176
|
+
metadata?: Record<string, any>;
|
|
177
|
+
};
|
|
178
|
+
execution_options?: {
|
|
179
|
+
dry_run?: boolean;
|
|
180
|
+
timeout_seconds?: number;
|
|
181
|
+
};
|
|
182
|
+
}> = {
|
|
183
|
+
type: 'object',
|
|
184
|
+
properties: {
|
|
185
|
+
controller_id: { type: 'string', pattern: '^[a-zA-Z0-9_-]+$' },
|
|
186
|
+
signal: tradingSignalSchema,
|
|
187
|
+
execution_options: {
|
|
188
|
+
type: 'object',
|
|
189
|
+
properties: {
|
|
190
|
+
dry_run: { type: 'boolean', nullable: true },
|
|
191
|
+
timeout_seconds: { type: 'integer', minimum: 1, maximum: 300, nullable: true }
|
|
192
|
+
},
|
|
193
|
+
required: [],
|
|
194
|
+
additionalProperties: false,
|
|
195
|
+
nullable: true
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
required: ['controller_id', 'signal'],
|
|
199
|
+
additionalProperties: false
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// =============================================================================
|
|
203
|
+
// DeFi Yield Optimization Schemas
|
|
204
|
+
// =============================================================================
|
|
205
|
+
|
|
206
|
+
const yieldOpportunitySchema: JSONSchemaType<{
|
|
207
|
+
protocol: string;
|
|
208
|
+
pool: string;
|
|
209
|
+
apy: number;
|
|
210
|
+
tvl: number;
|
|
211
|
+
risk_score: number;
|
|
212
|
+
chain: string;
|
|
213
|
+
asset: string;
|
|
214
|
+
lockup_period?: number;
|
|
215
|
+
minimum_deposit?: number;
|
|
216
|
+
confidence_score: number;
|
|
217
|
+
last_updated: string;
|
|
218
|
+
}> = {
|
|
219
|
+
type: 'object',
|
|
220
|
+
properties: {
|
|
221
|
+
protocol: { type: 'string', minLength: 1 },
|
|
222
|
+
pool: { type: 'string', minLength: 1 },
|
|
223
|
+
apy: { type: 'number', minimum: 0, maximum: 10000 }, // Max 10,000% APY for safety
|
|
224
|
+
tvl: { type: 'number', minimum: 0 },
|
|
225
|
+
risk_score: { type: 'number', minimum: 0, maximum: 100 },
|
|
226
|
+
chain: {
|
|
227
|
+
type: 'string',
|
|
228
|
+
enum: ['ethereum', 'arbitrum', 'polygon', 'base', 'optimism', 'bsc', 'avalanche']
|
|
229
|
+
},
|
|
230
|
+
asset: { type: 'string', minLength: 1 },
|
|
231
|
+
lockup_period: { type: 'number', minimum: 0, nullable: true },
|
|
232
|
+
minimum_deposit: { type: 'number', minimum: 0, nullable: true },
|
|
233
|
+
confidence_score: { type: 'number', minimum: 0, maximum: 100 },
|
|
234
|
+
last_updated: { type: 'string', format: 'date-time' }
|
|
235
|
+
},
|
|
236
|
+
required: ['protocol', 'pool', 'apy', 'tvl', 'risk_score', 'chain', 'asset', 'confidence_score', 'last_updated'],
|
|
237
|
+
additionalProperties: false
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const yieldSearchRequestSchema: JSONSchemaType<{
|
|
241
|
+
search_criteria: {
|
|
242
|
+
asset?: string;
|
|
243
|
+
chain?: string[];
|
|
244
|
+
min_apy?: number;
|
|
245
|
+
max_risk?: number;
|
|
246
|
+
min_tvl?: number;
|
|
247
|
+
protocols?: string[];
|
|
248
|
+
limit?: number;
|
|
249
|
+
};
|
|
250
|
+
risk_tolerance: 'conservative' | 'moderate' | 'aggressive';
|
|
251
|
+
time_horizon: 'short' | 'medium' | 'long';
|
|
252
|
+
}> = {
|
|
253
|
+
type: 'object',
|
|
254
|
+
properties: {
|
|
255
|
+
search_criteria: {
|
|
256
|
+
type: 'object',
|
|
257
|
+
properties: {
|
|
258
|
+
asset: { type: 'string', nullable: true },
|
|
259
|
+
chain: {
|
|
260
|
+
type: 'array',
|
|
261
|
+
items: { type: 'string' },
|
|
262
|
+
nullable: true
|
|
263
|
+
},
|
|
264
|
+
min_apy: { type: 'number', minimum: 0, nullable: true },
|
|
265
|
+
max_risk: { type: 'number', minimum: 0, maximum: 100, nullable: true },
|
|
266
|
+
min_tvl: { type: 'number', minimum: 0, nullable: true },
|
|
267
|
+
protocols: {
|
|
268
|
+
type: 'array',
|
|
269
|
+
items: { type: 'string' },
|
|
270
|
+
nullable: true
|
|
271
|
+
},
|
|
272
|
+
limit: { type: 'integer', minimum: 1, maximum: 100, nullable: true }
|
|
273
|
+
},
|
|
274
|
+
required: [],
|
|
275
|
+
additionalProperties: false
|
|
276
|
+
},
|
|
277
|
+
risk_tolerance: {
|
|
278
|
+
type: 'string',
|
|
279
|
+
enum: ['conservative', 'moderate', 'aggressive']
|
|
280
|
+
},
|
|
281
|
+
time_horizon: {
|
|
282
|
+
type: 'string',
|
|
283
|
+
enum: ['short', 'medium', 'long']
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
required: ['search_criteria', 'risk_tolerance', 'time_horizon'],
|
|
287
|
+
additionalProperties: false
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
const allocationOptimizationSchema: JSONSchemaType<{
|
|
291
|
+
strategy: 'max_sharpe' | 'risk_parity' | 'mean_variance';
|
|
292
|
+
allocations: Record<string, number>;
|
|
293
|
+
expected_return: number;
|
|
294
|
+
expected_risk: number;
|
|
295
|
+
sharpe_ratio: number;
|
|
296
|
+
constraints: {
|
|
297
|
+
max_protocol_allocation: number;
|
|
298
|
+
max_single_asset: number;
|
|
299
|
+
min_liquidity: number;
|
|
300
|
+
max_risk_score: number;
|
|
301
|
+
required_chains?: string[];
|
|
302
|
+
};
|
|
303
|
+
rebalancing_threshold: number;
|
|
304
|
+
}> = {
|
|
305
|
+
type: 'object',
|
|
306
|
+
properties: {
|
|
307
|
+
strategy: {
|
|
308
|
+
type: 'string',
|
|
309
|
+
enum: ['max_sharpe', 'risk_parity', 'mean_variance']
|
|
310
|
+
},
|
|
311
|
+
allocations: {
|
|
312
|
+
type: 'object',
|
|
313
|
+
additionalProperties: { type: 'number', minimum: 0, maximum: 1 },
|
|
314
|
+
required: []
|
|
315
|
+
},
|
|
316
|
+
expected_return: { type: 'number' },
|
|
317
|
+
expected_risk: { type: 'number', minimum: 0 },
|
|
318
|
+
sharpe_ratio: { type: 'number' },
|
|
319
|
+
constraints: {
|
|
320
|
+
type: 'object',
|
|
321
|
+
properties: {
|
|
322
|
+
max_protocol_allocation: { type: 'number', minimum: 0, maximum: 1 },
|
|
323
|
+
max_single_asset: { type: 'number', minimum: 0, maximum: 1 },
|
|
324
|
+
min_liquidity: { type: 'number', minimum: 0 },
|
|
325
|
+
max_risk_score: { type: 'number', minimum: 0, maximum: 100 },
|
|
326
|
+
required_chains: {
|
|
327
|
+
type: 'array',
|
|
328
|
+
items: { type: 'string' },
|
|
329
|
+
nullable: true
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
required: ['max_protocol_allocation', 'max_single_asset', 'min_liquidity', 'max_risk_score'],
|
|
333
|
+
additionalProperties: false
|
|
334
|
+
},
|
|
335
|
+
rebalancing_threshold: { type: 'number', minimum: 0, maximum: 1 }
|
|
336
|
+
},
|
|
337
|
+
required: ['strategy', 'allocations', 'expected_return', 'expected_risk', 'sharpe_ratio', 'constraints', 'rebalancing_threshold'],
|
|
338
|
+
additionalProperties: false
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
const defiSignalSchema: JSONSchemaType<{
|
|
342
|
+
type: string;
|
|
343
|
+
protocol: string;
|
|
344
|
+
action: 'deposit' | 'withdraw' | 'compound' | 'rebalance';
|
|
345
|
+
amount: number;
|
|
346
|
+
asset: string;
|
|
347
|
+
target_apy?: number;
|
|
348
|
+
max_slippage?: number;
|
|
349
|
+
metadata?: Record<string, any>;
|
|
350
|
+
}> = {
|
|
351
|
+
type: 'object',
|
|
352
|
+
properties: {
|
|
353
|
+
type: {
|
|
354
|
+
type: 'string',
|
|
355
|
+
enum: ['defi_yield_action']
|
|
356
|
+
},
|
|
357
|
+
protocol: { type: 'string', minLength: 1 },
|
|
358
|
+
action: {
|
|
359
|
+
type: 'string',
|
|
360
|
+
enum: ['deposit', 'withdraw', 'compound', 'rebalance']
|
|
361
|
+
},
|
|
362
|
+
amount: { type: 'number', minimum: 0, exclusiveMinimum: 0 },
|
|
363
|
+
asset: { type: 'string', minLength: 1 },
|
|
364
|
+
target_apy: { type: 'number', minimum: 0, nullable: true },
|
|
365
|
+
max_slippage: { type: 'number', minimum: 0, maximum: 1, nullable: true },
|
|
366
|
+
metadata: { type: 'object', nullable: true }
|
|
367
|
+
},
|
|
368
|
+
required: ['type', 'protocol', 'action', 'amount', 'asset'],
|
|
369
|
+
additionalProperties: false
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
const portfolioRiskAssessmentSchema: JSONSchemaType<{
|
|
373
|
+
var_95: number;
|
|
374
|
+
var_99: number;
|
|
375
|
+
expected_shortfall: number;
|
|
376
|
+
portfolio_volatility: number;
|
|
377
|
+
correlation_risk: number;
|
|
378
|
+
concentration_risk: number;
|
|
379
|
+
liquidity_risk: number;
|
|
380
|
+
overall_risk_score: number;
|
|
381
|
+
recommendations: string[];
|
|
382
|
+
}> = {
|
|
383
|
+
type: 'object',
|
|
384
|
+
properties: {
|
|
385
|
+
var_95: { type: 'number' },
|
|
386
|
+
var_99: { type: 'number' },
|
|
387
|
+
expected_shortfall: { type: 'number' },
|
|
388
|
+
portfolio_volatility: { type: 'number', minimum: 0 },
|
|
389
|
+
correlation_risk: { type: 'number', minimum: 0, maximum: 100 },
|
|
390
|
+
concentration_risk: { type: 'number', minimum: 0, maximum: 100 },
|
|
391
|
+
liquidity_risk: { type: 'number', minimum: 0, maximum: 100 },
|
|
392
|
+
overall_risk_score: { type: 'number', minimum: 0, maximum: 100 },
|
|
393
|
+
recommendations: {
|
|
394
|
+
type: 'array',
|
|
395
|
+
items: { type: 'string' }
|
|
396
|
+
}
|
|
397
|
+
},
|
|
398
|
+
required: ['var_95', 'var_99', 'expected_shortfall', 'portfolio_volatility', 'correlation_risk', 'concentration_risk', 'liquidity_risk', 'overall_risk_score', 'recommendations'],
|
|
399
|
+
additionalProperties: false
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
// =============================================================================
|
|
403
|
+
// Updated Compiled Validators
|
|
404
|
+
// =============================================================================
|
|
405
|
+
|
|
406
|
+
const validators = {
|
|
407
|
+
// Existing validators
|
|
408
|
+
successResponse: ajv.compile(successResponseSchema),
|
|
409
|
+
errorResponse: ajv.compile(errorResponseSchema),
|
|
410
|
+
createControllerRequest: ajv.compile(createControllerRequestSchema),
|
|
411
|
+
controller: ajv.compile(controllerSchema),
|
|
412
|
+
tradingSignal: ajv.compile(tradingSignalSchema),
|
|
413
|
+
executeSignalRequest: ajv.compile(executeSignalRequestSchema),
|
|
414
|
+
|
|
415
|
+
// New DeFi validators
|
|
416
|
+
yieldOpportunity: ajv.compile(yieldOpportunitySchema),
|
|
417
|
+
yieldSearchRequest: ajv.compile(yieldSearchRequestSchema),
|
|
418
|
+
allocationOptimization: ajv.compile(allocationOptimizationSchema),
|
|
419
|
+
defiSignal: ajv.compile(defiSignalSchema),
|
|
420
|
+
portfolioRiskAssessment: ajv.compile(portfolioRiskAssessmentSchema)
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
// =============================================================================
|
|
424
|
+
// Validation Functions
|
|
425
|
+
// =============================================================================
|
|
426
|
+
|
|
427
|
+
function formatValidationErrors(validator: ValidateFunction): ValidationError[] {
|
|
428
|
+
if (!validator.errors) return [];
|
|
429
|
+
|
|
430
|
+
return validator.errors.map((error: any) => ({
|
|
431
|
+
field: error.instancePath || error.schemaPath,
|
|
432
|
+
message: error.message || 'Validation error',
|
|
433
|
+
value: error.data,
|
|
434
|
+
code: error.keyword || 'VALIDATION_ERROR'
|
|
435
|
+
}));
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Validate a success response
|
|
440
|
+
*/
|
|
441
|
+
export function validateSuccessResponse(data: any): ValidationResult {
|
|
442
|
+
const valid = validators.successResponse(data);
|
|
443
|
+
return {
|
|
444
|
+
valid,
|
|
445
|
+
errors: valid ? [] : formatValidationErrors(validators.successResponse),
|
|
446
|
+
data: valid ? data : null
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Validate an error response
|
|
452
|
+
*/
|
|
453
|
+
export function validateErrorResponse(data: any): ValidationResult {
|
|
454
|
+
const valid = validators.errorResponse(data);
|
|
455
|
+
return {
|
|
456
|
+
valid,
|
|
457
|
+
errors: valid ? [] : formatValidationErrors(validators.errorResponse),
|
|
458
|
+
data: valid ? data : null
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Validate create controller request
|
|
464
|
+
*/
|
|
465
|
+
export function validateCreateControllerRequest(data: any): ValidationResult {
|
|
466
|
+
const valid = validators.createControllerRequest(data);
|
|
467
|
+
return {
|
|
468
|
+
valid,
|
|
469
|
+
errors: valid ? [] : formatValidationErrors(validators.createControllerRequest),
|
|
470
|
+
data: valid ? data : null
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Validate controller object
|
|
476
|
+
*/
|
|
477
|
+
export function validateController(data: any): ValidationResult {
|
|
478
|
+
const valid = validators.controller(data);
|
|
479
|
+
return {
|
|
480
|
+
valid,
|
|
481
|
+
errors: valid ? [] : formatValidationErrors(validators.controller),
|
|
482
|
+
data: valid ? data : null
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Validate trading signal
|
|
488
|
+
*/
|
|
489
|
+
export function validateTradingSignal(data: any): ValidationResult {
|
|
490
|
+
const valid = validators.tradingSignal(data);
|
|
491
|
+
return {
|
|
492
|
+
valid,
|
|
493
|
+
errors: valid ? [] : formatValidationErrors(validators.tradingSignal),
|
|
494
|
+
data: valid ? data : null
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Validate execute signal request (UNIFIED)
|
|
500
|
+
*/
|
|
501
|
+
export function validateExecuteSignalRequest(data: any): ValidationResult {
|
|
502
|
+
const valid = validators.executeSignalRequest(data);
|
|
503
|
+
return {
|
|
504
|
+
valid,
|
|
505
|
+
errors: valid ? [] : formatValidationErrors(validators.executeSignalRequest),
|
|
506
|
+
data: valid ? data : null
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// =============================================================================
|
|
511
|
+
// DeFi Yield Validation Functions
|
|
512
|
+
// =============================================================================
|
|
513
|
+
|
|
514
|
+
export function validateYieldOpportunity(data: any): ValidationResult {
|
|
515
|
+
const isValid = validators.yieldOpportunity(data);
|
|
516
|
+
return {
|
|
517
|
+
valid: isValid,
|
|
518
|
+
errors: isValid ? [] : formatValidationErrors(validators.yieldOpportunity),
|
|
519
|
+
data: isValid ? data : null
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
export function validateYieldSearchRequest(data: any): ValidationResult {
|
|
524
|
+
const isValid = validators.yieldSearchRequest(data);
|
|
525
|
+
return {
|
|
526
|
+
valid: isValid,
|
|
527
|
+
errors: isValid ? [] : formatValidationErrors(validators.yieldSearchRequest),
|
|
528
|
+
data: isValid ? data : null
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
export function validateAllocationOptimization(data: any): ValidationResult {
|
|
533
|
+
const isValid = validators.allocationOptimization(data);
|
|
534
|
+
return {
|
|
535
|
+
valid: isValid,
|
|
536
|
+
errors: isValid ? [] : formatValidationErrors(validators.allocationOptimization),
|
|
537
|
+
data: isValid ? data : null
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
export function validateDeFiSignal(data: any): ValidationResult {
|
|
542
|
+
const isValid = validators.defiSignal(data);
|
|
543
|
+
return {
|
|
544
|
+
valid: isValid,
|
|
545
|
+
errors: isValid ? [] : formatValidationErrors(validators.defiSignal),
|
|
546
|
+
data: isValid ? data : null
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
export function validatePortfolioRiskAssessment(data: any): ValidationResult {
|
|
551
|
+
const isValid = validators.portfolioRiskAssessment(data);
|
|
552
|
+
return {
|
|
553
|
+
valid: isValid,
|
|
554
|
+
errors: isValid ? [] : formatValidationErrors(validators.portfolioRiskAssessment),
|
|
555
|
+
data: isValid ? data : null
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// =============================================================================
|
|
560
|
+
// DeFi Response Helpers
|
|
561
|
+
// =============================================================================
|
|
562
|
+
|
|
563
|
+
export function createYieldOpportunityResponse(
|
|
564
|
+
opportunities: any[],
|
|
565
|
+
metadata?: any
|
|
566
|
+
): any {
|
|
567
|
+
return validateSuccessResponse({
|
|
568
|
+
success: true,
|
|
569
|
+
message: 'Yield opportunities found successfully',
|
|
570
|
+
timestamp: new Date().toISOString(),
|
|
571
|
+
data: {
|
|
572
|
+
opportunities,
|
|
573
|
+
count: opportunities.length,
|
|
574
|
+
metadata: {
|
|
575
|
+
search_timestamp: new Date().toISOString(),
|
|
576
|
+
...metadata
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
export function createAllocationOptimizationResponse(
|
|
583
|
+
optimization: any,
|
|
584
|
+
metadata?: any
|
|
585
|
+
): any {
|
|
586
|
+
return validateSuccessResponse({
|
|
587
|
+
success: true,
|
|
588
|
+
message: 'Portfolio optimization completed successfully',
|
|
589
|
+
timestamp: new Date().toISOString(),
|
|
590
|
+
data: {
|
|
591
|
+
optimization,
|
|
592
|
+
metadata: {
|
|
593
|
+
optimization_timestamp: new Date().toISOString(),
|
|
594
|
+
...metadata
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
export function createDeFiSignalResponse(
|
|
601
|
+
execution_result: any,
|
|
602
|
+
metadata?: any
|
|
603
|
+
): any {
|
|
604
|
+
return validateSuccessResponse({
|
|
605
|
+
success: true,
|
|
606
|
+
message: 'DeFi signal executed successfully',
|
|
607
|
+
timestamp: new Date().toISOString(),
|
|
608
|
+
data: {
|
|
609
|
+
execution_result,
|
|
610
|
+
metadata: {
|
|
611
|
+
execution_timestamp: new Date().toISOString(),
|
|
612
|
+
...metadata
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// =============================================================================
|
|
619
|
+
// Validation Middleware Helpers
|
|
620
|
+
// =============================================================================
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* Express middleware factory for request validation
|
|
624
|
+
*/
|
|
625
|
+
export function createValidationMiddleware(validator: (data: any) => ValidationResult) {
|
|
626
|
+
return (req: any, res: any, next: any) => {
|
|
627
|
+
const result = validator(req.body);
|
|
628
|
+
|
|
629
|
+
if (!result.valid) {
|
|
630
|
+
return res.status(400).json({
|
|
631
|
+
success: false,
|
|
632
|
+
error: 'Validation failed',
|
|
633
|
+
detail: 'Request body does not match required schema',
|
|
634
|
+
validation_errors: result.errors,
|
|
635
|
+
timestamp: new Date().toISOString()
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
next();
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* FastAPI dependency for request validation (Python equivalent helper)
|
|
645
|
+
*/
|
|
646
|
+
export function generatePythonValidator(schemaName: string): string {
|
|
647
|
+
return `
|
|
648
|
+
def validate_${schemaName}(data: dict) -> ValidationResult:
|
|
649
|
+
"""
|
|
650
|
+
Validate ${schemaName} data against unified schema.
|
|
651
|
+
|
|
652
|
+
This should be replaced with actual Python Pydantic validation
|
|
653
|
+
based on the OpenAPI specification.
|
|
654
|
+
"""
|
|
655
|
+
# TODO: Implement with Pydantic models generated from OpenAPI spec
|
|
656
|
+
return {"valid": True, "errors": []}
|
|
657
|
+
`;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// =============================================================================
|
|
661
|
+
// Schema Migration Helpers
|
|
662
|
+
// =============================================================================
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* Convert legacy execute signal format to unified format
|
|
666
|
+
*/
|
|
667
|
+
export function migrateLegacyExecuteSignal(legacyData: any): any {
|
|
668
|
+
// Handle old format: { signal_type, parameters }
|
|
669
|
+
if (legacyData.signal_type && legacyData.parameters) {
|
|
670
|
+
return {
|
|
671
|
+
controller_id: legacyData.controller_id,
|
|
672
|
+
signal: {
|
|
673
|
+
type: legacyData.signal_type,
|
|
674
|
+
...legacyData.parameters
|
|
675
|
+
}
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// Handle old format with different signal structure
|
|
680
|
+
if (legacyData.signal && typeof legacyData.signal === 'object') {
|
|
681
|
+
const signal = legacyData.signal;
|
|
682
|
+
|
|
683
|
+
// Ensure required fields are present and normalized
|
|
684
|
+
return {
|
|
685
|
+
controller_id: legacyData.controller_id,
|
|
686
|
+
signal: {
|
|
687
|
+
type: signal.type || 'place_order',
|
|
688
|
+
side: signal.side,
|
|
689
|
+
amount: signal.amount,
|
|
690
|
+
price: signal.price,
|
|
691
|
+
order_type: signal.order_type || 'limit',
|
|
692
|
+
time_in_force: signal.time_in_force || 'GTC',
|
|
693
|
+
metadata: signal.metadata
|
|
694
|
+
}
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
// Already in correct format
|
|
699
|
+
return legacyData;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
/**
|
|
703
|
+
* Convert unified format to legacy format for backward compatibility
|
|
704
|
+
*/
|
|
705
|
+
export function convertToLegacyFormat(unifiedData: any, format: 'signal_type' | 'nested'): any {
|
|
706
|
+
if (format === 'signal_type') {
|
|
707
|
+
return {
|
|
708
|
+
controller_id: unifiedData.controller_id,
|
|
709
|
+
signal_type: unifiedData.signal.type,
|
|
710
|
+
parameters: {
|
|
711
|
+
side: unifiedData.signal.side,
|
|
712
|
+
amount: unifiedData.signal.amount,
|
|
713
|
+
price: unifiedData.signal.price,
|
|
714
|
+
order_type: unifiedData.signal.order_type,
|
|
715
|
+
time_in_force: unifiedData.signal.time_in_force,
|
|
716
|
+
...unifiedData.signal.metadata
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
// Default: nested format (current unified format)
|
|
722
|
+
return unifiedData;
|
|
723
|
+
}
|