diff --git a/libs/queue/src/batch-processor.ts b/libs/queue/src/batch-processor.ts index 689e0d6..93e55da 100644 --- a/libs/queue/src/batch-processor.ts +++ b/libs/queue/src/batch-processor.ts @@ -14,7 +14,7 @@ export async function processItems( options: ProcessOptions ): Promise { const queueManager = QueueManager.getInstance(); - const queue = queueManager.getQueue(queueName); + queueManager.getQueue(queueName); const startTime = Date.now(); if (items.length === 0) { @@ -61,7 +61,7 @@ async function processDirect( options: ProcessOptions ): Promise> { const queueManager = QueueManager.getInstance(); - const queue = queueManager.getQueue(queueName); + queueManager.getQueue(queueName); const totalDelayMs = options.totalDelayHours * 60 * 60 * 1000; // Convert hours to milliseconds const delayPerItem = totalDelayMs / items.length; @@ -106,7 +106,7 @@ async function processBatched( options: ProcessOptions ): Promise> { const queueManager = QueueManager.getInstance(); - const queue = queueManager.getQueue(queueName); + queueManager.getQueue(queueName); const batchSize = options.batchSize || 100; const batches = createBatches(items, batchSize); const totalDelayMs = options.totalDelayHours * 60 * 60 * 1000; // Convert hours to milliseconds @@ -166,7 +166,7 @@ export async function processBatchJob( queueName: string ): Promise { const queueManager = QueueManager.getInstance(); - const queue = queueManager.getQueue(queueName); + queueManager.getQueue(queueName); const { payloadKey, batchIndex, totalBatches, itemCount } = jobData; logger.debug('Processing batch job', { @@ -274,7 +274,7 @@ async function loadPayload( delayPerItem: number; priority?: number; retries: number; - provider: string; + handler: string; operation: string; }; } | null; diff --git a/libs/queue/src/dlq-handler.ts b/libs/queue/src/dlq-handler.ts index 27dba28..9b643af 100644 --- a/libs/queue/src/dlq-handler.ts +++ b/libs/queue/src/dlq-handler.ts @@ -1,6 +1,6 @@ import { Queue, type Job } from 'bullmq'; import { getLogger } from '@stock-bot/logger'; -import type { JobData, DLQConfig, RedisConfig } from './types'; +import type { DLQConfig, RedisConfig } from './types'; import { getRedisConnection } from './utils'; const logger = getLogger('dlq-handler'); @@ -12,7 +12,7 @@ export class DeadLetterQueueHandler { constructor( private mainQueue: Queue, - private connection: RedisConfig, + connection: RedisConfig, config: DLQConfig = {} ) { this.config = { diff --git a/libs/queue/src/queue-manager.ts b/libs/queue/src/queue-manager.ts index 4fc5529..e806945 100644 --- a/libs/queue/src/queue-manager.ts +++ b/libs/queue/src/queue-manager.ts @@ -7,8 +7,7 @@ import type { QueueOptions, GlobalStats, QueueStats, - RateLimitRule, - RedisConfig + RateLimitRule } from './types'; import { getRedisConnection } from './utils'; @@ -127,7 +126,7 @@ export class QueueManager { const queueConfig: QueueWorkerConfig = { workers: mergedOptions.workers, concurrency: mergedOptions.concurrency, - startWorker: mergedOptions.workers && mergedOptions.workers > 0, + startWorker: !!mergedOptions.workers && mergedOptions.workers > 0, }; const queue = new Queue( @@ -387,7 +386,7 @@ export class QueueManager { try { await queue.close(); } catch (error) { - logger.warn('Error closing queue', { error: error.message }); + logger.warn('Error closing queue', { error: (error as Error).message }); } }); @@ -396,16 +395,10 @@ export class QueueManager { // Close all caches const cacheShutdownPromises = Array.from(this.caches.values()).map(async (cache) => { try { - // Try different disconnect methods as different cache providers may use different names - if (typeof cache.disconnect === 'function') { - await cache.disconnect(); - } else if (typeof cache.close === 'function') { - await cache.close(); - } else if (typeof cache.quit === 'function') { - await cache.quit(); - } + // Clear cache before shutdown + await cache.clear(); } catch (error) { - logger.warn('Error closing cache', { error: error.message }); + logger.warn('Error clearing cache', { error: (error as Error).message }); } }); @@ -417,7 +410,7 @@ export class QueueManager { logger.info('QueueManager shutdown complete'); } catch (error) { - logger.error('Error during shutdown', { error: error.message }); + logger.error('Error during shutdown', { error: (error as Error).message }); throw error; } finally { // Reset shutdown state diff --git a/libs/queue/src/queue-metrics.ts b/libs/queue/src/queue-metrics.ts index 8bbe142..6442206 100644 --- a/libs/queue/src/queue-metrics.ts +++ b/libs/queue/src/queue-metrics.ts @@ -2,7 +2,7 @@ import { Queue, QueueEvents } from 'bullmq'; import { getLogger } from '@stock-bot/logger'; import type { Job } from 'bullmq'; -const logger = getLogger('queue-metrics'); +// const logger = getLogger('queue-metrics'); export interface QueueMetrics { // Job counts @@ -55,20 +55,20 @@ export class QueueMetricsCollector { * Setup event listeners for metrics collection */ private setupEventListeners(): void { - this.queueEvents.on('completed', ({ jobId, returnvalue, prev }) => { + this.queueEvents.on('completed', () => { // Record completion this.completedTimestamps.push(Date.now()); this.cleanupOldTimestamps(); }); - this.queueEvents.on('failed', ({ jobId, failedReason, prev }) => { + this.queueEvents.on('failed', () => { // Record failure this.failedTimestamps.push(Date.now()); this.cleanupOldTimestamps(); }); // Track processing times - this.queueEvents.on('active', async ({ jobId, prev }) => { + this.queueEvents.on('active', async ({ jobId }) => { const job = await this.getJob(jobId); if (job) { (job as any)._startTime = Date.now(); @@ -177,11 +177,11 @@ export class QueueMetricsCollector { const sum = sorted.reduce((acc, val) => acc + val, 0); return { - avg: Math.round(sum / sorted.length), - min: sorted[0], - max: sorted[sorted.length - 1], - p95: sorted[Math.floor(sorted.length * 0.95)], - p99: sorted[Math.floor(sorted.length * 0.99)], + avg: sorted.length > 0 ? Math.round(sum / sorted.length) : 0, + min: sorted[0] || 0, + max: sorted[sorted.length - 1] || 0, + p95: sorted[Math.floor(sorted.length * 0.95)] || 0, + p99: sorted[Math.floor(sorted.length * 0.99)] || 0, }; } diff --git a/libs/queue/src/queue.ts b/libs/queue/src/queue.ts index 22602c0..0caccba 100644 --- a/libs/queue/src/queue.ts +++ b/libs/queue/src/queue.ts @@ -222,7 +222,6 @@ export class Queue { concurrency, maxStalledCount: 3, stalledInterval: 30000, - maxStalledTime: 60000, } ); diff --git a/libs/queue/src/rate-limiter.ts b/libs/queue/src/rate-limiter.ts index 1ef7042..f8cf62a 100644 --- a/libs/queue/src/rate-limiter.ts +++ b/libs/queue/src/rate-limiter.ts @@ -24,7 +24,7 @@ export class QueueRateLimiter { const key = this.getRuleKey(rule.level, rule.queueName, rule.handler, rule.operation); const limiter = new RateLimiterRedis({ storeClient: this.redisClient, - keyPrefix: rule.config.keyPrefix || `rl:${key}`, + keyPrefix: `rl:${key}`, points: rule.config.points, duration: rule.config.duration, blockDuration: rule.config.blockDuration || 0, @@ -224,7 +224,7 @@ export class QueueRateLimiter { queueName, handler, operation, - appliedRule, + appliedRule: applicableRule, limit, }; } catch (error) { @@ -233,7 +233,7 @@ export class QueueRateLimiter { queueName, handler, operation, - appliedRule, + appliedRule: applicableRule, }; } } diff --git a/libs/queue/src/types.ts b/libs/queue/src/types.ts index 2706581..f727431 100644 --- a/libs/queue/src/types.ts +++ b/libs/queue/src/types.ts @@ -154,3 +154,19 @@ export interface DLQConfig { alertThreshold?: number; cleanupAge?: number; } + +export interface DLQJobInfo { + id: string; + name: string; + failedReason: string; + attemptsMade: number; + timestamp: number; + data: any; +} + +export interface ScheduleConfig { + pattern: string; + jobName: string; + data?: any; + options?: JobOptions; +} diff --git a/libs/queue/src/utils.ts b/libs/queue/src/utils.ts index df851bb..d32739b 100644 --- a/libs/queue/src/utils.ts +++ b/libs/queue/src/utils.ts @@ -4,7 +4,7 @@ import type { RedisConfig } from './types'; * Get Redis connection configuration with retry settings */ export function getRedisConnection(config: RedisConfig) { - const isTest = process.env.NODE_ENV === 'test' || process.env.BUNIT === '1'; + const isTest = process.env.NODE_ENV === 'test' || process.env['BUNIT'] === '1'; return { host: config.host, diff --git a/libs/strategy-engine/package.json b/libs/strategy-engine/package.json deleted file mode 100644 index 6a694d0..0000000 --- a/libs/strategy-engine/package.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "@stock-bot/strategy-engine", - "version": "1.0.0", - "description": "Strategy execution engine with multi-mode support", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "type": "module", - "scripts": { - "build": "tsc", - "test": "bun test", - "clean": "rimraf dist" - }, - "dependencies": { - "@stock-bot/event-bus": "*", - "@stock-bot/logger": "*", - "@stock-bot/utils": "*", - "commander": "^14.0.0", - "eventemitter3": "^5.0.1" - }, - "devDependencies": { - "@types/node": "^20.11.0", - "typescript": "^5.3.0", - "bun-types": "^1.2.15" - }, - "exports": { - ".": { - "import": "./dist/index.js", - "require": "./dist/index.js", - "types": "./dist/index.d.ts" - } - }, - "files": [ - "dist", - "README.md" - ] -} diff --git a/libs/strategy-engine/src/index.ts b/libs/strategy-engine/src/index.ts deleted file mode 100644 index 3ec358b..0000000 --- a/libs/strategy-engine/src/index.ts +++ /dev/null @@ -1,365 +0,0 @@ -import { EventEmitter } from 'eventemitter3'; -import { EventBus } from '@stock-bot/event-bus'; -import { getLogger } from '@stock-bot/logger'; - -// Core types -export interface MarketData { - symbol: string; - timestamp: number; - open: number; - high: number; - low: number; - close: number; - volume: number; - [key: string]: any; -} - -export interface TradingSignal { - type: 'BUY' | 'SELL' | 'HOLD'; - symbol: string; - timestamp: number; - price: number; - quantity: number; - confidence: number; - reason: string; - metadata?: Record; -} - -export interface StrategyContext { - symbol: string; - timeframe: string; - indicators: Record; - position?: Position; - portfolio: PortfolioSummary; - timestamp: number; -} - -export interface Position { - symbol: string; - quantity: number; - averagePrice: number; - currentPrice: number; - unrealizedPnL: number; - side: 'LONG' | 'SHORT'; -} - -export interface PortfolioSummary { - totalValue: number; - cash: number; - positions: Position[]; - totalPnL: number; - dayPnL: number; -} - -export interface StrategyConfig { - id: string; - name: string; - description?: string; - symbols: string[]; - timeframes: string[]; - parameters: Record; - riskLimits: RiskLimits; - enabled: boolean; -} - -export interface RiskLimits { - maxPositionSize: number; - maxDailyLoss: number; - maxDrawdown: number; - stopLoss?: number; - takeProfit?: number; -} - -// Abstract base strategy class -export abstract class BaseStrategy extends EventEmitter { - protected logger; - protected eventBus: EventBus; - protected config: StrategyConfig; - protected isActive: boolean = false; - - constructor(config: StrategyConfig, eventBus: EventBus) { - super(); - this.config = config; - this.eventBus = eventBus; - this.logger = getLogger(`strategy:${config.id}`); - } - - // Abstract methods that must be implemented by concrete strategies - abstract initialize(): Promise; - abstract onMarketData(context: StrategyContext): Promise; - abstract onSignal(signal: TradingSignal): Promise; - abstract cleanup(): Promise; - - // Optional lifecycle methods - onStart?(): Promise; - onStop?(): Promise; - onError?(error: Error): Promise; - - // Control methods - async start(): Promise { - if (this.isActive) { - this.logger.warn('Strategy already active'); - return; - } - - try { - await this.initialize(); - - if (this.onStart) { - await this.onStart(); - } - - this.isActive = true; - this.logger.info('Strategy started', { strategyId: this.config.id }); - this.emit('started'); - } catch (error) { - this.logger.error('Failed to start strategy', { error, strategyId: this.config.id }); - throw error; - } - } - - async stop(): Promise { - if (!this.isActive) { - this.logger.warn('Strategy not active'); - return; - } - - try { - if (this.onStop) { - await this.onStop(); - } - - await this.cleanup(); - this.isActive = false; - this.logger.info('Strategy stopped', { strategyId: this.config.id }); - this.emit('stopped'); - } catch (error) { - this.logger.error('Failed to stop strategy', { error, strategyId: this.config.id }); - throw error; - } - } - - // Utility methods - protected async emitSignal(signal: TradingSignal): Promise { - await this.eventBus.publish(this.config.id, signal); - this.emit('signal', signal); - this.logger.info('Signal generated', { - signal: signal.type, - symbol: signal.symbol, - confidence: signal.confidence, - }); - } - - protected checkRiskLimits(signal: TradingSignal, context: StrategyContext): boolean { - const limits = this.config.riskLimits; - - // Check position size limit - if (signal.quantity > limits.maxPositionSize) { - this.logger.warn('Signal exceeds max position size', { - requested: signal.quantity, - limit: limits.maxPositionSize, - }); - return false; - } - - // Check daily loss limit - if (context.portfolio.dayPnL <= -limits.maxDailyLoss) { - this.logger.warn('Daily loss limit reached', { - dayPnL: context.portfolio.dayPnL, - limit: -limits.maxDailyLoss, - }); - return false; - } - - return true; - } - - // Getters - get id(): string { - return this.config.id; - } - - get name(): string { - return this.config.name; - } - - get active(): boolean { - return this.isActive; - } - - get configuration(): StrategyConfig { - return { ...this.config }; - } -} - -// Strategy execution engine -export class StrategyEngine extends EventEmitter { - private strategies: Map = new Map(); - private logger; - private eventBus: EventBus; - private isRunning: boolean = false; - - constructor(eventBus: EventBus) { - super(); - this.eventBus = eventBus; - this.logger = getLogger('strategy-engine'); - } - - async initialize(): Promise { - // Subscribe to market data events - await this.eventBus.subscribe('market.data', this.handleMarketData.bind(this)); - await this.eventBus.subscribe('order.update', this.handleOrderUpdate.bind(this)); - await this.eventBus.subscribe('portfolio.update', this.handlePortfolioUpdate.bind(this)); - - this.logger.info('Strategy engine initialized'); - } - - async registerStrategy(strategy: BaseStrategy): Promise { - if (this.strategies.has(strategy.id)) { - throw new Error(`Strategy ${strategy.id} already registered`); - } - - this.strategies.set(strategy.id, strategy); - - // Forward strategy events - strategy.on('signal', signal => this.emit('signal', signal)); - strategy.on('error', error => this.emit('error', error)); - - this.logger.info('Strategy registered', { strategyId: strategy.id }); - } - - async unregisterStrategy(strategyId: string): Promise { - const strategy = this.strategies.get(strategyId); - if (!strategy) { - throw new Error(`Strategy ${strategyId} not found`); - } - - if (strategy.active) { - await strategy.stop(); - } - - strategy.removeAllListeners(); - this.strategies.delete(strategyId); - - this.logger.info('Strategy unregistered', { strategyId }); - } - - async startStrategy(strategyId: string): Promise { - const strategy = this.strategies.get(strategyId); - if (!strategy) { - throw new Error(`Strategy ${strategyId} not found`); - } - - await strategy.start(); - } - - async stopStrategy(strategyId: string): Promise { - const strategy = this.strategies.get(strategyId); - if (!strategy) { - throw new Error(`Strategy ${strategyId} not found`); - } - - await strategy.stop(); - } - - async startAll(): Promise { - if (this.isRunning) { - this.logger.warn('Engine already running'); - return; - } - - const startPromises = Array.from(this.strategies.values()) - .filter(strategy => strategy.configuration.enabled) - .map(strategy => strategy.start()); - - await Promise.all(startPromises); - this.isRunning = true; - this.logger.info('All strategies started'); - this.emit('started'); - } - - async stopAll(): Promise { - if (!this.isRunning) { - this.logger.warn('Engine not running'); - return; - } - - const stopPromises = Array.from(this.strategies.values()) - .filter(strategy => strategy.active) - .map(strategy => strategy.stop()); - - await Promise.all(stopPromises); - this.isRunning = false; - this.logger.info('All strategies stopped'); - this.emit('stopped'); - } - - private async handleMarketData(message: any): Promise { - const { symbol, ...data } = message.data; - - // Find strategies that trade this symbol - const relevantStrategies = Array.from(this.strategies.values()).filter( - strategy => strategy.active && strategy.configuration.symbols.includes(symbol) - ); - - for (const strategy of relevantStrategies) { - try { - // Create context for this strategy - const context: StrategyContext = { - symbol, - timeframe: '1m', // TODO: Get from strategy config - indicators: {}, - portfolio: { - totalValue: 100000, // TODO: Get real portfolio data - cash: 50000, - positions: [], - totalPnL: 0, - dayPnL: 0, - }, - timestamp: data.timestamp, - }; - - const signals = await strategy.onMarketData(context); - - for (const signal of signals) { - await strategy.onSignal(signal); - } - } catch (error) { - this.logger.error('Error processing market data for strategy', { - error, - strategyId: strategy.id, - symbol, - }); - } - } - } - - private async handleOrderUpdate(message: any): Promise { - // Handle order updates - notify relevant strategies - this.logger.debug('Order update received', { data: message.data }); - } - - private async handlePortfolioUpdate(message: any): Promise { - // Handle portfolio updates - notify relevant strategies - this.logger.debug('Portfolio update received', { data: message.data }); - } - - getStrategy(strategyId: string): BaseStrategy | undefined { - return this.strategies.get(strategyId); - } - - getStrategies(): BaseStrategy[] { - return Array.from(this.strategies.values()); - } - - getActiveStrategies(): BaseStrategy[] { - return this.getStrategies().filter(strategy => strategy.active); - } - - async shutdown(): Promise { - await this.stopAll(); - this.strategies.clear(); - this.removeAllListeners(); - this.logger.info('Strategy engine shutdown'); - } -} diff --git a/libs/strategy-engine/tsconfig.json b/libs/strategy-engine/tsconfig.json deleted file mode 100644 index 09b47c0..0000000 --- a/libs/strategy-engine/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../../tsconfig.lib.json", - "compilerOptions": { - "outDir": "./dist", - "rootDir": "./src" - }, - "include": ["src/**/*"], - "references": [{ "path": "../event-bus" }, { "path": "../logger" }, { "path": "../utils" }] -} diff --git a/libs/vector-engine/package.json b/libs/vector-engine/package.json deleted file mode 100644 index 40ebee7..0000000 --- a/libs/vector-engine/package.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "name": "@stock-bot/vector-engine", - "version": "1.0.0", - "description": "Vectorized computation engine for high-performance backtesting", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "type": "module", - "scripts": { - "build": "tsc", - "test": "bun test", - "clean": "rimraf dist" - }, - "dependencies": { - "@stock-bot/logger": "*", - "@stock-bot/utils": "*" - }, - "devDependencies": { - "@types/node": "^20.11.0", - "typescript": "^5.3.0", - "bun-types": "^1.2.15" - }, - "exports": { - ".": { - "import": "./dist/index.js", - "require": "./dist/index.js", - "types": "./dist/index.d.ts" - } - }, - "files": [ - "dist", - "README.md" - ] -} diff --git a/libs/vector-engine/src/index.ts b/libs/vector-engine/src/index.ts deleted file mode 100644 index 6b4955d..0000000 --- a/libs/vector-engine/src/index.ts +++ /dev/null @@ -1,397 +0,0 @@ -// import { DataFrame } from '@stock-bot/data-frame'; -// import { getLogger } from '@stock-bot/logger'; -// import { atr, bollingerBands, ema, macd, rsi, sma } from '@stock-bot/utils'; - -// // Vector operations interface -// export interface VectorOperation { -// name: string; -// inputs: string[]; -// output: string; -// operation: (inputs: number[][]) => number[]; -// } - -// // Vectorized strategy context -// export interface VectorizedContext { -// data: DataFrame; -// lookback: number; -// indicators: Record; -// signals: Record; -// } - -// // Performance metrics for vectorized backtesting -// export interface VectorizedMetrics { -// totalReturns: number; -// sharpeRatio: number; -// maxDrawdown: number; -// winRate: number; -// profitFactor: number; -// totalTrades: number; -// avgTrade: number; -// returns: number[]; -// drawdown: number[]; -// equity: number[]; -// } - -// // Vectorized backtest result -// export interface VectorizedBacktestResult { -// metrics: VectorizedMetrics; -// trades: VectorizedTrade[]; -// equity: number[]; -// timestamps: number[]; -// signals: Record; -// } - -// export interface VectorizedTrade { -// entryIndex: number; -// exitIndex: number; -// entryPrice: number; -// exitPrice: number; -// quantity: number; -// side: 'LONG' | 'SHORT'; -// pnl: number; -// return: number; -// duration: number; -// } - -// // Vectorized strategy engine -// export class VectorEngine { -// private logger = getLogger('vector-engine'); -// private operations: Map = new Map(); - -// constructor() { -// this.registerDefaultOperations(); -// } - -// private registerDefaultOperations(): void { -// // Register common mathematical operations -// this.registerOperation({ -// name: 'add', -// inputs: ['a', 'b'], -// output: 'result', -// operation: ([a, b]) => a.map((val, i) => val + b[i]), -// }); - -// this.registerOperation({ -// name: 'subtract', -// inputs: ['a', 'b'], -// output: 'result', -// operation: ([a, b]) => a.map((val, i) => val - b[i]), -// }); - -// this.registerOperation({ -// name: 'multiply', -// inputs: ['a', 'b'], -// output: 'result', -// operation: ([a, b]) => a.map((val, i) => val * b[i]), -// }); - -// this.registerOperation({ -// name: 'divide', -// inputs: ['a', 'b'], -// output: 'result', -// operation: ([a, b]) => a.map((val, i) => (b[i] !== 0 ? val / b[i] : NaN)), -// }); - -// // Register comparison operations -// this.registerOperation({ -// name: 'greater_than', -// inputs: ['a', 'b'], -// output: 'result', -// operation: ([a, b]) => a.map((val, i) => (val > b[i] ? 1 : 0)), -// }); - -// this.registerOperation({ -// name: 'less_than', -// inputs: ['a', 'b'], -// output: 'result', -// operation: ([a, b]) => a.map((val, i) => (val < b[i] ? 1 : 0)), -// }); - -// this.registerOperation({ -// name: 'crossover', -// inputs: ['a', 'b'], -// output: 'result', -// operation: ([a, b]) => { -// const result = new Array(a.length).fill(0); -// for (let i = 1; i < a.length; i++) { -// if (a[i] > b[i] && a[i - 1] <= b[i - 1]) { -// result[i] = 1; -// } -// } -// return result; -// }, -// }); - -// this.registerOperation({ -// name: 'crossunder', -// inputs: ['a', 'b'], -// output: 'result', -// operation: ([a, b]) => { -// const result = new Array(a.length).fill(0); -// for (let i = 1; i < a.length; i++) { -// if (a[i] < b[i] && a[i - 1] >= b[i - 1]) { -// result[i] = 1; -// } -// } -// return result; -// }, -// }); -// } - -// registerOperation(operation: VectorOperation): void { -// this.operations.set(operation.name, operation); -// this.logger.debug(`Registered operation: ${operation.name}`); -// } - -// // Execute vectorized strategy -// async executeVectorizedStrategy( -// data: DataFrame, -// strategyCode: string -// ): Promise { -// try { -// const context = this.prepareContext(data); -// const signals = this.executeStrategy(context, strategyCode); -// const trades = this.generateTrades(data, signals); -// const metrics = this.calculateMetrics(data, trades); - -// return { -// metrics, -// trades, -// equity: metrics.equity, -// timestamps: data.getColumn('timestamp'), -// signals, -// }; -// } catch (error) { -// this.logger.error('Vectorized strategy execution failed', error); -// throw error; -// } -// } - -// private prepareContext(data: DataFrame): VectorizedContext { -// const close = data.getColumn('close'); -// const high = data.getColumn('high'); -// const low = data.getColumn('low'); -// const volume = data.getColumn('volume'); - -// // Calculate common indicators -// const indicators: Record = { -// sma_20: sma(close, 20), -// sma_50: sma(close, 50), -// ema_12: ema(close, 12), -// ema_26: ema(close, 26), -// rsi: rsi(close), -// }; - -// const m = macd(close); -// indicators.macd = m.macd; -// indicators.macd_signal = m.signal; -// indicators.macd_histogram = m.histogram; - -// const bb = bollingerBands(close); -// indicators.bb_upper = bb.upper; -// indicators.bb_middle = bb.middle; -// indicators.bb_lower = bb.lower; - -// return { -// data, -// lookback: 100, -// indicators, -// signals: {}, -// }; -// } - -// private executeStrategy( -// context: VectorizedContext, -// strategyCode: string -// ): Record { -// // This is a simplified strategy execution -// // In production, you'd want a more sophisticated strategy compiler/interpreter -// const signals: Record = { -// buy: new Array(context.data.length).fill(0), -// sell: new Array(context.data.length).fill(0), -// }; - -// // Example: Simple moving average crossover strategy -// if (strategyCode.includes('sma_crossover')) { -// const sma20 = context.indicators.sma_20; -// const sma50 = context.indicators.sma_50; - -// for (let i = 1; i < sma20.length; i++) { -// // Buy signal: SMA20 crosses above SMA50 -// if (!isNaN(sma20[i]) && !isNaN(sma50[i]) && !isNaN(sma20[i - 1]) && !isNaN(sma50[i - 1])) { -// if (sma20[i] > sma50[i] && sma20[i - 1] <= sma50[i - 1]) { -// signals.buy[i] = 1; -// } -// // Sell signal: SMA20 crosses below SMA50 -// else if (sma20[i] < sma50[i] && sma20[i - 1] >= sma50[i - 1]) { -// signals.sell[i] = 1; -// } -// } -// } -// } - -// return signals; -// } - -// private generateTrades(data: DataFrame, signals: Record): VectorizedTrade[] { -// const trades: VectorizedTrade[] = []; -// const close = data.getColumn('close'); -// const timestamps = data.getColumn('timestamp'); - -// let position: { index: number; price: number; side: 'LONG' | 'SHORT' } | null = null; - -// for (let i = 0; i < close.length; i++) { -// if (signals.buy[i] === 1 && !position) { -// // Open long position -// position = { -// index: i, -// price: close[i], -// side: 'LONG', -// }; -// } else if (signals.sell[i] === 1) { -// if (position && position.side === 'LONG') { -// // Close long position -// const trade: VectorizedTrade = { -// entryIndex: position.index, -// exitIndex: i, -// entryPrice: position.price, -// exitPrice: close[i], -// quantity: 1, // Simplified: always trade 1 unit -// side: 'LONG', -// pnl: close[i] - position.price, -// return: (close[i] - position.price) / position.price, -// duration: timestamps[i] - timestamps[position.index], -// }; -// trades.push(trade); -// position = null; -// } else if (!position) { -// // Open short position -// position = { -// index: i, -// price: close[i], -// side: 'SHORT', -// }; -// } -// } else if (signals.buy[i] === 1 && position && position.side === 'SHORT') { -// // Close short position -// const trade: VectorizedTrade = { -// entryIndex: position.index, -// exitIndex: i, -// entryPrice: position.price, -// exitPrice: close[i], -// quantity: 1, -// side: 'SHORT', -// pnl: position.price - close[i], -// return: (position.price - close[i]) / position.price, -// duration: timestamps[i] - timestamps[position.index], -// }; -// trades.push(trade); -// position = null; -// } -// } - -// return trades; -// } - -// private calculateMetrics(data: DataFrame, trades: VectorizedTrade[]): VectorizedMetrics { -// if (trades.length === 0) { -// return { -// totalReturns: 0, -// sharpeRatio: 0, -// maxDrawdown: 0, -// winRate: 0, -// profitFactor: 0, -// totalTrades: 0, -// avgTrade: 0, -// returns: [], -// drawdown: [], -// equity: [], -// }; -// } - -// const returns = trades.map(t => t.return); -// const pnls = trades.map(t => t.pnl); - -// // Calculate equity curve -// const equity: number[] = [10000]; // Starting capital -// let currentEquity = 10000; - -// for (const trade of trades) { -// currentEquity += trade.pnl; -// equity.push(currentEquity); -// } - -// // Calculate drawdown -// const drawdown: number[] = []; -// let peak = equity[0]; - -// for (const eq of equity) { -// if (eq > peak) { -// peak = eq; -// } -// drawdown.push((peak - eq) / peak); -// } - -// const totalReturns = (equity[equity.length - 1] - equity[0]) / equity[0]; -// const avgReturn = returns.reduce((sum, r) => sum + r, 0) / returns.length; -// const returnStd = Math.sqrt( -// returns.reduce((sum, r) => sum + Math.pow(r - avgReturn, 2), 0) / returns.length -// ); - -// const winningTrades = trades.filter(t => t.pnl > 0); -// const losingTrades = trades.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 { -// totalReturns, -// sharpeRatio: returnStd !== 0 ? (avgReturn / returnStd) * Math.sqrt(252) : 0, -// maxDrawdown: Math.max(...drawdown), -// winRate: winningTrades.length / trades.length, -// profitFactor: grossLoss !== 0 ? grossProfit / grossLoss : Infinity, -// totalTrades: trades.length, -// avgTrade: pnls.reduce((sum, pnl) => sum + pnl, 0) / trades.length, -// returns, -// drawdown, -// equity, -// }; -// } - -// // Utility methods for vectorized operations -// applyOperation(operationName: string, inputs: Record): number[] { -// const operation = this.operations.get(operationName); -// if (!operation) { -// throw new Error(`Operation '${operationName}' not found`); -// } - -// const inputArrays = operation.inputs.map(inputName => { -// if (!inputs[inputName]) { -// throw new Error(`Input '${inputName}' not provided for operation '${operationName}'`); -// } -// return inputs[inputName]; -// }); - -// return operation.operation(inputArrays); -// } - -// // Batch processing for multiple strategies -// async batchBacktest( -// data: DataFrame, -// strategies: Array<{ id: string; code: string }> -// ): Promise> { -// const results: Record = {}; - -// for (const strategy of strategies) { -// try { -// this.logger.info(`Running vectorized backtest for strategy: ${strategy.id}`); -// results[strategy.id] = await this.executeVectorizedStrategy(data, strategy.code); -// } catch (error) { -// this.logger.error(`Backtest failed for strategy: ${strategy.id}`, error); -// // Continue with other strategies -// } -// } - -// return results; -// } -// } diff --git a/libs/vector-engine/tsconfig.json b/libs/vector-engine/tsconfig.json deleted file mode 100644 index adf6deb..0000000 --- a/libs/vector-engine/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "../../tsconfig.lib.json", - "compilerOptions": { - "outDir": "./dist", - "rootDir": "./src" - }, - "include": ["src/**/*"], - "references": [{ "path": "../logger" }, { "path": "../utils" }] -}