adding data-services
This commit is contained in:
parent
e3bfd05b90
commit
405b818c86
139 changed files with 55943 additions and 416 deletions
|
|
@ -2,6 +2,12 @@ import { Hono } from 'hono';
|
|||
import { WebSocketServer } from 'ws';
|
||||
import Redis from 'ioredis';
|
||||
import * as cron from 'node-cron';
|
||||
import { BaseStrategy } from './core/Strategy';
|
||||
import { StrategyRegistry } from './core/strategies/StrategyRegistry';
|
||||
import { BacktestService, BacktestRequest } from './core/backtesting/BacktestService';
|
||||
import { BacktestResult } from './core/backtesting/BacktestEngine';
|
||||
import { PerformanceAnalytics } from './core/backtesting/PerformanceAnalytics';
|
||||
import { StrategyController } from './controllers/StrategyController';
|
||||
|
||||
const app = new Hono();
|
||||
const redis = new Redis({
|
||||
|
|
@ -14,6 +20,10 @@ const redis = new Redis({
|
|||
// WebSocket server for real-time strategy updates
|
||||
const wss = new WebSocketServer({ port: 8082 });
|
||||
|
||||
// Initialize strategy registry and backtest service
|
||||
const strategyRegistry = StrategyRegistry.getInstance();
|
||||
const backtestService = new BacktestService();
|
||||
|
||||
// Strategy interfaces
|
||||
interface TradingStrategy {
|
||||
id: string;
|
||||
|
|
@ -48,6 +58,9 @@ interface StrategySignal {
|
|||
// In-memory strategy registry (in production, this would be persisted)
|
||||
const strategies = new Map<string, TradingStrategy>();
|
||||
|
||||
// Initialize strategy controller
|
||||
const strategyController = new StrategyController();
|
||||
|
||||
// Health check endpoint
|
||||
app.get('/health', (c) => {
|
||||
return c.json({
|
||||
|
|
@ -56,41 +69,214 @@ app.get('/health', (c) => {
|
|||
timestamp: new Date(),
|
||||
version: '1.0.0',
|
||||
activeStrategies: Array.from(strategies.values()).filter(s => s.status === 'ACTIVE').length,
|
||||
registeredStrategies: strategyRegistry.getAllStrategies().length,
|
||||
connections: wss.clients.size
|
||||
});
|
||||
});
|
||||
|
||||
// Get all strategies
|
||||
// API Routes
|
||||
// Strategy management endpoints
|
||||
app.get('/api/strategy-types', async (c) => {
|
||||
try {
|
||||
const types = Object.values(strategyRegistry.getStrategyTypes());
|
||||
return c.json({ success: true, data: types });
|
||||
} catch (error) {
|
||||
console.error('Error getting strategy types:', error);
|
||||
return c.json({ success: false, error: 'Failed to get strategy types' }, 500);
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/api/strategies', async (c) => {
|
||||
try {
|
||||
const strategiesList = Array.from(strategies.values());
|
||||
return c.json({
|
||||
success: true,
|
||||
data: strategiesList
|
||||
});
|
||||
const strategies = strategyRegistry.getAllStrategies();
|
||||
const serializedStrategies = strategies.map(strategy => ({
|
||||
id: strategy.id,
|
||||
name: strategy.name,
|
||||
description: strategy.description,
|
||||
symbols: strategy.symbols,
|
||||
parameters: strategy.parameters,
|
||||
type: strategyRegistry.getStrategyType(strategy)
|
||||
}));
|
||||
|
||||
return c.json({ success: true, data: serializedStrategies });
|
||||
} catch (error) {
|
||||
console.error('Error fetching strategies:', error);
|
||||
return c.json({ success: false, error: 'Failed to fetch strategies' }, 500);
|
||||
}
|
||||
});
|
||||
|
||||
// Get specific strategy
|
||||
app.get('/api/strategies/:id', async (c) => {
|
||||
try {
|
||||
const id = c.req.param('id');
|
||||
const strategy = strategies.get(id);
|
||||
const strategy = strategyRegistry.getStrategyById(id);
|
||||
|
||||
if (!strategy) {
|
||||
return c.json({ success: false, error: 'Strategy not found' }, 404);
|
||||
return c.json({ success: false, error: `Strategy with ID ${id} not found` }, 404);
|
||||
}
|
||||
|
||||
return c.json({ success: true, data: strategy });
|
||||
|
||||
const type = strategyRegistry.getStrategyType(strategy);
|
||||
|
||||
return c.json({
|
||||
success: true,
|
||||
data: {
|
||||
id: strategy.id,
|
||||
name: strategy.name,
|
||||
description: strategy.description,
|
||||
symbols: strategy.symbols,
|
||||
parameters: strategy.parameters,
|
||||
type
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error fetching strategy:', error);
|
||||
return c.json({ success: false, error: 'Failed to fetch strategy' }, 500);
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/strategies', async (c) => {
|
||||
try {
|
||||
const { name, description, symbols, parameters, type } = await c.req.json();
|
||||
|
||||
if (!type) {
|
||||
return c.json({
|
||||
success: false,
|
||||
error: 'Invalid strategy type'
|
||||
}, 400);
|
||||
}
|
||||
|
||||
const strategy = strategyRegistry.createStrategy(
|
||||
type,
|
||||
`strategy_${Date.now()}`, // Generate an ID
|
||||
name || `New ${type} Strategy`,
|
||||
description || `Generated ${type} strategy`,
|
||||
symbols || [],
|
||||
parameters || {}
|
||||
);
|
||||
|
||||
return c.json({
|
||||
success: true,
|
||||
data: {
|
||||
id: strategy.id,
|
||||
name: strategy.name,
|
||||
description: strategy.description,
|
||||
symbols: strategy.symbols,
|
||||
parameters: strategy.parameters,
|
||||
type
|
||||
}
|
||||
}, 201);
|
||||
} catch (error) {
|
||||
console.error('Error creating strategy:', error);
|
||||
return c.json({ success: false, error: (error as Error).message }, 500);
|
||||
}
|
||||
});
|
||||
|
||||
app.put('/api/strategies/:id', async (c) => {
|
||||
try {
|
||||
const id = c.req.param('id');
|
||||
const { name, description, symbols, parameters } = await c.req.json();
|
||||
|
||||
const strategy = strategyRegistry.getStrategyById(id);
|
||||
|
||||
if (!strategy) {
|
||||
return c.json({ success: false, error: `Strategy with ID ${id} not found` }, 404);
|
||||
}
|
||||
|
||||
// Update properties
|
||||
if (name !== undefined) strategy.name = name;
|
||||
if (description !== undefined) strategy.description = description;
|
||||
if (symbols !== undefined) (strategy as any).symbols = symbols; // Hack since symbols is readonly
|
||||
if (parameters !== undefined) strategy.parameters = parameters;
|
||||
|
||||
return c.json({
|
||||
success: true,
|
||||
data: {
|
||||
id: strategy.id,
|
||||
name: strategy.name,
|
||||
description: strategy.description,
|
||||
symbols: strategy.symbols,
|
||||
parameters: strategy.parameters,
|
||||
type: strategyRegistry.getStrategyType(strategy)
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error updating strategy:', error);
|
||||
return c.json({ success: false, error: (error as Error).message }, 500);
|
||||
}
|
||||
});
|
||||
|
||||
app.delete('/api/strategies/:id', async (c) => {
|
||||
try {
|
||||
const id = c.req.param('id');
|
||||
const success = strategyRegistry.deleteStrategy(id);
|
||||
|
||||
if (!success) {
|
||||
return c.json({ success: false, error: `Strategy with ID ${id} not found` }, 404);
|
||||
}
|
||||
|
||||
return c.json({ success: true, data: { id } });
|
||||
} catch (error) {
|
||||
console.error('Error deleting strategy:', error);
|
||||
return c.json({ success: false, error: (error as Error).message }, 500);
|
||||
}
|
||||
});
|
||||
|
||||
// Backtesting endpoints
|
||||
app.post('/api/backtest', async (c) => {
|
||||
try {
|
||||
const backtestRequest = await c.req.json() as BacktestRequest;
|
||||
|
||||
// Validate request
|
||||
if (!backtestRequest.strategyType) {
|
||||
return c.json({ success: false, error: 'Strategy type is required' }, 400);
|
||||
}
|
||||
|
||||
if (!backtestRequest.symbols || backtestRequest.symbols.length === 0) {
|
||||
return c.json({ success: false, error: 'At least one symbol is required' }, 400);
|
||||
}
|
||||
|
||||
// Run the backtest
|
||||
const result = await backtestService.runBacktest(backtestRequest);
|
||||
|
||||
// Enhance results with additional metrics
|
||||
const enhancedResult = PerformanceAnalytics.enhanceResults(result);
|
||||
|
||||
// Calculate additional analytics
|
||||
const monthlyReturns = PerformanceAnalytics.calculateMonthlyReturns(result.dailyReturns);
|
||||
const drawdowns = PerformanceAnalytics.analyzeDrawdowns(result.dailyReturns);
|
||||
|
||||
return c.json({
|
||||
success: true,
|
||||
data: {
|
||||
...enhancedResult,
|
||||
monthlyReturns,
|
||||
drawdowns
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Backtest error:', error);
|
||||
return c.json({ success: false, error: (error as Error).message }, 500);
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/optimize', async (c) => {
|
||||
try {
|
||||
const { baseRequest, parameterGrid } = await c.req.json();
|
||||
|
||||
// Validate request
|
||||
if (!baseRequest || !parameterGrid) {
|
||||
return c.json({ success: false, error: 'Base request and parameter grid are required' }, 400);
|
||||
}
|
||||
|
||||
// Run optimization
|
||||
const results = await backtestService.optimizeStrategy(baseRequest, parameterGrid);
|
||||
|
||||
return c.json({ success: true, data: results });
|
||||
} catch (error) {
|
||||
console.error('Strategy optimization error:', error);
|
||||
return c.json({ success: false, error: (error as Error).message }, 500);
|
||||
}
|
||||
});
|
||||
|
||||
// Create new strategy
|
||||
app.post('/api/strategies', async (c) => {
|
||||
try {
|
||||
|
|
@ -252,6 +438,32 @@ app.get('/api/strategies/:id/signals', async (c) => {
|
|||
}
|
||||
});
|
||||
|
||||
// Get strategy trades
|
||||
app.get('/api/strategies/:id/trades', async (c) => {
|
||||
try {
|
||||
const id = c.req.param('id');
|
||||
const limit = parseInt(c.req.query('limit') || '50');
|
||||
|
||||
const tradeKeys = await redis.keys(`trade:${id}:*`);
|
||||
const trades: any[] = [];
|
||||
|
||||
for (const key of tradeKeys.slice(0, limit)) {
|
||||
const data = await redis.get(key);
|
||||
if (data) {
|
||||
trades.push(JSON.parse(data));
|
||||
}
|
||||
}
|
||||
|
||||
return c.json({
|
||||
success: true,
|
||||
data: trades.sort((a: any, b: any) => new Date(b.exitTime || b.timestamp).getTime() - new Date(a.exitTime || a.timestamp).getTime())
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error fetching strategy trades:', error);
|
||||
return c.json({ success: false, error: 'Failed to fetch trades' }, 500);
|
||||
}
|
||||
});
|
||||
|
||||
// Generate demo signal (for testing)
|
||||
app.post('/api/strategies/:id/generate-signal', async (c) => {
|
||||
try {
|
||||
|
|
@ -378,6 +590,91 @@ cron.schedule('*/5 * * * *', async () => {
|
|||
}
|
||||
});
|
||||
|
||||
// Backtesting API endpoints
|
||||
app.post('/api/backtest', async (c) => {
|
||||
try {
|
||||
const request = await c.req.json() as BacktestRequest;
|
||||
console.log('Received backtest request:', request);
|
||||
|
||||
const result = await backtestService.runBacktest(request);
|
||||
const enhancedResult = PerformanceAnalytics.enhanceResults(result);
|
||||
|
||||
// Store backtest result in Redis for persistence
|
||||
await redis.setex(
|
||||
`backtest:${result.strategyId}`,
|
||||
86400 * 7, // 7 days TTL
|
||||
JSON.stringify(enhancedResult)
|
||||
);
|
||||
|
||||
return c.json({ success: true, data: enhancedResult });
|
||||
} catch (error) {
|
||||
console.error('Backtest error:', error);
|
||||
return c.json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error'
|
||||
}, 500);
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/backtest/optimize', async (c) => {
|
||||
try {
|
||||
const { baseRequest, parameterGrid } = await c.req.json() as {
|
||||
baseRequest: BacktestRequest,
|
||||
parameterGrid: Record<string, any[]>
|
||||
};
|
||||
|
||||
console.log('Received optimization request:', baseRequest, parameterGrid);
|
||||
|
||||
const results = await backtestService.optimizeStrategy(baseRequest, parameterGrid);
|
||||
|
||||
return c.json({ success: true, data: results });
|
||||
} catch (error) {
|
||||
console.error('Optimization error:', error);
|
||||
return c.json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error'
|
||||
}, 500);
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/api/backtest/:id', async (c) => {
|
||||
try {
|
||||
const id = c.req.param('id');
|
||||
const data = await redis.get(`backtest:${id}`);
|
||||
|
||||
if (!data) {
|
||||
return c.json({ success: false, error: 'Backtest not found' }, 404);
|
||||
}
|
||||
|
||||
const result = JSON.parse(data) as BacktestResult;
|
||||
return c.json({ success: true, data: result });
|
||||
} catch (error) {
|
||||
console.error('Error fetching backtest:', error);
|
||||
return c.json({ success: false, error: 'Failed to fetch backtest' }, 500);
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/api/strategy-types', (c) => {
|
||||
const types = strategyRegistry.getStrategyTypes();
|
||||
return c.json({ success: true, data: types });
|
||||
});
|
||||
|
||||
app.get('/api/strategy-parameters/:type', (c) => {
|
||||
try {
|
||||
const type = c.req.param('type') as any;
|
||||
|
||||
if (!strategyRegistry.hasStrategyType(type)) {
|
||||
return c.json({ success: false, error: 'Strategy type not found' }, 404);
|
||||
}
|
||||
|
||||
const params = strategyRegistry.getDefaultParameters(type);
|
||||
return c.json({ success: true, data: params });
|
||||
} catch (error) {
|
||||
console.error('Error fetching strategy parameters:', error);
|
||||
return c.json({ success: false, error: 'Failed to fetch parameters' }, 500);
|
||||
}
|
||||
});
|
||||
|
||||
// Load existing strategies from Redis on startup
|
||||
async function loadStrategiesFromRedis() {
|
||||
try {
|
||||
|
|
@ -395,7 +692,7 @@ async function loadStrategiesFromRedis() {
|
|||
}
|
||||
}
|
||||
|
||||
const port = parseInt(process.env.PORT || '3003');
|
||||
const port = parseInt(process.env.PORT || '4001');
|
||||
|
||||
console.log(`🎯 Strategy Orchestrator starting on port ${port}`);
|
||||
console.log(`📡 WebSocket server running on port 8082`);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue