// TQSDK数据源实现 import { DataSource } from './DataSource'; import TqSdk from 'tqsdk'; import TqAccount from 'tqsdk'; export class TQDataSource implements DataSource { private tq: any = null; private initialized: boolean = false; private config: { username?: string; password?: string; timeout?: number; retries?: number; maxConnections?: number; }; constructor(config: any = {}) { this.config = { username: config.username || '', password: config.password || '', timeout: config.timeout || 30000, retries: config.retries || 3, maxConnections: config.maxConnections || 5 }; } async initialize(): Promise { try { // 创建TQSDK实例,使用配置的参数 const tqConfig: any = {}; // 如果有用户名和密码,使用用户名密码登录 if (this.config.username && this.config.password) { tqConfig.account = this.config.username; tqConfig.password = this.config.password; } this.tq = new TqSdk(tqConfig); // 等待初始化完成 await new Promise((resolve) => { this.tq?.on('connected', () => { resolve(true); }); }); this.initialized = true; console.log('TQSDK数据源初始化成功'); return true; } catch (error) { console.error('TQSDK数据源初始化失败:', error); this.initialized = false; return false; } } async getContractList(): Promise { if (!this.initialized || !this.tq) { throw new Error('TQSDK数据源未初始化'); } try { // 获取所有合约 const contracts = await this.tq.get_contracts(); // 过滤期货合约,排除期权等其他类型 const futuresContracts = contracts.filter((contract: any) => { return contract.exchange.includes('SHFE') || contract.exchange.includes('DCE') || contract.exchange.includes('CZCE') || contract.exchange.includes('INE'); }); return futuresContracts; } catch (error) { console.error('获取合约列表失败:', error); throw error; } } async getContractDetail(symbol: string): Promise { if (!this.initialized || !this.tq) { throw new Error('TQSDK数据源未初始化'); } try { // 获取合约详情 const contract = await this.tq.get_contract(symbol); return contract; } catch (error) { console.error(`获取合约${symbol}详情失败:`, error); throw error; } } async getKlineData(symbol: string, period: string, count: number): Promise { if (!this.initialized || !this.tq) { throw new Error('TQSDK数据源未初始化'); } try { // 转换周期格式 const tqPeriod = this.convertPeriod(period); // 获取K线数据 const kline = await this.tq.get_kline_serial(symbol, tqPeriod, count); return kline; } catch (error) { console.error(`获取合约${symbol}K线数据失败:`, error); throw error; } } async getTickData(symbol: string): Promise { if (!this.initialized || !this.tq) { throw new Error('TQSDK数据源未初始化'); } try { // 获取实时行情数据 const tick = await this.tq.get_tick_serial(symbol, 1); return tick[0]; } catch (error) { console.error(`获取合约${symbol}实时行情数据失败:`, error); throw error; } } async getMarketOverview(): Promise { if (!this.initialized || !this.tq) { throw new Error('TQSDK数据源未初始化'); } try { // 获取所有合约 const contracts = await this.getContractList(); // 限制获取的合约数量,避免请求过多 const limitedContracts = contracts.slice(0, 20); // 获取每个合约的实时行情 const overview = []; for (const contract of limitedContracts) { try { const tick = await this.getTickData(contract.symbol); overview.push({ symbol: contract.symbol, name: contract.name, price: tick.last_price, change: tick.price_change, change_percent: tick.price_change / tick.pre_close * 100, volume: tick.volume, open_interest: tick.open_interest }); } catch (error) { console.error(`获取合约${contract.symbol}行情失败:`, error); } } return overview; } catch (error) { console.error('获取市场概览失败:', error); throw error; } } async getHistoricalTrades(symbol: string, start: number, end: number): Promise { if (!this.initialized || !this.tq) { throw new Error('TQSDK数据源未初始化'); } try { // 获取历史成交数据 const trades = await this.tq.get_trade_serial(symbol, start, end); return trades; } catch (error) { console.error(`获取合约${symbol}历史成交数据失败:`, error); throw error; } } async close(): Promise { if (this.tq) { if (typeof this.tq.close === 'function') { this.tq.close(); } this.tq = null; this.initialized = false; console.log('TQSDK数据源已关闭'); } } // 转换周期格式 private convertPeriod(period: string): string { switch (period) { case '1M': return '1min'; case '5M': return '5min'; case '15M': return '15min'; case '30M': return '30min'; case '1H': return '60min'; case '4H': return '240min'; case '1D': return '1d'; case '1W': return '1w'; default: return '1min'; } } }