249 lines
5.8 KiB
TypeScript
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;
|
|
}
|