running prettier for cleanup

This commit is contained in:
Boki 2025-06-11 10:13:25 -04:00
parent fe7733aeb5
commit d85cd58acd
151 changed files with 29158 additions and 27966 deletions

View file

@ -1,370 +1,368 @@
import { EventEmitter } from 'eventemitter3';
import { getLogger } from '@stock-bot/logger';
import { EventBus } from '@stock-bot/event-bus';
import { DataFrame } from '@stock-bot/data-frame';
// 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<string, any>;
}
export interface StrategyContext {
symbol: string;
timeframe: string;
data: DataFrame;
indicators: Record<string, any>;
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<string, any>;
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<void>;
abstract onMarketData(context: StrategyContext): Promise<TradingSignal[]>;
abstract onSignal(signal: TradingSignal): Promise<void>;
abstract cleanup(): Promise<void>;
// Optional lifecycle methods
onStart?(): Promise<void>;
onStop?(): Promise<void>;
onError?(error: Error): Promise<void>;
// Control methods
async start(): Promise<void> {
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<void> {
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<void> {
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<string, BaseStrategy> = 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<void> {
// 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<void> {
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<void> {
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<void> {
const strategy = this.strategies.get(strategyId);
if (!strategy) {
throw new Error(`Strategy ${strategyId} not found`);
}
await strategy.start();
}
async stopStrategy(strategyId: string): Promise<void> {
const strategy = this.strategies.get(strategyId);
if (!strategy) {
throw new Error(`Strategy ${strategyId} not found`);
}
await strategy.stop();
}
async startAll(): Promise<void> {
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<void> {
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<void> {
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
data: new DataFrame([data]), // TODO: Use historical data
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<void> {
// Handle order updates - notify relevant strategies
this.logger.debug('Order update received', { data: message.data });
}
private async handlePortfolioUpdate(message: any): Promise<void> {
// 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<void> {
await this.stopAll();
this.strategies.clear();
this.removeAllListeners();
this.logger.info('Strategy engine shutdown');
}
}
import { EventEmitter } from 'eventemitter3';
import { DataFrame } from '@stock-bot/data-frame';
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<string, any>;
}
export interface StrategyContext {
symbol: string;
timeframe: string;
data: DataFrame;
indicators: Record<string, any>;
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<string, any>;
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<void>;
abstract onMarketData(context: StrategyContext): Promise<TradingSignal[]>;
abstract onSignal(signal: TradingSignal): Promise<void>;
abstract cleanup(): Promise<void>;
// Optional lifecycle methods
onStart?(): Promise<void>;
onStop?(): Promise<void>;
onError?(error: Error): Promise<void>;
// Control methods
async start(): Promise<void> {
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<void> {
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<void> {
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<string, BaseStrategy> = 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<void> {
// 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<void> {
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<void> {
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<void> {
const strategy = this.strategies.get(strategyId);
if (!strategy) {
throw new Error(`Strategy ${strategyId} not found`);
}
await strategy.start();
}
async stopStrategy(strategyId: string): Promise<void> {
const strategy = this.strategies.get(strategyId);
if (!strategy) {
throw new Error(`Strategy ${strategyId} not found`);
}
await strategy.stop();
}
async startAll(): Promise<void> {
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<void> {
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<void> {
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
data: new DataFrame([data]), // TODO: Use historical data
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<void> {
// Handle order updates - notify relevant strategies
this.logger.debug('Order update received', { data: message.data });
}
private async handlePortfolioUpdate(message: any): Promise<void> {
// 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<void> {
await this.stopAll();
this.strategies.clear();
this.removeAllListeners();
this.logger.info('Strategy engine shutdown');
}
}