UNPKG

skayn-trading-sdk

Version:

Professional Bitcoin trading strategy backtesting framework with institutional-grade architecture

393 lines (327 loc) • 11.6 kB
/** * Trading Framework Core * The main framework that orchestrates all plugins and provides the unified API */ const PluginRegistry = require('./plugin-registry'); const logger = require('../utils/logger'); class TradingFramework { constructor(config = {}) { this.config = config; this.registry = new PluginRegistry(); this.isInitialized = false; this.activeComponents = new Map(); // Initialize core plugin types this.initializeCorePluginTypes(); logger.info('šŸš€ Trading Framework initialized'); } /** * Initialize core plugin types with their interfaces */ initializeCorePluginTypes() { // Register all core plugin types this.registry.registerPluginType('dataProvider', require('../interfaces/IDataProvider'), { singleton: false, required: true, dependencies: [] }); this.registry.registerPluginType('executionEngine', require('../interfaces/IExecutionEngine'), { singleton: true, required: true, dependencies: ['dataProvider'] }); this.registry.registerPluginType('strategy', require('../interfaces/IStrategy'), { singleton: false, required: true, dependencies: [] }); this.registry.registerPluginType('riskManager', require('../interfaces/IRiskManager'), { singleton: true, required: false, dependencies: [] }); this.registry.registerPluginType('positionManager', require('../interfaces/IPositionManager'), { singleton: true, required: true, dependencies: ['riskManager'] }); this.registry.registerPluginType('backtestEngine', require('../interfaces/IBacktestEngine'), { singleton: false, required: false, dependencies: ['dataProvider', 'positionManager'] }); this.registry.registerPluginType('notificationProvider', require('../interfaces/INotificationProvider'), { singleton: false, required: false, dependencies: [] }); this.registry.registerPluginType('storageProvider', require('../interfaces/IStorageProvider'), { singleton: true, required: false, dependencies: [] }); logger.info('šŸ”Œ Core plugin types registered'); } /** * Register a plugin */ registerPlugin(type, name, pluginClass, metadata = {}) { return this.registry.registerPlugin(type, name, pluginClass, metadata); } /** * Use a specific plugin configuration */ use(type, nameOrInstance, config = {}) { if (typeof nameOrInstance === 'string') { // Get instance by name const instance = this.registry.getInstance(type, nameOrInstance, config); this.activeComponents.set(type, instance); logger.info(`āœ… Using ${type}: ${nameOrInstance}`); return this; } else { // Direct instance provided this.activeComponents.set(type, nameOrInstance); logger.info(`āœ… Using ${type}: direct instance`); return this; } } /** * Get active component instance */ get(type) { if (!this.activeComponents.has(type)) { const suggestions = this.getComponentSuggestions(type); throw new Error(`āŒ Component '${type}' not configured.\n\n${suggestions}\n\nExample: framework.use('${type}', ${this.getExampleUsage(type)})`); } return this.activeComponents.get(type); } /** * Get helpful suggestions for missing components */ getComponentSuggestions(type) { const suggestions = { dataProvider: `šŸ“Š Data Provider Missing • For live data: Use 'binance' or provide your own IDataProvider • For backtesting: Use 'sample' or 'binance' • Custom: Implement IDataProvider interface`, strategy: `🧠 Strategy Missing • Use SDK.strategy() helper function • Implement IStrategy interface • Required methods: analyze(), getRequiredWarmupPeriods(), getStrategyMetadata()`, executionEngine: `⚔ Execution Engine Missing • For live trading: Use exchange-specific engine • For backtesting: Mock engine is auto-configured • Custom: Implement IExecutionEngine interface`, positionManager: `šŸ“ˆ Position Manager Missing • For backtesting: SimplePositionManager is auto-configured • For live trading: Use exchange-specific manager • Custom: Implement IPositionManager interface`, backtestEngine: `šŸ”¬ Backtest Engine Missing • Use 'simple' for basic backtesting • Custom: Implement IBacktestEngine interface • Auto-configured when calling .backtest()`, riskManager: `āš ļø Risk Manager (Optional) • Implement IRiskManager interface • Provides position sizing and risk controls`, notificationProvider: `šŸ“¢ Notification Provider (Optional) • For alerts: Implement INotificationProvider • Examples: Discord, Slack, Email notifications`, storageProvider: `šŸ’¾ Storage Provider (Optional) • For persistence: Implement IStorageProvider • Examples: Database, file system storage` }; return suggestions[type] || `Component '${type}' needs to be configured`; } /** * Get example usage for component type */ getExampleUsage(type) { const examples = { dataProvider: `new BinanceDataProvider()`, strategy: `SDK.strategy(async (data, portfolio) => ({ action: 'HOLD' }))`, executionEngine: `new MockExecutionEngine()`, positionManager: `new SimplePositionManager()`, backtestEngine: `new SimpleBacktestEngine()`, riskManager: `new BasicRiskManager()`, notificationProvider: `new DiscordNotifications()`, storageProvider: `new FileStorageProvider()` }; return examples[type] || `new ${type.charAt(0).toUpperCase() + type.slice(1)}()`; } /** * Initialize the framework with current configuration */ async initialize() { try { logger.info('šŸ”„ Initializing Trading Framework...'); // Validate required components are configured this.validateConfiguration(); // Initialize all active components await this.initializeComponents(); this.isInitialized = true; logger.info('āœ… Trading Framework initialized successfully'); return this; } catch (error) { logger.error('āŒ Framework initialization failed:', error); throw error; } } /** * Validate framework configuration */ validateConfiguration() { const requiredTypes = ['dataProvider', 'executionEngine', 'strategy', 'positionManager']; const missing = []; for (const type of requiredTypes) { if (!this.activeComponents.has(type)) { missing.push(type); } } if (missing.length > 0) { const errorMessage = `āŒ Missing required components: ${missing.join(', ')}\n\n` + `šŸ”§ Quick Fix:\n` + missing.map(type => `• ${type}: framework.use('${type}', ${this.getExampleUsage(type)})`).join('\n') + `\n\nšŸ“– For detailed setup instructions, visit: https://github.com/jaca8602/skayn-ai/tree/main/sdk`; throw new Error(errorMessage); } logger.info('āœ… Framework configuration validated'); } /** * Initialize all active components */ async initializeComponents() { const initOrder = [ 'storageProvider', 'dataProvider', 'riskManager', 'positionManager', 'executionEngine', 'notificationProvider', 'strategy' ]; for (const type of initOrder) { if (this.activeComponents.has(type)) { const component = this.activeComponents.get(type); if (component.initialize) { await component.initialize(); logger.info(`āœ… ${type} initialized`); } } } } /** * Start live trading */ async startTrading(config = {}) { if (!this.isInitialized) { await this.initialize(); } logger.info('šŸš€ Starting live trading...'); const strategy = this.get('strategy'); const executionEngine = this.get('executionEngine'); const dataProvider = this.get('dataProvider'); const positionManager = this.get('positionManager'); // Start data feed await dataProvider.start(); // Start trading loop this.tradingLoop = setInterval(async () => { try { // Get latest market data const marketData = await dataProvider.getLatestData(); const portfolioState = await positionManager.getPortfolioState(); // Generate trading signal const signal = await strategy.analyze(marketData, portfolioState); // Execute if signal is actionable if (signal.action !== 'HOLD') { await executionEngine.execute(signal, portfolioState); } // Update positions await positionManager.updatePositions(marketData); } catch (error) { logger.error('Trading loop error:', error); // Notify about errors if (this.activeComponents.has('notificationProvider')) { await this.get('notificationProvider').sendAlert('Trading Error', error.message); } } }, config.interval || 60000); // Default 1 minute logger.info('āœ… Live trading started'); } /** * Stop live trading */ async stopTrading() { if (this.tradingLoop) { clearInterval(this.tradingLoop); this.tradingLoop = null; } // Stop data feeds if (this.activeComponents.has('dataProvider')) { await this.get('dataProvider').stop(); } logger.info('šŸ›‘ Live trading stopped'); } /** * Run backtest */ async runBacktest(config = {}) { if (!this.activeComponents.has('backtestEngine')) { throw new Error('Backtest engine not configured. Use framework.use("backtestEngine", "engineName")'); } const backtestEngine = this.get('backtestEngine'); const strategy = this.get('strategy'); backtestEngine.setStrategy(strategy); // Set data provider if one is configured if (this.activeComponents.has('dataProvider')) { const dataProvider = this.get('dataProvider'); backtestEngine.setDataProvider(dataProvider); } return await backtestEngine.runBacktest(config); } /** * List available plugins */ listAvailablePlugins(type = null) { if (type) { return this.registry.listPlugins(type); } const allPlugins = {}; for (const pluginType of ['dataProvider', 'executionEngine', 'strategy', 'riskManager', 'positionManager', 'backtestEngine', 'notificationProvider', 'storageProvider']) { allPlugins[pluginType] = this.registry.listPlugins(pluginType); } return allPlugins; } /** * Get framework status */ getStatus() { return { initialized: this.isInitialized, trading: !!this.tradingLoop, activeComponents: Array.from(this.activeComponents.keys()), registryStats: this.registry.getStats(), uptime: process.uptime() }; } /** * Graceful shutdown */ async shutdown() { logger.info('šŸ”„ Shutting down Trading Framework...'); // Stop trading await this.stopTrading(); // Cleanup components for (const [type, component] of this.activeComponents) { if (component.cleanup) { try { await component.cleanup(); logger.info(`āœ… ${type} cleaned up`); } catch (error) { logger.error(`āŒ Error cleaning up ${type}:`, error); } } } logger.info('āœ… Trading Framework shutdown complete'); } } module.exports = TradingFramework;