UNPKG

@copytrade/unified-broker

Version:

Unified broker interface library for Indian stock market brokers with plugin architecture

298 lines 12.1 kB
"use strict"; /** * Fyers Service Adapter * Adapts the existing FyersService to implement IBrokerService interface */ Object.defineProperty(exports, "__esModule", { value: true }); exports.FyersServiceAdapter = void 0; const IBrokerService_1 = require("../../interfaces/IBrokerService"); const fyersService_1 = require("../../services/fyersService"); class FyersServiceAdapter extends IBrokerService_1.IBrokerService { constructor() { super('fyers'); this.fyersService = new fyersService_1.FyersService(); } async login(credentials) { try { const fyersCredentials = credentials; // If we have an auth code, complete the OAuth flow if (fyersCredentials.authCode) { // Complete OAuth authentication const serviceCredentials = { clientId: fyersCredentials.clientId, secretKey: fyersCredentials.secretKey, redirectUri: fyersCredentials.redirectUri || '', authCode: fyersCredentials.authCode, accessToken: fyersCredentials.accessToken, refreshToken: fyersCredentials.refreshToken }; const tokenResponse = await this.fyersService.generateAccessToken(fyersCredentials.authCode, serviceCredentials); if (tokenResponse.success) { this.setConnected(true, fyersCredentials.clientId); return this.createSuccessResponse('OAuth authentication completed', { accountId: fyersCredentials.clientId, accessToken: tokenResponse.accessToken }); } else { return this.createErrorResponse(tokenResponse.message || 'OAuth completion failed', tokenResponse); } } else { // Generate OAuth URL for initial authentication const serviceCredentials = { clientId: fyersCredentials.clientId, secretKey: fyersCredentials.secretKey, redirectUri: fyersCredentials.redirectUri || '', authCode: fyersCredentials.authCode, accessToken: fyersCredentials.accessToken, refreshToken: fyersCredentials.refreshToken }; const response = await this.fyersService.login(serviceCredentials); if (!response.success && response.authUrl) { // Return OAuth URL for frontend to handle return { success: false, message: response.message || 'OAuth authentication required', data: { authUrl: response.authUrl } }; } else { return this.createErrorResponse(response.message || 'Failed to generate OAuth URL', response); } } } catch (error) { return this.createErrorResponse(error.message || 'Login failed', error); } } async logout() { try { const result = await this.fyersService.logout(); this.setConnected(false); // logout returns an object, check if it indicates success return typeof result === 'object' ? result.success === true : Boolean(result); } catch (error) { console.error('Logout failed:', error); return false; } } async validateSession(accountId) { try { return await this.fyersService.validateSession(); } catch (error) { return false; } } async placeOrder(orderRequest) { try { // Map unified order type to Fyers-specific format let fyersOrderType; switch (orderRequest.orderType) { case 'LIMIT': fyersOrderType = 'LIMIT'; break; case 'MARKET': fyersOrderType = 'MARKET'; break; case 'SL-LIMIT': fyersOrderType = 'SL'; break; case 'SL-MARKET': fyersOrderType = 'SL-M'; break; default: fyersOrderType = 'MARKET'; } // Map unified product type to Fyers format const productTypeMap = { 'CNC': 'CNC', 'MIS': 'INTRADAY', 'NRML': 'MARGIN', 'BO': 'BO', 'C': 'CNC', 'M': 'INTRADAY', 'H': 'MARGIN', 'B': 'BO' }; const fyersProductType = productTypeMap[orderRequest.productType] || orderRequest.productType; // Transform to Fyers-specific order format const fyersOrderRequest = { symbol: `${orderRequest.exchange}:${orderRequest.symbol}`, qty: orderRequest.quantity, type: fyersOrderType, side: orderRequest.action, // Keep as 'BUY' | 'SELL' string productType: fyersProductType, limitPrice: orderRequest.price || 0, stopPrice: orderRequest.triggerPrice || 0, validity: (orderRequest.validity === 'GTD' ? 'DAY' : orderRequest.validity), disclosedQty: 0, offlineOrder: false, stopLoss: 0, takeProfit: 0 }; const response = await this.fyersService.placeOrder(fyersOrderRequest); // Fyers response format: { s: 'ok'/'error', message: string, id?: string } if (response.s === 'ok') { return this.createSuccessResponse('Order placed successfully', { orderId: response.id, message: response.message, brokerOrderId: response.id, status: 'PLACED' }); } else { return this.createErrorResponse(response.message || 'Order placement failed', response); } } catch (error) { // Handle token expiry with auto-retry logic if (error.message?.includes('token') || error.message?.includes('unauthorized')) { return this.createErrorResponse('Authentication expired. Please reactivate your account.', { errorType: 'AUTH_EXPIRED', originalError: error.message }); } return this.createErrorResponse(error.message || 'Order placement failed', error); } } async getOrderStatus(accountId, orderId) { try { // Fyers doesn't have a separate getOrderStatus method, use getOrderBook const orderBook = await this.fyersService.getOrderBook(); // orderBook is an array, not an object with orderBook property if (!Array.isArray(orderBook)) { throw new Error('Invalid order book response'); } const order = orderBook.find((o) => o.id === orderId); if (order) { return { orderId: order.id || orderId, status: order.status || 'UNKNOWN', quantity: order.qty || 0, filledQuantity: order.filledQty || 0, price: order.limitPrice || 0, averagePrice: order.avgPrice || 0, timestamp: new Date(order.orderDateTime || Date.now()) }; } else { throw new Error(`Order ${orderId} not found`); } } catch (error) { throw new Error(`Failed to get order status: ${error.message}`); } } async getOrderHistory(accountId) { try { // Use getOrderBook for order history in Fyers const response = await this.fyersService.getOrderBook(); // response is an array, not an object with orderBook property if (!Array.isArray(response)) { return []; } return response.map((order) => ({ orderId: order.id || '', status: order.status || 'UNKNOWN', quantity: order.qty || 0, filledQuantity: order.filledQty || 0, price: order.limitPrice || 0, averagePrice: order.avgPrice || 0, timestamp: new Date(order.orderDateTime || Date.now()) })); } catch (error) { throw new Error(`Failed to get order history: ${error.message}`); } } async getPositions(accountId) { try { const response = await this.fyersService.getPositions(); // response is an array, not an object with netPositions property if (!Array.isArray(response)) { return []; } return response.map((position) => ({ symbol: position.symbol || '', quantity: position.netQty || 0, averagePrice: position.avgPrice || 0, currentPrice: position.ltp || 0, pnl: position.pl || 0, exchange: position.exchange || '', productType: position.productType || '' })); } catch (error) { throw new Error(`Failed to get positions: ${error.message}`); } } async getQuote(symbol, exchange) { try { const response = await this.fyersService.getQuotes([`${exchange}:${symbol}`]); if (!response || response.length === 0) { throw new Error('No quote data received'); } const quote = response[0]; if (!quote) { throw new Error('No quote data in response'); } return { symbol: quote.symbol || symbol, price: quote.ltp || 0, change: quote.chng || 0, changePercent: quote.chngPercent || 0, volume: quote.volume || 0, exchange: exchange, timestamp: new Date() }; } catch (error) { throw new Error(`Failed to get quote: ${error.message}`); } } async searchSymbols(query, exchange) { try { // Fyers doesn't have a searchSymbols method, return empty array for now console.warn('searchSymbols not implemented for Fyers'); return []; } catch (error) { throw new Error(`Failed to search symbols: ${error.message}`); } } // Fyers-specific methods that can be accessed if needed getFyersService() { return this.fyersService; } async refreshToken() { try { // Fyers refresh token functionality - simplified for now console.warn('refreshToken not fully implemented for Fyers'); return false; } catch (error) { console.warn('refreshToken not available, using fallback'); return false; } } async completeAuth(authCode) { try { // Simplified auth completion - just store the auth code this.setConnected(true, this.accountId); return this.createSuccessResponse('Authentication completed', { accountId: this.accountId, authCode: authCode }); } catch (error) { return this.createErrorResponse(error.message || 'Authentication failed', error); } } } exports.FyersServiceAdapter = FyersServiceAdapter; //# sourceMappingURL=FyersServiceAdapter.js.map