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

254 lines
7.5 KiB
TypeScript

/**
* Paper Trading Controller
* Handles paper trading account and position endpoints
*/
import type { Request, Response, NextFunction } from 'express';
import { paperTradingService, TradeDirection } from '../services/paper-trading.service';
import type { AuthenticatedRequest } from '../../../core/guards/auth.guard';
// ============================================================================
// Account Endpoints
// ============================================================================
export async function getAccount(req: Request, res: Response, next: NextFunction): Promise<void> {
try {
const authReq = req as AuthenticatedRequest;
const account = await paperTradingService.getOrCreateAccount(authReq.user.id);
res.json({ success: true, data: account });
} catch (error) {
next(error);
}
}
export async function getAccounts(req: Request, res: Response, next: NextFunction): Promise<void> {
try {
const authReq = req as AuthenticatedRequest;
const accounts = await paperTradingService.getUserAccounts(authReq.user.id);
res.json({ success: true, data: accounts });
} catch (error) {
next(error);
}
}
export async function createAccount(req: Request, res: Response, next: NextFunction): Promise<void> {
try {
const authReq = req as AuthenticatedRequest;
const { name, initialBalance, currency } = req.body;
const account = await paperTradingService.createAccount(authReq.user.id, {
name,
initialBalance,
currency,
});
res.status(201).json({ success: true, data: account });
} catch (error) {
next(error);
}
}
export async function resetAccount(req: Request, res: Response, next: NextFunction): Promise<void> {
try {
const authReq = req as AuthenticatedRequest;
const { accountId } = req.params;
const account = await paperTradingService.resetAccount(accountId, authReq.user.id);
if (!account) {
res.status(404).json({ success: false, error: 'Account not found or unauthorized' });
return;
}
res.json({ success: true, data: account, message: 'Account reset successfully' });
} catch (error) {
next(error);
}
}
export async function getAccountSummary(req: Request, res: Response, next: NextFunction): Promise<void> {
try {
const authReq = req as AuthenticatedRequest;
const { accountId } = req.params;
const summary = await paperTradingService.getAccountSummary(authReq.user.id, accountId);
if (!summary) {
res.status(404).json({ success: false, error: 'Account not found' });
return;
}
res.json({ success: true, data: summary });
} catch (error) {
next(error);
}
}
// ============================================================================
// Position Endpoints
// ============================================================================
export async function openPosition(req: Request, res: Response, next: NextFunction): Promise<void> {
try {
const authReq = req as AuthenticatedRequest;
const { symbol, direction, lotSize, entryPrice, stopLoss, takeProfit } = req.body;
if (!symbol || !direction || !lotSize) {
res.status(400).json({
success: false,
error: 'Symbol, direction, and lotSize are required',
});
return;
}
const validDirections: TradeDirection[] = ['long', 'short'];
if (!validDirections.includes(direction)) {
res.status(400).json({
success: false,
error: `Invalid direction. Must be one of: ${validDirections.join(', ')}`,
});
return;
}
if (lotSize <= 0) {
res.status(400).json({
success: false,
error: 'Lot size must be greater than 0',
});
return;
}
const position = await paperTradingService.openPosition(authReq.user.id, {
symbol,
direction,
lotSize,
entryPrice,
stopLoss,
takeProfit,
});
res.status(201).json({ success: true, data: position });
} catch (error) {
next(error);
}
}
export async function closePosition(req: Request, res: Response, next: NextFunction): Promise<void> {
try {
const authReq = req as AuthenticatedRequest;
const { positionId } = req.params;
const { exitPrice, closeReason } = req.body;
const position = await paperTradingService.closePosition(positionId, authReq.user.id, {
exitPrice,
closeReason,
});
if (!position) {
res.status(404).json({ success: false, error: 'Position not found or already closed' });
return;
}
res.json({ success: true, data: position });
} catch (error) {
next(error);
}
}
export async function getPosition(req: Request, res: Response, next: NextFunction): Promise<void> {
try {
const authReq = req as AuthenticatedRequest;
const { positionId } = req.params;
const position = await paperTradingService.getPosition(positionId, authReq.user.id);
if (!position) {
res.status(404).json({ success: false, error: 'Position not found' });
return;
}
res.json({ success: true, data: position });
} catch (error) {
next(error);
}
}
export async function getPositions(req: Request, res: Response, next: NextFunction): Promise<void> {
try {
const authReq = req as AuthenticatedRequest;
const { accountId, status, symbol, limit } = req.query;
const positions = await paperTradingService.getPositions(authReq.user.id, {
accountId: accountId as string,
status: status as 'open' | 'closed' | 'pending',
symbol: symbol as string,
limit: limit ? parseInt(limit as string, 10) : undefined,
});
res.json({ success: true, data: positions });
} catch (error) {
next(error);
}
}
export async function updatePosition(req: Request, res: Response, next: NextFunction): Promise<void> {
try {
const authReq = req as AuthenticatedRequest;
const { positionId } = req.params;
const { stopLoss, takeProfit } = req.body;
const position = await paperTradingService.updatePosition(positionId, authReq.user.id, {
stopLoss,
takeProfit,
});
if (!position) {
res.status(404).json({ success: false, error: 'Position not found or not open' });
return;
}
res.json({ success: true, data: position });
} catch (error) {
next(error);
}
}
// ============================================================================
// Trade History & Analytics
// ============================================================================
export async function getTradeHistory(req: Request, res: Response, next: NextFunction): Promise<void> {
try {
const authReq = req as AuthenticatedRequest;
const { accountId, symbol, startDate, endDate, limit } = req.query;
const trades = await paperTradingService.getTradeHistory(authReq.user.id, {
accountId: accountId as string,
symbol: symbol as string,
startDate: startDate ? new Date(startDate as string) : undefined,
endDate: endDate ? new Date(endDate as string) : undefined,
limit: limit ? parseInt(limit as string, 10) : undefined,
});
res.json({ success: true, data: trades });
} catch (error) {
next(error);
}
}
export async function getPerformanceStats(req: Request, res: Response, next: NextFunction): Promise<void> {
try {
const authReq = req as AuthenticatedRequest;
const { accountId } = req.query;
const stats = await paperTradingService.getPerformanceStats(
authReq.user.id,
accountId as string | undefined
);
res.json({ success: true, data: stats });
} catch (error) {
next(error);
}
}