skayn-trading-sdk
Version:
Professional Bitcoin trading strategy backtesting framework with institutional-grade architecture
393 lines (327 loc) ⢠11.6 kB
JavaScript
/**
* 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;