trading-platform/apps/backend/src/modules/ml/controllers/ml-overlay.controller.ts

249 lines
5.8 KiB
TypeScript

/**
* ML Overlay Controller
* Handles chart overlay endpoints for trading visualization
*/
import { Request, Response, NextFunction } from 'express';
import { mlOverlayService, OverlayConfig } from '../services/ml-overlay.service';
// ============================================================================
// Overlay Endpoints
// ============================================================================
/**
* Get complete chart overlay for a symbol
*/
export async function getChartOverlay(
req: Request,
res: Response,
next: NextFunction
): Promise<void> {
try {
const { symbol } = req.params;
const config = parseOverlayConfig(req.query);
const overlay = await mlOverlayService.getChartOverlay(symbol.toUpperCase(), config);
res.json({
success: true,
data: overlay,
});
} catch (error) {
next(error);
}
}
/**
* Get overlays for multiple symbols
*/
export async function getBatchOverlays(
req: Request,
res: Response,
next: NextFunction
): Promise<void> {
try {
const { symbols } = req.body;
const config = parseOverlayConfig(req.body);
if (!symbols || !Array.isArray(symbols) || symbols.length === 0) {
res.status(400).json({
success: false,
error: { message: 'Symbols array is required', code: 'VALIDATION_ERROR' },
});
return;
}
if (symbols.length > 20) {
res.status(400).json({
success: false,
error: { message: 'Maximum 20 symbols allowed per request', code: 'VALIDATION_ERROR' },
});
return;
}
const overlays = await mlOverlayService.getBatchOverlays(
symbols.map((s: string) => s.toUpperCase()),
config
);
// Convert Map to object for JSON response
const result: Record<string, unknown> = {};
overlays.forEach((value, key) => {
result[key] = value;
});
res.json({
success: true,
data: result,
});
} catch (error) {
next(error);
}
}
/**
* Get price levels only
*/
export async function getPriceLevels(
req: Request,
res: Response,
next: NextFunction
): Promise<void> {
try {
const { symbol } = req.params;
const levels = await mlOverlayService.getPriceLevels(symbol.toUpperCase());
res.json({
success: true,
data: {
symbol: symbol.toUpperCase(),
levels,
timestamp: new Date().toISOString(),
},
});
} catch (error) {
next(error);
}
}
/**
* Get signal markers
*/
export async function getSignalMarkers(
req: Request,
res: Response,
next: NextFunction
): Promise<void> {
try {
const { symbol } = req.params;
const limit = Math.min(Number(req.query.limit) || 20, 100);
const markers = await mlOverlayService.getSignalMarkers(symbol.toUpperCase(), limit);
res.json({
success: true,
data: {
symbol: symbol.toUpperCase(),
markers,
timestamp: new Date().toISOString(),
},
});
} catch (error) {
next(error);
}
}
/**
* Get AMD phase overlay
*/
export async function getAMDPhaseOverlay(
req: Request,
res: Response,
next: NextFunction
): Promise<void> {
try {
const { symbol } = req.params;
const amdPhase = await mlOverlayService.getAMDPhaseOverlay(symbol.toUpperCase());
res.json({
success: true,
data: {
symbol: symbol.toUpperCase(),
...amdPhase,
timestamp: new Date().toISOString(),
},
});
} catch (error) {
next(error);
}
}
/**
* Get prediction bands
*/
export async function getPredictionBands(
req: Request,
res: Response,
next: NextFunction
): Promise<void> {
try {
const { symbol } = req.params;
const horizonMinutes = Math.min(Number(req.query.horizon) || 90, 480);
const intervals = Math.min(Number(req.query.intervals) || 10, 50);
const bands = await mlOverlayService.getPredictionBands(
symbol.toUpperCase(),
horizonMinutes,
intervals
);
res.json({
success: true,
data: {
symbol: symbol.toUpperCase(),
horizonMinutes,
bands,
timestamp: new Date().toISOString(),
},
});
} catch (error) {
next(error);
}
}
/**
* Clear overlay cache
*/
export async function clearCache(
req: Request,
res: Response,
next: NextFunction
): Promise<void> {
try {
const { symbol } = req.params;
mlOverlayService.clearCache(symbol?.toUpperCase());
res.json({
success: true,
message: symbol ? `Cache cleared for ${symbol.toUpperCase()}` : 'All cache cleared',
});
} catch (error) {
next(error);
}
}
// ============================================================================
// Helper Functions
// ============================================================================
function parseOverlayConfig(query: Record<string, unknown>): Partial<OverlayConfig> {
const config: Partial<OverlayConfig> = {};
if (query.showPriceLevels !== undefined) {
config.showPriceLevels = query.showPriceLevels === 'true' || query.showPriceLevels === true;
}
if (query.showTrendLines !== undefined) {
config.showTrendLines = query.showTrendLines === 'true' || query.showTrendLines === true;
}
if (query.showSignalMarkers !== undefined) {
config.showSignalMarkers = query.showSignalMarkers === 'true' || query.showSignalMarkers === true;
}
if (query.showZones !== undefined) {
config.showZones = query.showZones === 'true' || query.showZones === true;
}
if (query.showPredictionBands !== undefined) {
config.showPredictionBands = query.showPredictionBands === 'true' || query.showPredictionBands === true;
}
if (query.showIndicators !== undefined) {
config.showIndicators = query.showIndicators === 'true' || query.showIndicators === true;
}
if (query.showAMDPhase !== undefined) {
config.showAMDPhase = query.showAMDPhase === 'true' || query.showAMDPhase === true;
}
return config;
}