From bc14acaeba58f76bbb42fd04c288ecfe3978f66a Mon Sep 17 00:00:00 2001 From: Boki Date: Wed, 18 Jun 2025 11:43:39 -0400 Subject: [PATCH] removed old services --- apps/execution-service/package.json | 37 -- .../execution-service/src/broker/interface.ts | 94 ---- .../src/execution/order-manager.ts | 58 --- .../src/execution/risk-manager.ts | 113 ----- apps/execution-service/src/index.ts | 101 ----- apps/execution-service/tsconfig.json | 25 -- apps/execution-service/turbo.json | 26 -- apps/portfolio-service/package.json | 38 -- .../src/analytics/performance-analyzer.ts | 240 ---------- apps/portfolio-service/src/index.ts | 134 ------ .../src/portfolio/portfolio-manager.ts | 159 ------- apps/portfolio-service/tsconfig.json | 26 -- apps/portfolio-service/turbo.json | 27 -- apps/processing-service/package.json | 27 -- apps/processing-service/src/index.ts | 122 ----- .../src/routes/health.routes.ts | 44 -- apps/processing-service/src/routes/index.ts | 5 - .../src/routes/processing.routes.ts | 57 --- apps/processing-service/src/services/index.ts | 4 - .../src/services/processing.service.ts | 115 ----- apps/processing-service/tsconfig.json | 28 -- apps/processing-service/turbo.json | 29 -- apps/strategy-service/package.json | 34 -- .../src/backtesting/modes/event-mode.ts | 75 ---- .../src/backtesting/modes/hybrid-mode.ts | 424 ------------------ .../src/backtesting/modes/live-mode.ts | 31 -- .../src/backtesting/modes/vectorized-mode.ts | 236 ---------- apps/strategy-service/src/cli/index.ts | 285 ------------ .../src/framework/execution-mode.ts | 80 ---- apps/strategy-service/src/index.ts | 89 ---- apps/strategy-service/tsconfig.json | 26 -- apps/strategy-service/turbo.json | 27 -- 32 files changed, 2816 deletions(-) delete mode 100644 apps/execution-service/package.json delete mode 100644 apps/execution-service/src/broker/interface.ts delete mode 100644 apps/execution-service/src/execution/order-manager.ts delete mode 100644 apps/execution-service/src/execution/risk-manager.ts delete mode 100644 apps/execution-service/src/index.ts delete mode 100644 apps/execution-service/tsconfig.json delete mode 100644 apps/execution-service/turbo.json delete mode 100644 apps/portfolio-service/package.json delete mode 100644 apps/portfolio-service/src/analytics/performance-analyzer.ts delete mode 100644 apps/portfolio-service/src/index.ts delete mode 100644 apps/portfolio-service/src/portfolio/portfolio-manager.ts delete mode 100644 apps/portfolio-service/tsconfig.json delete mode 100644 apps/portfolio-service/turbo.json delete mode 100644 apps/processing-service/package.json delete mode 100644 apps/processing-service/src/index.ts delete mode 100644 apps/processing-service/src/routes/health.routes.ts delete mode 100644 apps/processing-service/src/routes/index.ts delete mode 100644 apps/processing-service/src/routes/processing.routes.ts delete mode 100644 apps/processing-service/src/services/index.ts delete mode 100644 apps/processing-service/src/services/processing.service.ts delete mode 100644 apps/processing-service/tsconfig.json delete mode 100644 apps/processing-service/turbo.json delete mode 100644 apps/strategy-service/package.json delete mode 100644 apps/strategy-service/src/backtesting/modes/event-mode.ts delete mode 100644 apps/strategy-service/src/backtesting/modes/hybrid-mode.ts delete mode 100644 apps/strategy-service/src/backtesting/modes/live-mode.ts delete mode 100644 apps/strategy-service/src/backtesting/modes/vectorized-mode.ts delete mode 100644 apps/strategy-service/src/cli/index.ts delete mode 100644 apps/strategy-service/src/framework/execution-mode.ts delete mode 100644 apps/strategy-service/src/index.ts delete mode 100644 apps/strategy-service/tsconfig.json delete mode 100644 apps/strategy-service/turbo.json diff --git a/apps/execution-service/package.json b/apps/execution-service/package.json deleted file mode 100644 index efdf413..0000000 --- a/apps/execution-service/package.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "@stock-bot/execution-service", - "version": "1.0.0", - "description": "Execution service for stock trading bot - handles order execution and broker integration", - "main": "dist/index.js", - "type": "module", - "scripts": { - "build": "tsc", - "devvvvv": "bun --watch src/index.ts", - "start": "bun src/index.ts", - "test": "bun test", - "lint": "eslint src --ext .ts", - "type-check": "tsc --noEmit" - }, - "dependencies": { - "@hono/node-server": "^1.12.0", - "hono": "^4.6.1", - "@stock-bot/config": "*", - "@stock-bot/logger": "*", - "@stock-bot/types": "*", - "@stock-bot/event-bus": "*", - "@stock-bot/utils": "*" - }, - "devDependencies": { - "@types/node": "^22.5.0", - "typescript": "^5.5.4" - }, - "keywords": [ - "trading", - "execution", - "broker", - "orders", - "stock-bot" - ], - "author": "Stock Bot Team", - "license": "MIT" -} diff --git a/apps/execution-service/src/broker/interface.ts b/apps/execution-service/src/broker/interface.ts deleted file mode 100644 index 3ad6f33..0000000 --- a/apps/execution-service/src/broker/interface.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { Order, OrderResult, OrderStatus } from '@stock-bot/types'; - -export interface BrokerInterface { - /** - * Execute an order with the broker - */ - executeOrder(order: Order): Promise; - - /** - * Get order status from broker - */ - getOrderStatus(orderId: string): Promise; - - /** - * Cancel an order - */ - cancelOrder(orderId: string): Promise; - - /** - * Get current positions - */ - getPositions(): Promise; - - /** - * Get account balance - */ - getAccountBalance(): Promise; -} - -export interface Position { - symbol: string; - quantity: number; - averagePrice: number; - currentPrice: number; - unrealizedPnL: number; - side: 'long' | 'short'; -} - -export interface AccountBalance { - totalValue: number; - availableCash: number; - buyingPower: number; - marginUsed: number; -} - -export class MockBroker implements BrokerInterface { - private orders: Map = new Map(); - private positions: Position[] = []; - - async executeOrder(order: Order): Promise { - const orderId = `mock_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; - - const result: OrderResult = { - orderId, - symbol: order.symbol, - quantity: order.quantity, - side: order.side, - status: 'filled', - executedPrice: order.price || 100, // Mock price - executedAt: new Date(), - commission: 1.0, - }; - - this.orders.set(orderId, result); - return result; - } - - async getOrderStatus(orderId: string): Promise { - const order = this.orders.get(orderId); - return order?.status || 'unknown'; - } - - async cancelOrder(orderId: string): Promise { - const order = this.orders.get(orderId); - if (order && order.status === 'pending') { - order.status = 'cancelled'; - return true; - } - return false; - } - - async getPositions(): Promise { - return this.positions; - } - - async getAccountBalance(): Promise { - return { - totalValue: 100000, - availableCash: 50000, - buyingPower: 200000, - marginUsed: 0, - }; - } -} diff --git a/apps/execution-service/src/execution/order-manager.ts b/apps/execution-service/src/execution/order-manager.ts deleted file mode 100644 index 653ccd4..0000000 --- a/apps/execution-service/src/execution/order-manager.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { logger } from '@stock-bot/logger'; -import { Order, OrderResult } from '@stock-bot/types'; -import { BrokerInterface } from '../broker/interface.ts'; - -export class OrderManager { - private broker: BrokerInterface; - private pendingOrders: Map = new Map(); - - constructor(broker: BrokerInterface) { - this.broker = broker; - } - - async executeOrder(order: Order): Promise { - try { - logger.info( - `Executing order: ${order.symbol} ${order.side} ${order.quantity} @ ${order.price}` - ); - - // Add to pending orders - const orderId = `order_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; - this.pendingOrders.set(orderId, order); - - // Execute with broker - const result = await this.broker.executeOrder(order); - - // Remove from pending - this.pendingOrders.delete(orderId); - - logger.info(`Order executed successfully: ${result.orderId}`); - return result; - } catch (error) { - logger.error('Order execution failed', error); - throw error; - } - } - - async cancelOrder(orderId: string): Promise { - try { - const success = await this.broker.cancelOrder(orderId); - if (success) { - this.pendingOrders.delete(orderId); - logger.info(`Order cancelled: ${orderId}`); - } - return success; - } catch (error) { - logger.error('Order cancellation failed', error); - throw error; - } - } - - async getOrderStatus(orderId: string) { - return await this.broker.getOrderStatus(orderId); - } - - getPendingOrders(): Order[] { - return Array.from(this.pendingOrders.values()); - } -} diff --git a/apps/execution-service/src/execution/risk-manager.ts b/apps/execution-service/src/execution/risk-manager.ts deleted file mode 100644 index d6287da..0000000 --- a/apps/execution-service/src/execution/risk-manager.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { getLogger } from '@stock-bot/logger'; -import { Order } from '@stock-bot/types'; - -export interface RiskRule { - name: string; - validate(order: Order, context: RiskContext): Promise; -} - -export interface RiskContext { - currentPositions: Map; - accountBalance: number; - totalExposure: number; - maxPositionSize: number; - maxDailyLoss: number; -} - -export interface RiskValidationResult { - isValid: boolean; - reason?: string; - severity: 'info' | 'warning' | 'error'; -} - -export class RiskManager { - private logger = getLogger('risk-manager'); - private rules: RiskRule[] = []; - - constructor() { - this.initializeDefaultRules(); - } - - addRule(rule: RiskRule): void { - this.rules.push(rule); - } - - async validateOrder(order: Order, context: RiskContext): Promise { - for (const rule of this.rules) { - const result = await rule.validate(order, context); - if (!result.isValid) { - logger.warn(`Risk rule violation: ${rule.name}`, { - order, - reason: result.reason, - }); - return result; - } - } - - return { isValid: true, severity: 'info' }; - } - - private initializeDefaultRules(): void { - // Position size rule - this.addRule({ - name: 'MaxPositionSize', - async validate(order: Order, context: RiskContext): Promise { - const orderValue = order.quantity * (order.price || 0); - - if (orderValue > context.maxPositionSize) { - return { - isValid: false, - reason: `Order size ${orderValue} exceeds maximum position size ${context.maxPositionSize}`, - severity: 'error', - }; - } - - return { isValid: true, severity: 'info' }; - }, - }); - - // Balance check rule - this.addRule({ - name: 'SufficientBalance', - async validate(order: Order, context: RiskContext): Promise { - const orderValue = order.quantity * (order.price || 0); - - if (order.side === 'buy' && orderValue > context.accountBalance) { - return { - isValid: false, - reason: `Insufficient balance: need ${orderValue}, have ${context.accountBalance}`, - severity: 'error', - }; - } - - return { isValid: true, severity: 'info' }; - }, - }); - - // Concentration risk rule - this.addRule({ - name: 'ConcentrationLimit', - async validate(order: Order, context: RiskContext): Promise { - const currentPosition = context.currentPositions.get(order.symbol) || 0; - const newPosition = - order.side === 'buy' - ? currentPosition + order.quantity - : currentPosition - order.quantity; - - const positionValue = Math.abs(newPosition) * (order.price || 0); - const concentrationRatio = positionValue / context.accountBalance; - - if (concentrationRatio > 0.25) { - // 25% max concentration - return { - isValid: false, - reason: `Position concentration ${(concentrationRatio * 100).toFixed(2)}% exceeds 25% limit`, - severity: 'warning', - }; - } - - return { isValid: true, severity: 'info' }; - }, - }); - } -} diff --git a/apps/execution-service/src/index.ts b/apps/execution-service/src/index.ts deleted file mode 100644 index 3e6652b..0000000 --- a/apps/execution-service/src/index.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { serve } from '@hono/node-server'; -import { Hono } from 'hono'; -import { config } from '@stock-bot/config'; -import { getLogger } from '@stock-bot/logger'; - -// import { BrokerInterface } from './broker/interface.ts'; -// import { OrderManager } from './execution/order-manager.ts'; -// import { RiskManager } from './execution/risk-manager.ts'; - -const app = new Hono(); -const logger = getLogger('execution-service'); -// Health check endpoint -app.get('/health', c => { - return c.json({ - status: 'healthy', - service: 'execution-service', - timestamp: new Date().toISOString(), - }); -}); - -// Order execution endpoints -app.post('/orders/execute', async c => { - try { - const orderRequest = await c.req.json(); - logger.info('Received order execution request', orderRequest); - - // TODO: Validate order and execute - return c.json({ - orderId: `order_${Date.now()}`, - status: 'pending', - message: 'Order submitted for execution', - }); - } catch (error) { - logger.error('Order execution failed', error); - return c.json({ error: 'Order execution failed' }, 500); - } -}); - -app.get('/orders/:orderId/status', async c => { - const orderId = c.req.param('orderId'); - - try { - // TODO: Get order status from broker - return c.json({ - orderId, - status: 'filled', - executedAt: new Date().toISOString(), - }); - } catch (error) { - logger.error('Failed to get order status', error); - return c.json({ error: 'Failed to get order status' }, 500); - } -}); - -app.post('/orders/:orderId/cancel', async c => { - const orderId = c.req.param('orderId'); - - try { - // TODO: Cancel order with broker - return c.json({ - orderId, - status: 'cancelled', - cancelledAt: new Date().toISOString(), - }); - } catch (error) { - logger.error('Failed to cancel order', error); - return c.json({ error: 'Failed to cancel order' }, 500); - } -}); - -// Risk management endpoints -app.get('/risk/position/:symbol', async c => { - const symbol = c.req.param('symbol'); - - try { - // TODO: Get position risk metrics - return c.json({ - symbol, - position: 100, - exposure: 10000, - risk: 'low', - }); - } catch (error) { - logger.error('Failed to get position risk', error); - return c.json({ error: 'Failed to get position risk' }, 500); - } -}); - -const port = config.EXECUTION_SERVICE_PORT || 3004; - -logger.info(`Starting execution service on port ${port}`); - -serve( - { - fetch: app.fetch, - port, - }, - info => { - logger.info(`Execution service is running on port ${info.port}`); - } -); diff --git a/apps/execution-service/tsconfig.json b/apps/execution-service/tsconfig.json deleted file mode 100644 index b94f8f9..0000000 --- a/apps/execution-service/tsconfig.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "./dist", - "rootDir": "./src" - }, - "include": ["src/**/*"], - "exclude": [ - "node_modules", - "dist", - "**/*.test.ts", - "**/*.spec.ts", - "**/test/**", - "**/tests/**", - "**/__tests__/**" - ], - "references": [ - { "path": "../../libs/types" }, - { "path": "../../libs/config" }, - { "path": "../../libs/logger" }, - { "path": "../../libs/utils" }, - { "path": "../../libs/event-bus" }, - { "path": "../../libs/shutdown" } - ] -} diff --git a/apps/execution-service/turbo.json b/apps/execution-service/turbo.json deleted file mode 100644 index 846749e..0000000 --- a/apps/execution-service/turbo.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "extends": ["//"], - "tasks": { - "build": { - "dependsOn": [ - "@stock-bot/types#build", - "@stock-bot/config#build", - "@stock-bot/logger#build", - "@stock-bot/utils#build", - "@stock-bot/event-bus#build", - "@stock-bot/shutdown#build" - ], - "outputs": ["dist/**"], - "inputs": [ - "src/**", - "package.json", - "tsconfig.json", - "!**/*.test.ts", - "!**/*.spec.ts", - "!**/test/**", - "!**/tests/**", - "!**/__tests__/**" - ] - } - } -} diff --git a/apps/portfolio-service/package.json b/apps/portfolio-service/package.json deleted file mode 100644 index 6e838da..0000000 --- a/apps/portfolio-service/package.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "name": "@stock-bot/portfolio-service", - "version": "1.0.0", - "description": "Portfolio service for stock trading bot - handles portfolio tracking and performance analytics", - "main": "dist/index.js", - "type": "module", - "scripts": { - "build": "tsc", - "devvvvv": "bun --watch src/index.ts", - "start": "bun src/index.ts", - "test": "bun test", - "lint": "eslint src --ext .ts", - "type-check": "tsc --noEmit" - }, - "dependencies": { - "@hono/node-server": "^1.12.0", - "hono": "^4.6.1", - "@stock-bot/config": "*", - "@stock-bot/logger": "*", - "@stock-bot/types": "*", - "@stock-bot/questdb-client": "*", - "@stock-bot/utils": "*", - "@stock-bot/data-frame": "*" - }, - "devDependencies": { - "@types/node": "^22.5.0", - "typescript": "^5.5.4" - }, - "keywords": [ - "trading", - "portfolio", - "performance", - "analytics", - "stock-bot" - ], - "author": "Stock Bot Team", - "license": "MIT" -} diff --git a/apps/portfolio-service/src/analytics/performance-analyzer.ts b/apps/portfolio-service/src/analytics/performance-analyzer.ts deleted file mode 100644 index a670894..0000000 --- a/apps/portfolio-service/src/analytics/performance-analyzer.ts +++ /dev/null @@ -1,240 +0,0 @@ -import { PortfolioSnapshot } from '../portfolio/portfolio-manager'; - -export interface PerformanceMetrics { - totalReturn: number; - annualizedReturn: number; - sharpeRatio: number; - maxDrawdown: number; - volatility: number; - beta: number; - alpha: number; - calmarRatio: number; - sortinoRatio: number; -} - -export interface RiskMetrics { - var95: number; // Value at Risk (95% confidence) - cvar95: number; // Conditional Value at Risk - maxDrawdown: number; - downsideDeviation: number; - correlationMatrix: Record>; -} - -export class PerformanceAnalyzer { - private snapshots: PortfolioSnapshot[] = []; - private benchmarkReturns: number[] = []; // S&P 500 or other benchmark - - addSnapshot(snapshot: PortfolioSnapshot): void { - this.snapshots.push(snapshot); - // Keep only last 252 trading days (1 year) - if (this.snapshots.length > 252) { - this.snapshots = this.snapshots.slice(-252); - } - } - - calculatePerformanceMetrics( - period: 'daily' | 'weekly' | 'monthly' = 'daily' - ): PerformanceMetrics { - if (this.snapshots.length < 2) { - throw new Error('Need at least 2 snapshots to calculate performance'); - } - - const returns = this.calculateReturns(period); - const riskFreeRate = 0.02; // 2% annual risk-free rate - - return { - totalReturn: this.calculateTotalReturn(), - annualizedReturn: this.calculateAnnualizedReturn(returns), - sharpeRatio: this.calculateSharpeRatio(returns, riskFreeRate), - maxDrawdown: this.calculateMaxDrawdown(), - volatility: this.calculateVolatility(returns), - beta: this.calculateBeta(returns), - alpha: this.calculateAlpha(returns, riskFreeRate), - calmarRatio: this.calculateCalmarRatio(returns), - sortinoRatio: this.calculateSortinoRatio(returns, riskFreeRate), - }; - } - - calculateRiskMetrics(): RiskMetrics { - const returns = this.calculateReturns('daily'); - - return { - var95: this.calculateVaR(returns, 0.95), - cvar95: this.calculateCVaR(returns, 0.95), - maxDrawdown: this.calculateMaxDrawdown(), - downsideDeviation: this.calculateDownsideDeviation(returns), - correlationMatrix: {}, // TODO: Implement correlation matrix - }; - } - - private calculateReturns(_period: 'daily' | 'weekly' | 'monthly'): number[] { - if (this.snapshots.length < 2) { - return []; - } - - const returns: number[] = []; - - for (let i = 1; i < this.snapshots.length; i++) { - const currentValue = this.snapshots[i].totalValue; - const previousValue = this.snapshots[i - 1].totalValue; - const return_ = (currentValue - previousValue) / previousValue; - returns.push(return_); - } - - return returns; - } - - private calculateTotalReturn(): number { - if (this.snapshots.length < 2) { - return 0; - } - - const firstValue = this.snapshots[0].totalValue; - const lastValue = this.snapshots[this.snapshots.length - 1].totalValue; - - return (lastValue - firstValue) / firstValue; - } - - private calculateAnnualizedReturn(returns: number[]): number { - if (returns.length === 0) { - return 0; - } - - const avgReturn = returns.reduce((sum, ret) => sum + ret, 0) / returns.length; - return Math.pow(1 + avgReturn, 252) - 1; // 252 trading days per year - } - - private calculateVolatility(returns: number[]): number { - if (returns.length === 0) { - return 0; - } - - const avgReturn = returns.reduce((sum, ret) => sum + ret, 0) / returns.length; - const variance = - returns.reduce((sum, ret) => sum + Math.pow(ret - avgReturn, 2), 0) / returns.length; - - return Math.sqrt(variance * 252); // Annualized volatility - } - - private calculateSharpeRatio(returns: number[], riskFreeRate: number): number { - if (returns.length === 0) { - return 0; - } - - const avgReturn = returns.reduce((sum, ret) => sum + ret, 0) / returns.length; - const annualizedReturn = Math.pow(1 + avgReturn, 252) - 1; - const volatility = this.calculateVolatility(returns); - - if (volatility === 0) { - return 0; - } - - return (annualizedReturn - riskFreeRate) / volatility; - } - - private calculateMaxDrawdown(): number { - if (this.snapshots.length === 0) { - return 0; - } - - let maxDrawdown = 0; - let peak = this.snapshots[0].totalValue; - - for (const snapshot of this.snapshots) { - if (snapshot.totalValue > peak) { - peak = snapshot.totalValue; - } - - const drawdown = (peak - snapshot.totalValue) / peak; - maxDrawdown = Math.max(maxDrawdown, drawdown); - } - - return maxDrawdown; - } - - private calculateBeta(returns: number[]): number { - if (returns.length === 0 || this.benchmarkReturns.length === 0) { - return 1.0; - } - - // Simple beta calculation - would need actual benchmark data - return 1.0; // Placeholder - } - - private calculateAlpha(returns: number[], riskFreeRate: number): number { - const beta = this.calculateBeta(returns); - const portfolioReturn = this.calculateAnnualizedReturn(returns); - const benchmarkReturn = 0.1; // 10% benchmark return (placeholder) - - return portfolioReturn - (riskFreeRate + beta * (benchmarkReturn - riskFreeRate)); - } - - private calculateCalmarRatio(returns: number[]): number { - const annualizedReturn = this.calculateAnnualizedReturn(returns); - const maxDrawdown = this.calculateMaxDrawdown(); - - if (maxDrawdown === 0) { - return 0; - } - - return annualizedReturn / maxDrawdown; - } - - private calculateSortinoRatio(returns: number[], riskFreeRate: number): number { - const annualizedReturn = this.calculateAnnualizedReturn(returns); - const downsideDeviation = this.calculateDownsideDeviation(returns); - - if (downsideDeviation === 0) { - return 0; - } - - return (annualizedReturn - riskFreeRate) / downsideDeviation; - } - - private calculateDownsideDeviation(returns: number[]): number { - if (returns.length === 0) { - return 0; - } - - const negativeReturns = returns.filter(ret => ret < 0); - if (negativeReturns.length === 0) { - return 0; - } - - const avgNegativeReturn = - negativeReturns.reduce((sum, ret) => sum + ret, 0) / negativeReturns.length; - const variance = - negativeReturns.reduce((sum, ret) => sum + Math.pow(ret - avgNegativeReturn, 2), 0) / - negativeReturns.length; - - return Math.sqrt(variance * 252); // Annualized - } - - private calculateVaR(returns: number[], confidence: number): number { - if (returns.length === 0) { - return 0; - } - - const sortedReturns = returns.slice().sort((a, b) => a - b); - const index = Math.floor((1 - confidence) * sortedReturns.length); - - return -sortedReturns[index]; // Return as positive value - } - - private calculateCVaR(returns: number[], confidence: number): number { - if (returns.length === 0) { - return 0; - } - - const sortedReturns = returns.slice().sort((a, b) => a - b); - const cutoffIndex = Math.floor((1 - confidence) * sortedReturns.length); - const tailReturns = sortedReturns.slice(0, cutoffIndex + 1); - - if (tailReturns.length === 0) { - return 0; - } - - const avgTailReturn = tailReturns.reduce((sum, ret) => sum + ret, 0) / tailReturns.length; - return -avgTailReturn; // Return as positive value - } -} diff --git a/apps/portfolio-service/src/index.ts b/apps/portfolio-service/src/index.ts deleted file mode 100644 index f5485e5..0000000 --- a/apps/portfolio-service/src/index.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { serve } from '@hono/node-server'; -import { Hono } from 'hono'; -import { config } from '@stock-bot/config'; -import { getLogger } from '@stock-bot/logger'; - -const app = new Hono(); -const logger = getLogger('portfolio-service'); -// Health check endpoint -app.get('/health', c => { - return c.json({ - status: 'healthy', - service: 'portfolio-service', - timestamp: new Date().toISOString(), - }); -}); - -// Portfolio endpoints -app.get('/portfolio/overview', async c => { - try { - // TODO: Get portfolio overview - return c.json({ - totalValue: 125000, - totalReturn: 25000, - totalReturnPercent: 25.0, - dayChange: 1250, - dayChangePercent: 1.0, - positions: [], - }); - } catch (error) { - logger.error('Failed to get portfolio overview', error); - return c.json({ error: 'Failed to get portfolio overview' }, 500); - } -}); - -app.get('/portfolio/positions', async c => { - try { - // TODO: Get current positions - return c.json([ - { - symbol: 'AAPL', - quantity: 100, - averagePrice: 150.0, - currentPrice: 155.0, - marketValue: 15500, - unrealizedPnL: 500, - unrealizedPnLPercent: 3.33, - }, - ]); - } catch (error) { - logger.error('Failed to get positions', error); - return c.json({ error: 'Failed to get positions' }, 500); - } -}); - -app.get('/portfolio/history', async c => { - const days = c.req.query('days') || '30'; - - try { - // TODO: Get portfolio history - return c.json({ - period: `${days} days`, - data: [], - }); - } catch (error) { - logger.error('Failed to get portfolio history', error); - return c.json({ error: 'Failed to get portfolio history' }, 500); - } -}); - -// Performance analytics endpoints -app.get('/analytics/performance', async c => { - const period = c.req.query('period') || '1M'; - - try { - // TODO: Calculate performance metrics - return c.json({ - period, - totalReturn: 0.25, - annualizedReturn: 0.3, - sharpeRatio: 1.5, - maxDrawdown: 0.05, - volatility: 0.15, - beta: 1.1, - alpha: 0.02, - }); - } catch (error) { - logger.error('Failed to get performance analytics', error); - return c.json({ error: 'Failed to get performance analytics' }, 500); - } -}); - -app.get('/analytics/risk', async c => { - try { - // TODO: Calculate risk metrics - return c.json({ - var95: 0.02, - cvar95: 0.03, - maxDrawdown: 0.05, - downside_deviation: 0.08, - correlation_matrix: {}, - }); - } catch (error) { - logger.error('Failed to get risk analytics', error); - return c.json({ error: 'Failed to get risk analytics' }, 500); - } -}); - -app.get('/analytics/attribution', async c => { - try { - // TODO: Calculate performance attribution - return c.json({ - sector_allocation: {}, - security_selection: {}, - interaction_effect: {}, - }); - } catch (error) { - logger.error('Failed to get attribution analytics', error); - return c.json({ error: 'Failed to get attribution analytics' }, 500); - } -}); - -const port = config.PORTFOLIO_SERVICE_PORT || 3005; - -logger.info(`Starting portfolio service on port ${port}`); - -serve( - { - fetch: app.fetch, - port, - }, - info => { - logger.info(`Portfolio service is running on port ${info.port}`); - } -); diff --git a/apps/portfolio-service/src/portfolio/portfolio-manager.ts b/apps/portfolio-service/src/portfolio/portfolio-manager.ts deleted file mode 100644 index 17ddc45..0000000 --- a/apps/portfolio-service/src/portfolio/portfolio-manager.ts +++ /dev/null @@ -1,159 +0,0 @@ -import { getLogger } from '@stock-bot/logger'; - -export interface Position { - symbol: string; - quantity: number; - averagePrice: number; - currentPrice: number; - marketValue: number; - unrealizedPnL: number; - unrealizedPnLPercent: number; - costBasis: number; - lastUpdated: Date; -} - -export interface PortfolioSnapshot { - timestamp: Date; - totalValue: number; - cashBalance: number; - positions: Position[]; - totalReturn: number; - totalReturnPercent: number; - dayChange: number; - dayChangePercent: number; -} - -export interface Trade { - id: string; - symbol: string; - quantity: number; - price: number; - side: 'buy' | 'sell'; - timestamp: Date; - commission: number; -} - -export class PortfolioManager { - private logger = getLogger('PortfolioManager'); - private positions: Map = new Map(); - private trades: Trade[] = []; - private cashBalance: number = 100000; // Starting cash - - constructor(initialCash: number = 100000) { - this.cashBalance = initialCash; - } - - addTrade(trade: Trade): void { - this.trades.push(trade); - this.updatePosition(trade); - logger.info(`Trade added: ${trade.symbol} ${trade.side} ${trade.quantity} @ ${trade.price}`); - } - - private updatePosition(trade: Trade): void { - const existing = this.positions.get(trade.symbol); - - if (!existing) { - // New position - if (trade.side === 'buy') { - this.positions.set(trade.symbol, { - symbol: trade.symbol, - quantity: trade.quantity, - averagePrice: trade.price, - currentPrice: trade.price, - marketValue: trade.quantity * trade.price, - unrealizedPnL: 0, - unrealizedPnLPercent: 0, - costBasis: trade.quantity * trade.price + trade.commission, - lastUpdated: trade.timestamp, - }); - this.cashBalance -= trade.quantity * trade.price + trade.commission; - } - return; - } - - // Update existing position - if (trade.side === 'buy') { - const newQuantity = existing.quantity + trade.quantity; - const newCostBasis = existing.costBasis + trade.quantity * trade.price + trade.commission; - - existing.quantity = newQuantity; - existing.averagePrice = (newCostBasis - this.getTotalCommissions(trade.symbol)) / newQuantity; - existing.costBasis = newCostBasis; - existing.lastUpdated = trade.timestamp; - - this.cashBalance -= trade.quantity * trade.price + trade.commission; - } else if (trade.side === 'sell') { - existing.quantity -= trade.quantity; - existing.lastUpdated = trade.timestamp; - - const proceeds = trade.quantity * trade.price - trade.commission; - this.cashBalance += proceeds; - - // Remove position if quantity is zero - if (existing.quantity <= 0) { - this.positions.delete(trade.symbol); - } - } - } - - updatePrice(symbol: string, price: number): void { - const position = this.positions.get(symbol); - if (position) { - position.currentPrice = price; - position.marketValue = position.quantity * price; - position.unrealizedPnL = position.marketValue - position.quantity * position.averagePrice; - position.unrealizedPnLPercent = - (position.unrealizedPnL / (position.quantity * position.averagePrice)) * 100; - position.lastUpdated = new Date(); - } - } - - getPosition(symbol: string): Position | undefined { - return this.positions.get(symbol); - } - - getAllPositions(): Position[] { - return Array.from(this.positions.values()); - } - - getPortfolioSnapshot(): PortfolioSnapshot { - const positions = this.getAllPositions(); - const totalMarketValue = positions.reduce((sum, pos) => sum + pos.marketValue, 0); - const totalValue = totalMarketValue + this.cashBalance; - const totalUnrealizedPnL = positions.reduce((sum, pos) => sum + pos.unrealizedPnL, 0); - - return { - timestamp: new Date(), - totalValue, - cashBalance: this.cashBalance, - positions, - totalReturn: totalUnrealizedPnL, // Simplified - should include realized gains - totalReturnPercent: (totalUnrealizedPnL / (totalValue - totalUnrealizedPnL)) * 100, - dayChange: 0, // TODO: Calculate from previous day - dayChangePercent: 0, - }; - } - - getTrades(symbol?: string): Trade[] { - if (symbol) { - return this.trades.filter(trade => trade.symbol === symbol); - } - return this.trades; - } - - private getTotalCommissions(symbol: string): number { - return this.trades - .filter(trade => trade.symbol === symbol) - .reduce((sum, trade) => sum + trade.commission, 0); - } - - getCashBalance(): number { - return this.cashBalance; - } - - getNetLiquidationValue(): number { - const positions = this.getAllPositions(); - const positionValue = positions.reduce((sum, pos) => sum + pos.marketValue, 0); - return positionValue + this.cashBalance; - } -} diff --git a/apps/portfolio-service/tsconfig.json b/apps/portfolio-service/tsconfig.json deleted file mode 100644 index 712651c..0000000 --- a/apps/portfolio-service/tsconfig.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "./dist", - "rootDir": "./src" - }, - "include": ["src/**/*"], - "exclude": [ - "node_modules", - "dist", - "**/*.test.ts", - "**/*.spec.ts", - "**/test/**", - "**/tests/**", - "**/__tests__/**" - ], - "references": [ - { "path": "../../libs/types" }, - { "path": "../../libs/config" }, - { "path": "../../libs/logger" }, - { "path": "../../libs/utils" }, - { "path": "../../libs/postgres-client" }, - { "path": "../../libs/event-bus" }, - { "path": "../../libs/shutdown" } - ] -} diff --git a/apps/portfolio-service/turbo.json b/apps/portfolio-service/turbo.json deleted file mode 100644 index 679a273..0000000 --- a/apps/portfolio-service/turbo.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "extends": ["//"], - "tasks": { - "build": { - "dependsOn": [ - "@stock-bot/types#build", - "@stock-bot/config#build", - "@stock-bot/logger#build", - "@stock-bot/utils#build", - "@stock-bot/postgres-client#build", - "@stock-bot/event-bus#build", - "@stock-bot/shutdown#build" - ], - "outputs": ["dist/**"], - "inputs": [ - "src/**", - "package.json", - "tsconfig.json", - "!**/*.test.ts", - "!**/*.spec.ts", - "!**/test/**", - "!**/tests/**", - "!**/__tests__/**" - ] - } - } -} diff --git a/apps/processing-service/package.json b/apps/processing-service/package.json deleted file mode 100644 index 0d45f89..0000000 --- a/apps/processing-service/package.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "@stock-bot/processing-service", - "version": "1.0.0", - "description": "Combined data processing and technical indicators service", - "main": "dist/index.js", - "type": "module", - "scripts": { - "dev": "bun --watch src/index.ts", - "build": "bun build src/index.ts --outdir dist --target node", - "start": "bun dist/index.js", - "test": "bun test", - "clean": "rm -rf dist" - }, - "dependencies": { - "@stock-bot/config": "*", - "@stock-bot/logger": "*", - "@stock-bot/shutdown": "*", - "@stock-bot/types": "*", - "@stock-bot/utils": "*", - "@stock-bot/event-bus": "*", - "@stock-bot/vector-engine": "*", - "hono": "^4.0.0" - }, - "devDependencies": { - "typescript": "^5.0.0" - } -} diff --git a/apps/processing-service/src/index.ts b/apps/processing-service/src/index.ts deleted file mode 100644 index 1a5e400..0000000 --- a/apps/processing-service/src/index.ts +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Processing Service - Data processing and technical indicators service - */ -import { Hono } from 'hono'; -import { loadEnvVariables } from '@stock-bot/config'; -import { getLogger, shutdownLoggers } from '@stock-bot/logger'; -import { Shutdown } from '@stock-bot/shutdown'; -import { healthRoutes, processingRoutes } from './routes'; -import { processingServiceManager } from './services'; - -// Load environment variables -loadEnvVariables(); - -const app = new Hono(); -const logger = getLogger('processing-service'); -const PORT = parseInt(process.env.PROCESSING_SERVICE_PORT || '3003'); -let server: ReturnType | null = null; - -// Initialize shutdown manager with 15 second timeout -const shutdown = Shutdown.getInstance({ timeout: 15000 }); - -// Register all routes -app.route('', healthRoutes); -app.route('/api/processing', processingRoutes); - -// Initialize services -async function initializeServices() { - logger.info('Initializing processing service...'); - - try { - // Initialize processing service manager - logger.info('Starting processing service manager initialization...'); - await processingServiceManager.initialize(); - logger.info('Processing service manager initialized'); - - // TODO: Add other service initializations here as needed - // - MongoDB client for reading/writing processed data - // - Event bus for listening to data events - // - Technical indicators engines - // - Vector engines for similarity calculations - - logger.info('All services initialized successfully'); - } catch (error) { - logger.error('Failed to initialize services', { error }); - throw error; - } -} - -// Start server -async function startServer() { - await initializeServices(); - - // Start the HTTP server using Bun's native serve - server = Bun.serve({ - port: PORT, - fetch: app.fetch, - development: process.env.NODE_ENV === 'development', - }); - - logger.info(`Processing Service started on port ${PORT}`); -} - -// Register shutdown handlers -shutdown.onShutdown(async () => { - if (server) { - logger.info('Stopping HTTP server...'); - try { - server.stop(); - logger.info('HTTP server stopped successfully'); - } catch (error) { - logger.error('Error stopping HTTP server', { error }); - } - } -}); - -shutdown.onShutdown(async () => { - logger.info('Shutting down processing service manager...'); - try { - await processingServiceManager.shutdown(); - logger.info('Processing service manager shut down successfully'); - } catch (error) { - logger.error('Error shutting down processing service manager', { error }); - } -}); - -shutdown.onShutdown(async () => { - logger.info('Shutting down loggers...'); - try { - await shutdownLoggers(); - logger.info('Loggers shut down successfully'); - } catch (error) { - logger.error('Error shutting down loggers', { error }); - } -}); - -// Handle uncaught exceptions and unhandled rejections -process.on('uncaughtException', error => { - logger.error('Uncaught exception', { error }); - shutdown.shutdownAndExit('uncaughtException', 1); -}); - -process.on('unhandledRejection', (reason, promise) => { - logger.error('Unhandled rejection', { reason, promise }); - shutdown.shutdownAndExit('unhandledRejection', 1); -}); - -// Handle shutdown signals -process.on('SIGINT', () => { - logger.info('Received SIGINT signal'); - shutdown.shutdownAndExit('SIGINT', 0); -}); - -process.on('SIGTERM', () => { - logger.info('Received SIGTERM signal'); - shutdown.shutdownAndExit('SIGTERM', 0); -}); - -// Start the service -startServer().catch(error => { - logger.error('Failed to start processing service', { error }); - process.exit(1); -}); diff --git a/apps/processing-service/src/routes/health.routes.ts b/apps/processing-service/src/routes/health.routes.ts deleted file mode 100644 index 6149964..0000000 --- a/apps/processing-service/src/routes/health.routes.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Health and status routes for processing service - */ -import { Hono } from 'hono'; - -const healthRoutes = new Hono(); - -// Health check endpoint -healthRoutes.get('/health', c => { - return c.json({ - status: 'healthy', - service: 'processing-service', - timestamp: new Date().toISOString(), - version: '1.0.0', - }); -}); - -// Detailed status endpoint -healthRoutes.get('/status', c => { - return c.json({ - service: 'processing-service', - status: 'running', - uptime: process.uptime(), - memory: process.memoryUsage(), - timestamp: new Date().toISOString(), - environment: process.env.NODE_ENV || 'development', - }); -}); - -// Ready check endpoint -healthRoutes.get('/ready', c => { - // TODO: Add checks for service dependencies - // - Database connections - // - Event bus connections - // - Required resources - - return c.json({ - status: 'ready', - service: 'processing-service', - timestamp: new Date().toISOString(), - }); -}); - -export { healthRoutes }; diff --git a/apps/processing-service/src/routes/index.ts b/apps/processing-service/src/routes/index.ts deleted file mode 100644 index f271d84..0000000 --- a/apps/processing-service/src/routes/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -/** - * Route exports for processing service - */ -export { healthRoutes } from './health.routes'; -export { processingRoutes } from './processing.routes'; diff --git a/apps/processing-service/src/routes/processing.routes.ts b/apps/processing-service/src/routes/processing.routes.ts deleted file mode 100644 index 437edb7..0000000 --- a/apps/processing-service/src/routes/processing.routes.ts +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Processing routes for data processing operations - */ -import { Hono } from 'hono'; -import { getLogger } from '@stock-bot/logger'; -import { processingServiceManager } from '../services'; - -const processingRoutes = new Hono(); -const logger = getLogger('processing-routes'); - -// Process data endpoint -processingRoutes.post('/process', async c => { - try { - const body = await c.req.json(); - - logger.info('Processing request received', { - dataType: body.type, - recordCount: body.data?.length || 0, - }); - - // Use processing service manager to handle the request - const result = await processingServiceManager.processData( - body.type || 'unknown', - body.data || [] - ); - - return c.json({ - status: 'success', - message: 'Data processing completed', - result, - timestamp: new Date().toISOString(), - }); - } catch (error) { - logger.error('Processing error', { error }); - return c.json( - { - status: 'error', - message: 'Processing failed', - error: error instanceof Error ? error.message : 'Unknown error', - }, - 500 - ); - } -}); - -// Get processing status -processingRoutes.get('/status', c => { - const status = processingServiceManager.getStatus(); - return c.json({ - ...status, - activeJobs: 0, // TODO: Implement job tracking - queueLength: 0, // TODO: Implement queue monitoring - lastProcessed: null, // TODO: Track last processing time - }); -}); - -export { processingRoutes }; diff --git a/apps/processing-service/src/services/index.ts b/apps/processing-service/src/services/index.ts deleted file mode 100644 index a57545c..0000000 --- a/apps/processing-service/src/services/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Services exports for processing service - */ -export { ProcessingServiceManager, processingServiceManager } from './processing.service'; diff --git a/apps/processing-service/src/services/processing.service.ts b/apps/processing-service/src/services/processing.service.ts deleted file mode 100644 index a9070bf..0000000 --- a/apps/processing-service/src/services/processing.service.ts +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Processing Service Manager - * - * Manages the core processing operations for the processing service - */ -import { getLogger } from '@stock-bot/logger'; - -const logger = getLogger('processing-service-manager'); - -export class ProcessingServiceManager { - private isInitialized = false; - - async initialize(): Promise { - if (this.isInitialized) { - logger.warn('Processing service manager already initialized'); - return; - } - - logger.info('Initializing processing service manager...'); - - try { - // TODO: Initialize processing components - // - Technical indicators engine - // - Data transformation pipeline - // - Event listeners for data events - // - Job queues for processing tasks - - this.isInitialized = true; - logger.info('Processing service manager initialized successfully'); - } catch (error) { - logger.error('Failed to initialize processing service manager', { error }); - throw error; - } - } - - async shutdown(): Promise { - if (!this.isInitialized) { - logger.warn('Processing service manager not initialized, nothing to shutdown'); - return; - } - - logger.info('Shutting down processing service manager...'); - - try { - // TODO: Cleanup processing components - // - Stop job processing - // - Close database connections - // - Cleanup event listeners - - this.isInitialized = false; - logger.info('Processing service manager shut down successfully'); - } catch (error) { - logger.error('Error shutting down processing service manager', { error }); - throw error; - } - } - - /** - * Process data with technical indicators - */ - async processData( - dataType: string, - data: unknown[] - ): Promise<{ - status: string; - dataType: string; - inputCount: number; - outputCount: number; - processedAt: Date; - processingTime: number; - }> { - if (!this.isInitialized) { - throw new Error('Processing service manager not initialized'); - } - - logger.info(`Processing ${data.length} records of type: ${dataType}`); - - try { - // TODO: Implement actual processing logic - // - Apply technical indicators - // - Calculate signals - // - Transform data format - // - Save processed results - - const result = { - status: 'success', - dataType, - inputCount: data.length, - outputCount: data.length, // Placeholder - processedAt: new Date(), - processingTime: 0, // Placeholder - }; - - logger.info('Data processing completed', result); - return result; - } catch (error) { - logger.error('Data processing failed', { error, dataType, inputCount: data.length }); - throw error; - } - } - - /** - * Get processing service status - */ - getStatus() { - return { - initialized: this.isInitialized, - status: this.isInitialized ? 'ready' : 'not_initialized', - timestamp: new Date().toISOString(), - }; - } -} - -// Export singleton instance -export const processingServiceManager = new ProcessingServiceManager(); diff --git a/apps/processing-service/tsconfig.json b/apps/processing-service/tsconfig.json deleted file mode 100644 index 028d54a..0000000 --- a/apps/processing-service/tsconfig.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "./dist", - "rootDir": "./src" - }, - "include": ["src/**/*"], - "exclude": [ - "node_modules", - "dist", - "**/*.test.ts", - "**/*.spec.ts", - "**/test/**", - "**/tests/**", - "**/__tests__/**" - ], - "references": [ - { "path": "../../libs/types" }, - { "path": "../../libs/config" }, - { "path": "../../libs/logger" }, - { "path": "../../libs/utils" }, - { "path": "../../libs/data-frame" }, - { "path": "../../libs/vector-engine" }, - { "path": "../../libs/mongodb-client" }, - { "path": "../../libs/event-bus" }, - { "path": "../../libs/shutdown" } - ] -} diff --git a/apps/processing-service/turbo.json b/apps/processing-service/turbo.json deleted file mode 100644 index 2b9cd1e..0000000 --- a/apps/processing-service/turbo.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "extends": ["//"], - "tasks": { - "build": { - "dependsOn": [ - "@stock-bot/types#build", - "@stock-bot/config#build", - "@stock-bot/logger#build", - "@stock-bot/utils#build", - "@stock-bot/data-frame#build", - "@stock-bot/vector-engine#build", - "@stock-bot/mongodb-client#build", - "@stock-bot/event-bus#build", - "@stock-bot/shutdown#build" - ], - "outputs": ["dist/**"], - "inputs": [ - "src/**", - "package.json", - "tsconfig.json", - "!**/*.test.ts", - "!**/*.spec.ts", - "!**/test/**", - "!**/tests/**", - "!**/__tests__/**" - ] - } - } -} diff --git a/apps/strategy-service/package.json b/apps/strategy-service/package.json deleted file mode 100644 index 5e76503..0000000 --- a/apps/strategy-service/package.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "@stock-bot/strategy-service", - "version": "1.0.0", - "description": "Combined strategy execution and multi-mode backtesting service", - "main": "dist/index.js", - "type": "module", - "scripts": { - "devvvvv": "bun --watch src/index.ts", - "build": "bun build src/index.ts --outdir dist --target node", - "start": "bun dist/index.js", - "test": "bun test", - "clean": "rm -rf dist", - "backtest": "bun src/cli/index.ts", - "optimize": "bun src/cli/index.ts optimize", - "cli": "bun src/cli/index.ts" - }, - "dependencies": { - "@stock-bot/config": "*", - "@stock-bot/logger": "*", - "@stock-bot/types": "*", - "@stock-bot/utils": "*", - "@stock-bot/event-bus": "*", - "@stock-bot/strategy-engine": "*", - "@stock-bot/vector-engine": "*", - "@stock-bot/data-frame": "*", - "@stock-bot/questdb-client": "*", - "hono": "^4.0.0", - "commander": "^11.0.0" - }, - "devDependencies": { - "@types/node": "^20.0.0", - "typescript": "^5.0.0" - } -} diff --git a/apps/strategy-service/src/backtesting/modes/event-mode.ts b/apps/strategy-service/src/backtesting/modes/event-mode.ts deleted file mode 100644 index 0d4bfb4..0000000 --- a/apps/strategy-service/src/backtesting/modes/event-mode.ts +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Event-Driven Backtesting Mode - * Processes data point by point with realistic order execution - */ -import { ExecutionMode, MarketData, Order, OrderResult } from '../../framework/execution-mode'; - -export interface BacktestConfig { - startDate: Date; - endDate: Date; - initialCapital: number; - slippageModel?: string; - commissionModel?: string; -} - -export class EventMode extends ExecutionMode { - name = 'event-driven'; - private simulationTime: Date; - private historicalData: Map = new Map(); - - constructor(private config: BacktestConfig) { - super(); - this.simulationTime = config.startDate; - } - - async executeOrder(order: Order): Promise { - this.logger.debug('Simulating order execution', { - orderId: order.id, - simulationTime: this.simulationTime, - }); - - // TODO: Implement realistic order simulation - // Include slippage, commission, market impact - const simulatedResult: OrderResult = { - orderId: order.id, - symbol: order.symbol, - executedQuantity: order.quantity, - executedPrice: 100, // TODO: Get realistic price - commission: 1.0, // TODO: Calculate based on commission model - slippage: 0.01, // TODO: Calculate based on slippage model - timestamp: this.simulationTime, - executionTime: 50, // ms - }; - - return simulatedResult; - } - - getCurrentTime(): Date { - return this.simulationTime; - } - - async getMarketData(symbol: string): Promise { - const data = this.historicalData.get(symbol) || []; - const currentData = data.find(d => d.timestamp <= this.simulationTime); - - if (!currentData) { - throw new Error(`No market data available for ${symbol} at ${this.simulationTime}`); - } - - return currentData; - } - - async publishEvent(event: string, data: any): Promise { - // In-memory event bus for simulation - this.logger.debug('Publishing simulation event', { event, data }); - } - - // Simulation control methods - advanceTime(newTime: Date): void { - this.simulationTime = newTime; - } - - loadHistoricalData(symbol: string, data: MarketData[]): void { - this.historicalData.set(symbol, data); - } -} diff --git a/apps/strategy-service/src/backtesting/modes/hybrid-mode.ts b/apps/strategy-service/src/backtesting/modes/hybrid-mode.ts deleted file mode 100644 index 2f4f4fc..0000000 --- a/apps/strategy-service/src/backtesting/modes/hybrid-mode.ts +++ /dev/null @@ -1,424 +0,0 @@ -import { DataFrame } from '@stock-bot/data-frame'; -import { EventBus } from '@stock-bot/event-bus'; -import { getLogger } from '@stock-bot/logger'; -import { VectorEngine, VectorizedBacktestResult } from '@stock-bot/vector-engine'; -import { BacktestContext, BacktestResult, ExecutionMode } from '../framework/execution-mode'; -import { EventMode } from './event-mode'; -import VectorizedMode from './vectorized-mode'; - -export interface HybridModeConfig { - vectorizedThreshold: number; // Switch to vectorized if data points > threshold - warmupPeriod: number; // Number of periods for initial vectorized calculation - eventDrivenRealtime: boolean; // Use event-driven for real-time portions - optimizeIndicators: boolean; // Pre-calculate indicators vectorized - batchSize: number; // Size of batches for hybrid processing -} - -export class HybridMode extends ExecutionMode { - private vectorEngine: VectorEngine; - private eventMode: EventMode; - private vectorizedMode: VectorizedMode; - private config: HybridModeConfig; - private precomputedIndicators: Map = new Map(); - private currentIndex: number = 0; - - constructor(context: BacktestContext, eventBus: EventBus, config: HybridModeConfig = {}) { - super(context, eventBus); - - this.config = { - vectorizedThreshold: 50000, - warmupPeriod: 1000, - eventDrivenRealtime: true, - optimizeIndicators: true, - batchSize: 10000, - ...config, - }; - - this.vectorEngine = new VectorEngine(); - this.eventMode = new EventMode(context, eventBus); - this.vectorizedMode = new VectorizedMode(context, eventBus); - - this.logger = getLogger('hybrid-mode'); - } - - async initialize(): Promise { - await super.initialize(); - - // Initialize both modes - await this.eventMode.initialize(); - await this.vectorizedMode.initialize(); - - this.logger.info('Hybrid mode initialized', { - backtestId: this.context.backtestId, - config: this.config, - }); - } - - async execute(): Promise { - const startTime = Date.now(); - this.logger.info('Starting hybrid backtest execution'); - - try { - // Determine execution strategy based on data size - const dataSize = await this.estimateDataSize(); - - if (dataSize <= this.config.vectorizedThreshold) { - // Small dataset: use pure vectorized approach - this.logger.info('Using pure vectorized approach for small dataset', { dataSize }); - return await this.vectorizedMode.execute(); - } - - // Large dataset: use hybrid approach - this.logger.info('Using hybrid approach for large dataset', { dataSize }); - return await this.executeHybrid(startTime); - } catch (error) { - this.logger.error('Hybrid backtest failed', { - error, - backtestId: this.context.backtestId, - }); - - await this.eventBus.publishBacktestUpdate(this.context.backtestId, 0, { - status: 'failed', - error: error.message, - }); - - throw error; - } - } - - private async executeHybrid(startTime: number): Promise { - // Phase 1: Vectorized warmup and indicator pre-computation - const warmupResult = await this.executeWarmupPhase(); - - // Phase 2: Event-driven processing with pre-computed indicators - const eventResult = await this.executeEventPhase(warmupResult); - - // Phase 3: Combine results - const combinedResult = this.combineResults(warmupResult, eventResult, startTime); - - await this.eventBus.publishBacktestUpdate(this.context.backtestId, 100, { - status: 'completed', - result: combinedResult, - }); - - this.logger.info('Hybrid backtest completed', { - backtestId: this.context.backtestId, - duration: Date.now() - startTime, - totalTrades: combinedResult.trades.length, - warmupTrades: warmupResult.trades.length, - eventTrades: eventResult.trades.length, - }); - - return combinedResult; - } - - private async executeWarmupPhase(): Promise { - this.logger.info('Executing vectorized warmup phase', { - warmupPeriod: this.config.warmupPeriod, - }); - - // Load warmup data - const warmupData = await this.loadWarmupData(); - const dataFrame = this.createDataFrame(warmupData); - - // Pre-compute indicators for entire dataset if optimization is enabled - if (this.config.optimizeIndicators) { - await this.precomputeIndicators(dataFrame); - } - - // Run vectorized backtest on warmup period - const strategyCode = this.generateStrategyCode(); - const vectorResult = await this.vectorEngine.executeVectorizedStrategy( - dataFrame.head(this.config.warmupPeriod), - strategyCode - ); - - // Convert to standard format - return this.convertVectorizedResult(vectorResult, Date.now()); - } - - private async executeEventPhase(warmupResult: BacktestResult): Promise { - this.logger.info('Executing event-driven phase'); - - // Set up event mode with warmup context - this.currentIndex = this.config.warmupPeriod; - - // Create modified context for event phase - const eventContext: BacktestContext = { - ...this.context, - initialPortfolio: this.extractFinalPortfolio(warmupResult), - }; - - // Execute event-driven backtest for remaining data - const eventMode = new EventMode(eventContext, this.eventBus); - await eventMode.initialize(); - - // Override indicator calculations to use pre-computed values - if (this.config.optimizeIndicators) { - this.overrideIndicatorCalculations(eventMode); - } - - return await eventMode.execute(); - } - - private async precomputeIndicators(dataFrame: DataFrame): Promise { - this.logger.info('Pre-computing indicators vectorized'); - - const close = dataFrame.getColumn('close'); - const high = dataFrame.getColumn('high'); - const low = dataFrame.getColumn('low'); - - // Import technical indicators from vector engine - const { TechnicalIndicators } = await import('@stock-bot/vector-engine'); - - // Pre-compute common indicators - this.precomputedIndicators.set('sma_20', TechnicalIndicators.sma(close, 20)); - this.precomputedIndicators.set('sma_50', TechnicalIndicators.sma(close, 50)); - this.precomputedIndicators.set('ema_12', TechnicalIndicators.ema(close, 12)); - this.precomputedIndicators.set('ema_26', TechnicalIndicators.ema(close, 26)); - this.precomputedIndicators.set('rsi', TechnicalIndicators.rsi(close)); - this.precomputedIndicators.set('atr', TechnicalIndicators.atr(high, low, close)); - - const macd = TechnicalIndicators.macd(close); - this.precomputedIndicators.set('macd', macd.macd); - this.precomputedIndicators.set('macd_signal', macd.signal); - this.precomputedIndicators.set('macd_histogram', macd.histogram); - - const bb = TechnicalIndicators.bollingerBands(close); - this.precomputedIndicators.set('bb_upper', bb.upper); - this.precomputedIndicators.set('bb_middle', bb.middle); - this.precomputedIndicators.set('bb_lower', bb.lower); - - this.logger.info('Indicators pre-computed', { - indicators: Array.from(this.precomputedIndicators.keys()), - }); - } - - private overrideIndicatorCalculations(eventMode: EventMode): void { - // Override the event mode's indicator calculations to use pre-computed values - // This is a simplified approach - in production you'd want a more sophisticated interface - const _originalCalculateIndicators = (eventMode as any).calculateIndicators; - - (eventMode as any).calculateIndicators = (symbol: string, index: number) => { - const indicators: Record = {}; - - for (const [name, values] of this.precomputedIndicators.entries()) { - if (index < values.length) { - indicators[name] = values[index]; - } - } - - return indicators; - }; - } - - private async estimateDataSize(): Promise { - // Estimate the number of data points for the backtest period - const startTime = new Date(this.context.startDate).getTime(); - const endTime = new Date(this.context.endDate).getTime(); - const timeRange = endTime - startTime; - - // Assume 1-minute intervals (60000ms) - const estimatedPoints = Math.floor(timeRange / 60000); - - this.logger.debug('Estimated data size', { - timeRange, - estimatedPoints, - threshold: this.config.vectorizedThreshold, - }); - - return estimatedPoints; - } - - private async loadWarmupData(): Promise { - // Load historical data for warmup phase - // This should load more data than just the warmup period for indicator calculations - const data = []; - const startTime = new Date(this.context.startDate).getTime(); - const warmupEndTime = startTime + this.config.warmupPeriod * 60000; - - // Add extra lookback for indicator calculations - const lookbackTime = startTime - 200 * 60000; // 200 periods lookback - - for (let timestamp = lookbackTime; timestamp <= warmupEndTime; timestamp += 60000) { - const basePrice = 100 + Math.sin(timestamp / 1000000) * 10; - const volatility = 0.02; - - const open = basePrice + (Math.random() - 0.5) * volatility * basePrice; - const close = open + (Math.random() - 0.5) * volatility * basePrice; - const high = Math.max(open, close) + Math.random() * volatility * basePrice; - const low = Math.min(open, close) - Math.random() * volatility * basePrice; - const volume = Math.floor(Math.random() * 10000) + 1000; - - data.push({ - timestamp, - symbol: this.context.symbol, - open, - high, - low, - close, - volume, - }); - } - - return data; - } - - private createDataFrame(data: any[]): DataFrame { - return new DataFrame(data, { - columns: ['timestamp', 'symbol', 'open', 'high', 'low', 'close', 'volume'], - dtypes: { - timestamp: 'number', - symbol: 'string', - open: 'number', - high: 'number', - low: 'number', - close: 'number', - volume: 'number', - }, - }); - } - - private generateStrategyCode(): string { - // Generate strategy code based on context - const strategy = this.context.strategy; - - if (strategy.type === 'sma_crossover') { - return 'sma_crossover'; - } - - return strategy.code || 'sma_crossover'; - } - - private convertVectorizedResult( - vectorResult: VectorizedBacktestResult, - startTime: number - ): BacktestResult { - return { - backtestId: this.context.backtestId, - strategy: this.context.strategy, - symbol: this.context.symbol, - startDate: this.context.startDate, - endDate: this.context.endDate, - mode: 'hybrid-vectorized', - duration: Date.now() - startTime, - trades: vectorResult.trades.map(trade => ({ - id: `trade_${trade.entryIndex}_${trade.exitIndex}`, - symbol: this.context.symbol, - side: trade.side, - entryTime: vectorResult.timestamps[trade.entryIndex], - exitTime: vectorResult.timestamps[trade.exitIndex], - entryPrice: trade.entryPrice, - exitPrice: trade.exitPrice, - quantity: trade.quantity, - pnl: trade.pnl, - commission: 0, - slippage: 0, - })), - performance: { - totalReturn: vectorResult.metrics.totalReturns, - sharpeRatio: vectorResult.metrics.sharpeRatio, - maxDrawdown: vectorResult.metrics.maxDrawdown, - winRate: vectorResult.metrics.winRate, - profitFactor: vectorResult.metrics.profitFactor, - totalTrades: vectorResult.metrics.totalTrades, - winningTrades: vectorResult.trades.filter(t => t.pnl > 0).length, - losingTrades: vectorResult.trades.filter(t => t.pnl <= 0).length, - avgTrade: vectorResult.metrics.avgTrade, - avgWin: - vectorResult.trades.filter(t => t.pnl > 0).reduce((sum, t) => sum + t.pnl, 0) / - vectorResult.trades.filter(t => t.pnl > 0).length || 0, - avgLoss: - vectorResult.trades.filter(t => t.pnl <= 0).reduce((sum, t) => sum + t.pnl, 0) / - vectorResult.trades.filter(t => t.pnl <= 0).length || 0, - largestWin: Math.max(...vectorResult.trades.map(t => t.pnl), 0), - largestLoss: Math.min(...vectorResult.trades.map(t => t.pnl), 0), - }, - equity: vectorResult.equity, - drawdown: vectorResult.metrics.drawdown, - metadata: { - mode: 'hybrid-vectorized', - dataPoints: vectorResult.timestamps.length, - signals: Object.keys(vectorResult.signals), - optimizations: ['vectorized_warmup', 'precomputed_indicators'], - }, - }; - } - - private extractFinalPortfolio(warmupResult: BacktestResult): any { - // Extract the final portfolio state from warmup phase - const finalEquity = warmupResult.equity[warmupResult.equity.length - 1] || 10000; - - return { - cash: finalEquity, - positions: [], // Simplified - in production would track actual positions - equity: finalEquity, - }; - } - - private combineResults( - warmupResult: BacktestResult, - eventResult: BacktestResult, - startTime: number - ): BacktestResult { - // Combine results from both phases - const combinedTrades = [...warmupResult.trades, ...eventResult.trades]; - const combinedEquity = [...warmupResult.equity, ...eventResult.equity]; - const combinedDrawdown = [...(warmupResult.drawdown || []), ...(eventResult.drawdown || [])]; - - // Recalculate combined performance metrics - const totalPnL = combinedTrades.reduce((sum, trade) => sum + trade.pnl, 0); - const winningTrades = combinedTrades.filter(t => t.pnl > 0); - const losingTrades = combinedTrades.filter(t => t.pnl <= 0); - - const grossProfit = winningTrades.reduce((sum, t) => sum + t.pnl, 0); - const grossLoss = Math.abs(losingTrades.reduce((sum, t) => sum + t.pnl, 0)); - - return { - backtestId: this.context.backtestId, - strategy: this.context.strategy, - symbol: this.context.symbol, - startDate: this.context.startDate, - endDate: this.context.endDate, - mode: 'hybrid', - duration: Date.now() - startTime, - trades: combinedTrades, - performance: { - totalReturn: - (combinedEquity[combinedEquity.length - 1] - combinedEquity[0]) / combinedEquity[0], - sharpeRatio: eventResult.performance.sharpeRatio, // Use event result for more accurate calculation - maxDrawdown: Math.max(...combinedDrawdown), - winRate: winningTrades.length / combinedTrades.length, - profitFactor: grossLoss !== 0 ? grossProfit / grossLoss : Infinity, - totalTrades: combinedTrades.length, - winningTrades: winningTrades.length, - losingTrades: losingTrades.length, - avgTrade: totalPnL / combinedTrades.length, - avgWin: grossProfit / winningTrades.length || 0, - avgLoss: grossLoss / losingTrades.length || 0, - largestWin: Math.max(...combinedTrades.map(t => t.pnl), 0), - largestLoss: Math.min(...combinedTrades.map(t => t.pnl), 0), - }, - equity: combinedEquity, - drawdown: combinedDrawdown, - metadata: { - mode: 'hybrid', - phases: ['vectorized-warmup', 'event-driven'], - warmupPeriod: this.config.warmupPeriod, - optimizations: ['precomputed_indicators', 'hybrid_execution'], - warmupTrades: warmupResult.trades.length, - eventTrades: eventResult.trades.length, - }, - }; - } - - async cleanup(): Promise { - await super.cleanup(); - await this.eventMode.cleanup(); - await this.vectorizedMode.cleanup(); - this.precomputedIndicators.clear(); - this.logger.info('Hybrid mode cleanup completed'); - } -} - -export default HybridMode; diff --git a/apps/strategy-service/src/backtesting/modes/live-mode.ts b/apps/strategy-service/src/backtesting/modes/live-mode.ts deleted file mode 100644 index 9beee49..0000000 --- a/apps/strategy-service/src/backtesting/modes/live-mode.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Live Trading Mode - * Executes orders through real brokers - */ -import { ExecutionMode, MarketData, Order, OrderResult } from '../../framework/execution-mode'; - -export class LiveMode extends ExecutionMode { - name = 'live'; - - async executeOrder(order: Order): Promise { - this.logger.info('Executing live order', { orderId: order.id }); - - // TODO: Implement real broker integration - // This will connect to actual brokerage APIs - throw new Error('Live broker integration not implemented yet'); - } - - getCurrentTime(): Date { - return new Date(); // Real time - } - - async getMarketData(_symbol: string): Promise { - // TODO: Get live market data - throw new Error('Live market data fetching not implemented yet'); - } - - async publishEvent(event: string, data: any): Promise { - // TODO: Publish to real event bus (Dragonfly) - this.logger.debug('Publishing event', { event, data }); - } -} diff --git a/apps/strategy-service/src/backtesting/modes/vectorized-mode.ts b/apps/strategy-service/src/backtesting/modes/vectorized-mode.ts deleted file mode 100644 index ed23921..0000000 --- a/apps/strategy-service/src/backtesting/modes/vectorized-mode.ts +++ /dev/null @@ -1,236 +0,0 @@ -import { DataFrame } from '@stock-bot/data-frame'; -import { EventBus } from '@stock-bot/event-bus'; -import { getLogger } from '@stock-bot/logger'; -import { VectorEngine, VectorizedBacktestResult } from '@stock-bot/vector-engine'; -import { BacktestContext, BacktestResult, ExecutionMode } from '../framework/execution-mode'; - -export interface VectorizedModeConfig { - batchSize?: number; - enableOptimization?: boolean; - parallelProcessing?: boolean; -} - -export class VectorizedMode extends ExecutionMode { - private vectorEngine: VectorEngine; - private config: VectorizedModeConfig; - private logger = getLogger('vectorized-mode'); - - constructor(context: BacktestContext, eventBus: EventBus, config: VectorizedModeConfig = {}) { - super(context, eventBus); - this.vectorEngine = new VectorEngine(); - this.config = { - batchSize: 10000, - enableOptimization: true, - parallelProcessing: true, - ...config, - }; - } - - async initialize(): Promise { - await super.initialize(); - this.logger.info('Vectorized mode initialized', { - backtestId: this.context.backtestId, - config: this.config, - }); - } - - async execute(): Promise { - const startTime = Date.now(); - this.logger.info('Starting vectorized backtest execution'); - - try { - // Load all data at once for vectorized processing - const data = await this.loadHistoricalData(); - - // Convert to DataFrame format - const dataFrame = this.createDataFrame(data); - - // Execute vectorized strategy - const strategyCode = this.generateStrategyCode(); - const vectorResult = await this.vectorEngine.executeVectorizedStrategy( - dataFrame, - strategyCode - ); - - // Convert to standard backtest result format - const result = this.convertVectorizedResult(vectorResult, startTime); - - // Emit completion event - await this.eventBus.publishBacktestUpdate(this.context.backtestId, 100, { - status: 'completed', - result, - }); - - this.logger.info('Vectorized backtest completed', { - backtestId: this.context.backtestId, - duration: Date.now() - startTime, - totalTrades: result.trades.length, - }); - - return result; - } catch (error) { - this.logger.error('Vectorized backtest failed', { - error, - backtestId: this.context.backtestId, - }); - - await this.eventBus.publishBacktestUpdate(this.context.backtestId, 0, { - status: 'failed', - error: error.message, - }); - - throw error; - } - } - - private async loadHistoricalData(): Promise { - // Load all historical data at once - // This is much more efficient than loading tick by tick - const data = []; - - // Simulate loading data (in production, this would be a bulk database query) - const startTime = new Date(this.context.startDate).getTime(); - const endTime = new Date(this.context.endDate).getTime(); - const interval = 60000; // 1 minute intervals - - for (let timestamp = startTime; timestamp <= endTime; timestamp += interval) { - // Simulate OHLCV data - const basePrice = 100 + Math.sin(timestamp / 1000000) * 10; - const volatility = 0.02; - - const open = basePrice + (Math.random() - 0.5) * volatility * basePrice; - const close = open + (Math.random() - 0.5) * volatility * basePrice; - const high = Math.max(open, close) + Math.random() * volatility * basePrice; - const low = Math.min(open, close) - Math.random() * volatility * basePrice; - const volume = Math.floor(Math.random() * 10000) + 1000; - - data.push({ - timestamp, - symbol: this.context.symbol, - open, - high, - low, - close, - volume, - }); - } - - return data; - } - - private createDataFrame(data: any[]): DataFrame { - return new DataFrame(data, { - columns: ['timestamp', 'symbol', 'open', 'high', 'low', 'close', 'volume'], - dtypes: { - timestamp: 'number', - symbol: 'string', - open: 'number', - high: 'number', - low: 'number', - close: 'number', - volume: 'number', - }, - }); - } - - private generateStrategyCode(): string { - // Convert strategy configuration to vectorized strategy code - // This is a simplified example - in production you'd have a more sophisticated compiler - const strategy = this.context.strategy; - - if (strategy.type === 'sma_crossover') { - return 'sma_crossover'; - } - - // Add more strategy types as needed - return strategy.code || 'sma_crossover'; - } - - private convertVectorizedResult( - vectorResult: VectorizedBacktestResult, - startTime: number - ): BacktestResult { - return { - backtestId: this.context.backtestId, - strategy: this.context.strategy, - symbol: this.context.symbol, - startDate: this.context.startDate, - endDate: this.context.endDate, - mode: 'vectorized', - duration: Date.now() - startTime, - trades: vectorResult.trades.map(trade => ({ - id: `trade_${trade.entryIndex}_${trade.exitIndex}`, - symbol: this.context.symbol, - side: trade.side, - entryTime: vectorResult.timestamps[trade.entryIndex], - exitTime: vectorResult.timestamps[trade.exitIndex], - entryPrice: trade.entryPrice, - exitPrice: trade.exitPrice, - quantity: trade.quantity, - pnl: trade.pnl, - commission: 0, // Simplified - slippage: 0, - })), - performance: { - totalReturn: vectorResult.metrics.totalReturns, - sharpeRatio: vectorResult.metrics.sharpeRatio, - maxDrawdown: vectorResult.metrics.maxDrawdown, - winRate: vectorResult.metrics.winRate, - profitFactor: vectorResult.metrics.profitFactor, - totalTrades: vectorResult.metrics.totalTrades, - winningTrades: vectorResult.trades.filter(t => t.pnl > 0).length, - losingTrades: vectorResult.trades.filter(t => t.pnl <= 0).length, - avgTrade: vectorResult.metrics.avgTrade, - avgWin: - vectorResult.trades.filter(t => t.pnl > 0).reduce((sum, t) => sum + t.pnl, 0) / - vectorResult.trades.filter(t => t.pnl > 0).length || 0, - avgLoss: - vectorResult.trades.filter(t => t.pnl <= 0).reduce((sum, t) => sum + t.pnl, 0) / - vectorResult.trades.filter(t => t.pnl <= 0).length || 0, - largestWin: Math.max(...vectorResult.trades.map(t => t.pnl), 0), - largestLoss: Math.min(...vectorResult.trades.map(t => t.pnl), 0), - }, - equity: vectorResult.equity, - drawdown: vectorResult.metrics.drawdown, - metadata: { - mode: 'vectorized', - dataPoints: vectorResult.timestamps.length, - signals: Object.keys(vectorResult.signals), - optimizations: this.config.enableOptimization ? ['vectorized_computation'] : [], - }, - }; - } - - async cleanup(): Promise { - await super.cleanup(); - this.logger.info('Vectorized mode cleanup completed'); - } - - // Batch processing capabilities - async batchBacktest( - strategies: Array<{ id: string; config: any }> - ): Promise> { - this.logger.info('Starting batch vectorized backtest', { - strategiesCount: strategies.length, - }); - - const data = await this.loadHistoricalData(); - const dataFrame = this.createDataFrame(data); - - const strategyConfigs = strategies.map(s => ({ - id: s.id, - code: this.generateStrategyCode(), - })); - - const batchResults = await this.vectorEngine.batchBacktest(dataFrame, strategyConfigs); - const results: Record = {}; - - for (const [strategyId, vectorResult] of Object.entries(batchResults)) { - results[strategyId] = this.convertVectorizedResult(vectorResult, Date.now()); - } - - return results; - } -} - -export default VectorizedMode; diff --git a/apps/strategy-service/src/cli/index.ts b/apps/strategy-service/src/cli/index.ts deleted file mode 100644 index 94182b3..0000000 --- a/apps/strategy-service/src/cli/index.ts +++ /dev/null @@ -1,285 +0,0 @@ -#!/usr/bin/env bun -/** - * Strategy Service CLI - * Command-line interface for running backtests and managing strategies - */ -import { program } from 'commander'; -import { createEventBus } from '@stock-bot/event-bus'; -import { getLogger } from '@stock-bot/logger'; -import { EventMode } from '../backtesting/modes/event-mode'; -import HybridMode from '../backtesting/modes/hybrid-mode'; -import { LiveMode } from '../backtesting/modes/live-mode'; -import VectorizedMode from '../backtesting/modes/vectorized-mode'; -import { BacktestContext } from '../framework/execution-mode'; - -const logger = getLogger('strategy-cli'); - -interface CLIBacktestConfig { - strategy: string; - strategies: string; - symbol: string; - startDate: string; - endDate: string; - mode: 'live' | 'event' | 'vectorized' | 'hybrid'; - initialCapital?: number; - config?: string; - output?: string; - verbose?: boolean; -} - -async function runBacktest(options: CLIBacktestConfig): Promise { - logger.info('Starting backtest from CLI', { options }); - - try { - // Initialize event bus - const eventBus = createEventBus({ - serviceName: 'strategy-cli', - enablePersistence: false, // Disable Redis for CLI - }); - - // Create backtest context - const context: BacktestContext = { - backtestId: `cli_${Date.now()}`, - strategy: { - id: options.strategy, - name: options.strategy, - type: options.strategy, - code: options.strategy, - parameters: {}, - }, - symbol: options.symbol, - startDate: options.startDate, - endDate: options.endDate, - initialCapital: options.initialCapital || 10000, - mode: options.mode, - }; - - // Load additional config if provided - if (options.config) { - const configData = await loadConfig(options.config); - context.strategy.parameters = { ...context.strategy.parameters, ...configData }; - } - - // Create and execute the appropriate mode - let executionMode; - - switch (options.mode) { - case 'live': - executionMode = new LiveMode(context, eventBus); - break; - case 'event': - executionMode = new EventMode(context, eventBus); - break; - case 'vectorized': - executionMode = new VectorizedMode(context, eventBus); - break; - case 'hybrid': - executionMode = new HybridMode(context, eventBus); - break; - default: - throw new Error(`Unknown execution mode: ${options.mode}`); - } - - // Subscribe to progress updates - eventBus.subscribe('backtest.update', message => { - const { backtestId: _backtestId, progress, ...data } = message.data; - console.log(`Progress: ${progress}%`, data); - }); - - await executionMode.initialize(); - const result = await executionMode.execute(); - await executionMode.cleanup(); - - // Display results - displayResults(result); - - // Save results if output specified - if (options.output) { - await saveResults(result, options.output); - } - - await eventBus.close(); - } catch (error) { - logger.error('Backtest failed', error); - process.exit(1); - } -} - -async function loadConfig(configPath: string): Promise { - try { - if (configPath.endsWith('.json')) { - const file = Bun.file(configPath); - return await file.json(); - } else { - // Assume it's a JavaScript/TypeScript module - return await import(configPath); - } - } catch (error) { - logger.error('Failed to load config', { configPath, error }); - throw new Error(`Failed to load config from ${configPath}: ${(error as Error).message}`); - } -} - -function displayResults(result: any): void { - console.log('\n=== Backtest Results ==='); - console.log(`Strategy: ${result.strategy.name}`); - console.log(`Symbol: ${result.symbol}`); - console.log(`Period: ${result.startDate} to ${result.endDate}`); - console.log(`Mode: ${result.mode}`); - console.log(`Duration: ${result.duration}ms`); - - console.log('\n--- Performance ---'); - console.log(`Total Return: ${(result.performance.totalReturn * 100).toFixed(2)}%`); - console.log(`Sharpe Ratio: ${result.performance.sharpeRatio.toFixed(3)}`); - console.log(`Max Drawdown: ${(result.performance.maxDrawdown * 100).toFixed(2)}%`); - console.log(`Win Rate: ${(result.performance.winRate * 100).toFixed(1)}%`); - console.log(`Profit Factor: ${result.performance.profitFactor.toFixed(2)}`); - - console.log('\n--- Trading Stats ---'); - console.log(`Total Trades: ${result.performance.totalTrades}`); - console.log(`Winning Trades: ${result.performance.winningTrades}`); - console.log(`Losing Trades: ${result.performance.losingTrades}`); - console.log(`Average Trade: ${result.performance.avgTrade.toFixed(2)}`); - console.log(`Average Win: ${result.performance.avgWin.toFixed(2)}`); - console.log(`Average Loss: ${result.performance.avgLoss.toFixed(2)}`); - console.log(`Largest Win: ${result.performance.largestWin.toFixed(2)}`); - console.log(`Largest Loss: ${result.performance.largestLoss.toFixed(2)}`); - - if (result.metadata) { - console.log('\n--- Metadata ---'); - Object.entries(result.metadata).forEach(([key, value]) => { - console.log(`${key}: ${Array.isArray(value) ? value.join(', ') : value}`); - }); - } -} - -async function saveResults(result: any, outputPath: string): Promise { - try { - if (outputPath.endsWith('.json')) { - await Bun.write(outputPath, JSON.stringify(result, null, 2)); - } else if (outputPath.endsWith('.csv')) { - const csv = convertTradesToCSV(result.trades); - await Bun.write(outputPath, csv); - } else { - // Default to JSON - await Bun.write(outputPath + '.json', JSON.stringify(result, null, 2)); - } - - logger.info(`\nResults saved to: ${outputPath}`); - } catch (error) { - logger.error('Failed to save results', { outputPath, error }); - } -} - -function convertTradesToCSV(trades: any[]): string { - if (trades.length === 0) { - return 'No trades executed\n'; - } - - const headers = Object.keys(trades[0]).join(','); - const rows = trades.map(trade => - Object.values(trade) - .map(value => (typeof value === 'string' ? `"${value}"` : value)) - .join(',') - ); - - return [headers, ...rows].join('\n'); -} - -async function listStrategies(): Promise { - console.log('Available strategies:'); - console.log(' sma_crossover - Simple Moving Average Crossover'); - console.log(' ema_crossover - Exponential Moving Average Crossover'); - console.log(' rsi_mean_reversion - RSI Mean Reversion'); - console.log(' macd_trend - MACD Trend Following'); - console.log(' bollinger_bands - Bollinger Bands Strategy'); - // Add more as they're implemented -} - -async function validateStrategy(strategy: string): Promise { - console.log(`Validating strategy: ${strategy}`); - - // TODO: Add strategy validation logic - // This could check if the strategy exists, has valid parameters, etc. - - const validStrategies = [ - 'sma_crossover', - 'ema_crossover', - 'rsi_mean_reversion', - 'macd_trend', - 'bollinger_bands', - ]; - - if (!validStrategies.includes(strategy)) { - console.warn(`Warning: Strategy '${strategy}' is not in the list of known strategies`); - console.log('Use --list-strategies to see available strategies'); - } else { - console.log(`✓ Strategy '${strategy}' is valid`); - } -} - -// CLI Commands -program.name('strategy-cli').description('Stock Trading Bot Strategy CLI').version('1.0.0'); - -program - .command('backtest') - .description('Run a backtest') - .requiredOption('-s, --strategy ', 'Strategy to test') - .requiredOption('--symbol ', 'Symbol to trade') - .requiredOption('--start-date ', 'Start date (YYYY-MM-DD)') - .requiredOption('--end-date ', 'End date (YYYY-MM-DD)') - .option('-m, --mode ', 'Execution mode', 'vectorized') - .option('-c, --initial-capital ', 'Initial capital', '10000') - .option('--config ', 'Configuration file path') - .option('-o, --output ', 'Output file path') - .option('-v, --verbose', 'Verbose output') - .action(async (options: CLIBacktestConfig) => { - await runBacktest(options); - }); - -program.command('list-strategies').description('List available strategies').action(listStrategies); - -program - .command('validate') - .description('Validate a strategy') - .requiredOption('-s, --strategy ', 'Strategy to validate') - .action(async (options: CLIBacktestConfig) => { - await validateStrategy(options.strategy); - }); - -program - .command('compare') - .description('Compare multiple strategies') - .requiredOption('--strategies ', 'Comma-separated list of strategies') - .requiredOption('--symbol ', 'Symbol to trade') - .requiredOption('--start-date ', 'Start date (YYYY-MM-DD)') - .requiredOption('--end-date ', 'End date (YYYY-MM-DD)') - .option('-m, --mode ', 'Execution mode', 'vectorized') - .option('-c, --initial-capital ', 'Initial capital', '10000') - .option('-o, --output ', 'Output directory') - .action(async (options: CLIBacktestConfig) => { - const strategies = options.strategies.split(',').map((s: string) => s.trim()); - console.log(`Comparing strategies: ${strategies.join(', ')}`); - - const _results: any[] = []; - - for (const strategy of strategies) { - console.log(`\nRunning ${strategy}...`); - try { - await runBacktest({ - ...options, - strategy, - output: options.output ? `${options.output}/${strategy}.json` : undefined, - }); - } catch (error) { - console.error(`Failed to run ${strategy}:`, (error as Error).message); - } - } - - console.log('\nComparison completed!'); - }); - -// Parse command line arguments -program.parse(); - -export { listStrategies, runBacktest, validateStrategy }; diff --git a/apps/strategy-service/src/framework/execution-mode.ts b/apps/strategy-service/src/framework/execution-mode.ts deleted file mode 100644 index 290c6bb..0000000 --- a/apps/strategy-service/src/framework/execution-mode.ts +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Execution Mode Framework - * Base classes for different execution modes (live, event-driven, vectorized) - */ -import { getLogger } from '@stock-bot/logger'; - -const _logger = getLogger('execution-mode'); - -export interface Order { - id: string; - symbol: string; - side: 'BUY' | 'SELL'; - quantity: number; - type: 'MARKET' | 'LIMIT'; - price?: number; - timestamp: Date; -} - -export interface OrderResult { - orderId: string; - symbol: string; - executedQuantity: number; - executedPrice: number; - commission: number; - slippage: number; - timestamp: Date; - executionTime: number; -} - -export interface MarketData { - symbol: string; - timestamp: Date; - open: number; - high: number; - low: number; - close: number; - volume: number; -} - -export abstract class ExecutionMode { - protected logger = getLogger(this.constructor.name); - - abstract name: string; - abstract executeOrder(order: Order): Promise; - abstract getCurrentTime(): Date; - abstract getMarketData(symbol: string): Promise; - abstract publishEvent(event: string, data: any): Promise; -} - -export enum BacktestMode { - LIVE = 'live', - EVENT_DRIVEN = 'event-driven', - VECTORIZED = 'vectorized', - HYBRID = 'hybrid', -} - -export class ModeFactory { - static create(mode: BacktestMode, _config?: any): ExecutionMode { - switch (mode) { - case BacktestMode.LIVE: - // TODO: Import and create LiveMode - throw new Error('LiveMode not implemented yet'); - - case BacktestMode.EVENT_DRIVEN: - // TODO: Import and create EventBacktestMode - throw new Error('EventBacktestMode not implemented yet'); - - case BacktestMode.VECTORIZED: - // TODO: Import and create VectorBacktestMode - throw new Error('VectorBacktestMode not implemented yet'); - - case BacktestMode.HYBRID: - // TODO: Import and create HybridBacktestMode - throw new Error('HybridBacktestMode not implemented yet'); - - default: - throw new Error(`Unknown mode: ${mode}`); - } - } -} diff --git a/apps/strategy-service/src/index.ts b/apps/strategy-service/src/index.ts deleted file mode 100644 index ac3f8ae..0000000 --- a/apps/strategy-service/src/index.ts +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Strategy Service - Multi-mode strategy execution and backtesting - */ -import { serve } from '@hono/node-server'; -import { Hono } from 'hono'; -import { loadEnvVariables } from '@stock-bot/config'; -import { getLogger } from '@stock-bot/logger'; - -// Load environment variables -loadEnvVariables(); - -const app = new Hono(); -const logger = getLogger('strategy-service'); -const PORT = parseInt(process.env.STRATEGY_SERVICE_PORT || '3004'); - -// Health check endpoint -app.get('/health', c => { - return c.json({ - service: 'strategy-service', - status: 'healthy', - timestamp: new Date().toISOString(), - }); -}); - -// Strategy execution endpoints -app.post('/api/strategy/run', async c => { - const body = await c.req.json(); - logger.info('Strategy run request', { - strategy: body.strategy, - mode: body.mode, - }); - - // TODO: Implement strategy execution - return c.json({ - message: 'Strategy execution endpoint - not implemented yet', - strategy: body.strategy, - mode: body.mode, - }); -}); - -// Backtesting endpoints -app.post('/api/backtest/event', async c => { - const body = await c.req.json(); - logger.info('Event-driven backtest request', { strategy: body.strategy }); - - // TODO: Implement event-driven backtesting - return c.json({ - message: 'Event-driven backtest endpoint - not implemented yet', - }); -}); - -app.post('/api/backtest/vector', async c => { - const body = await c.req.json(); - logger.info('Vectorized backtest request', { strategy: body.strategy }); - - // TODO: Implement vectorized backtesting - return c.json({ - message: 'Vectorized backtest endpoint - not implemented yet', - }); -}); - -app.post('/api/backtest/hybrid', async c => { - const body = await c.req.json(); - logger.info('Hybrid backtest request', { strategy: body.strategy }); - - // TODO: Implement hybrid backtesting - return c.json({ - message: 'Hybrid backtest endpoint - not implemented yet', - }); -}); - -// Parameter optimization endpoint -app.post('/api/optimize', async c => { - const body = await c.req.json(); - logger.info('Parameter optimization request', { strategy: body.strategy }); - - // TODO: Implement parameter optimization - return c.json({ - message: 'Parameter optimization endpoint - not implemented yet', - }); -}); - -// Start server -serve({ - fetch: app.fetch, - port: PORT, -}); - -logger.info(`Strategy Service started on port ${PORT}`); diff --git a/apps/strategy-service/tsconfig.json b/apps/strategy-service/tsconfig.json deleted file mode 100644 index 6c8062d..0000000 --- a/apps/strategy-service/tsconfig.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "outDir": "./dist", - "rootDir": "./src" - }, - "include": ["src/**/*"], - "exclude": [ - "node_modules", - "dist", - "**/*.test.ts", - "**/*.spec.ts", - "**/test/**", - "**/tests/**", - "**/__tests__/**" - ], - "references": [ - { "path": "../../libs/types" }, - { "path": "../../libs/config" }, - { "path": "../../libs/logger" }, - { "path": "../../libs/utils" }, - { "path": "../../libs/strategy-engine" }, - { "path": "../../libs/event-bus" }, - { "path": "../../libs/shutdown" } - ] -} diff --git a/apps/strategy-service/turbo.json b/apps/strategy-service/turbo.json deleted file mode 100644 index 9157e39..0000000 --- a/apps/strategy-service/turbo.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "extends": ["//"], - "tasks": { - "build": { - "dependsOn": [ - "@stock-bot/types#build", - "@stock-bot/config#build", - "@stock-bot/logger#build", - "@stock-bot/utils#build", - "@stock-bot/strategy-engine#build", - "@stock-bot/event-bus#build", - "@stock-bot/shutdown#build" - ], - "outputs": ["dist/**"], - "inputs": [ - "src/**", - "package.json", - "tsconfig.json", - "!**/*.test.ts", - "!**/*.spec.ts", - "!**/test/**", - "!**/tests/**", - "!**/__tests__/**" - ] - } - } -}