302 lines
7.1 KiB
TypeScript
302 lines
7.1 KiB
TypeScript
/**
|
|
* ML Controller
|
|
* Handles ML Engine integration endpoints
|
|
*/
|
|
|
|
import { Request, Response, NextFunction } from 'express';
|
|
import {
|
|
mlIntegrationService,
|
|
TimeHorizon,
|
|
SignalType,
|
|
} from '../services/ml-integration.service';
|
|
|
|
// ============================================================================
|
|
// Types
|
|
// ============================================================================
|
|
|
|
// Use Request directly - user is already declared globally in auth.middleware.ts
|
|
type AuthRequest = Request;
|
|
|
|
// ============================================================================
|
|
// Health & Status
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Get ML Engine health status
|
|
*/
|
|
export async function getHealth(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const health = await mlIntegrationService.getHealth();
|
|
|
|
res.json({
|
|
success: true,
|
|
data: health,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check ML Engine connection
|
|
*/
|
|
export async function checkConnection(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const isConnected = await mlIntegrationService.checkConnection();
|
|
|
|
res.json({
|
|
success: true,
|
|
data: { connected: isConnected },
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Signals
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Get trading signal for a symbol
|
|
*/
|
|
export async function getSignal(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const { symbol } = req.params;
|
|
const { timeHorizon = 'intraday' } = req.query;
|
|
|
|
const signal = await mlIntegrationService.getSignal(
|
|
symbol,
|
|
timeHorizon as TimeHorizon
|
|
);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: signal,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get signals for multiple symbols
|
|
*/
|
|
export async function getSignals(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const { symbols, timeHorizon = 'intraday' } = 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;
|
|
}
|
|
|
|
const signals = await mlIntegrationService.getSignals(
|
|
symbols,
|
|
timeHorizon as TimeHorizon
|
|
);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: signals,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get historical signals
|
|
*/
|
|
export async function getHistoricalSignals(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const { symbol } = req.params;
|
|
const { startTime, endTime, limit, signalType } = req.query;
|
|
|
|
const signals = await mlIntegrationService.getHistoricalSignals(symbol, {
|
|
startTime: startTime ? new Date(startTime as string) : undefined,
|
|
endTime: endTime ? new Date(endTime as string) : undefined,
|
|
limit: limit ? Number(limit) : undefined,
|
|
signalType: signalType as SignalType | undefined,
|
|
});
|
|
|
|
res.json({
|
|
success: true,
|
|
data: signals,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Predictions
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Get price prediction
|
|
*/
|
|
export async function getPrediction(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const { symbol } = req.params;
|
|
const { horizonMinutes = 90 } = req.query;
|
|
|
|
const prediction = await mlIntegrationService.getPrediction(
|
|
symbol,
|
|
Number(horizonMinutes)
|
|
);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: prediction,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get AMD phase prediction
|
|
*/
|
|
export async function getAMDPhase(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const { symbol } = req.params;
|
|
|
|
const amdPhase = await mlIntegrationService.getAMDPhase(symbol);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: amdPhase,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Indicators
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Get technical indicators
|
|
*/
|
|
export async function getIndicators(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const { symbol } = req.params;
|
|
|
|
const indicators = await mlIntegrationService.getIndicators(symbol);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: indicators,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Backtesting
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Run backtest
|
|
*/
|
|
export async function runBacktest(req: AuthRequest, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const userId = req.user?.id;
|
|
if (!userId) {
|
|
res.status(401).json({
|
|
success: false,
|
|
error: { message: 'Unauthorized', code: 'UNAUTHORIZED' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
const { symbol, startDate, endDate, initialCapital, strategy, params } = req.body;
|
|
|
|
if (!symbol || !startDate || !endDate) {
|
|
res.status(400).json({
|
|
success: false,
|
|
error: { message: 'Symbol, startDate, and endDate are required', code: 'VALIDATION_ERROR' },
|
|
});
|
|
return;
|
|
}
|
|
|
|
const result = await mlIntegrationService.runBacktest(symbol, {
|
|
startDate: new Date(startDate),
|
|
endDate: new Date(endDate),
|
|
initialCapital,
|
|
strategy,
|
|
params,
|
|
});
|
|
|
|
res.json({
|
|
success: true,
|
|
data: result,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Models (Admin)
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Get available models
|
|
*/
|
|
export async function getModels(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const models = await mlIntegrationService.getModels();
|
|
|
|
res.json({
|
|
success: true,
|
|
data: models,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Trigger model retraining
|
|
*/
|
|
export async function triggerRetraining(req: AuthRequest, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
// TODO: Add admin check
|
|
const { symbol } = req.body;
|
|
|
|
const result = await mlIntegrationService.triggerRetraining(symbol);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: result,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get retraining job status
|
|
*/
|
|
export async function getRetrainingStatus(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const { jobId } = req.params;
|
|
|
|
const status = await mlIntegrationService.getRetrainingStatus(jobId);
|
|
|
|
res.json({
|
|
success: true,
|
|
data: status,
|
|
});
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|