@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.
@@ -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
+ }