@copytrade/unified-broker
Version:
Unified broker interface library for Indian stock market brokers with plugin architecture
753 lines • 35 kB
JavaScript
"use strict";
/**
* Unified Shoonya Service
* Implements the new IUnifiedBrokerService interface with all business logic encapsulated
* Handles authentication, token management, and provides standardized responses
* Uses comprehensive error handler for consistent error processing
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.UnifiedShoonyaService = void 0;
const UnifiedBrokerResponse_1 = require("../../interfaces/UnifiedBrokerResponse");
const shoonyaService_1 = require("../../services/shoonyaService");
class UnifiedShoonyaService {
constructor() {
this.isConnectedFlag = false;
this.accountInfo = null;
this.tokenInfo = null;
this.currentCredentials = null;
this.shoonyaService = new shoonyaService_1.ShoonyaService();
}
getBrokerName() {
return 'shoonya';
}
/**
* Connect to Shoonya broker with direct authentication
* Handles all authentication logic internally and returns standardized response
*/
async connect(credentials) {
try {
const shoonyaCredentials = credentials;
this.currentCredentials = shoonyaCredentials;
console.log('🔄 Attempting Shoonya authentication...');
const response = await this.shoonyaService.login(shoonyaCredentials);
if (response.stat === 'Ok') {
// Authentication successful - populate account and token info
this.accountInfo = {
accountId: response.actid || shoonyaCredentials.userId,
userName: response.uname || shoonyaCredentials.userId,
email: response.email || '',
brokerDisplayName: 'Shoonya (Finvasia)',
exchanges: response.exarr || ['NSE', 'BSE'],
products: response.prarr || ['C', 'M', 'I']
};
// Shoonya tokens don't expire (infinity)
this.tokenInfo = {
accessToken: response.susertoken,
refreshToken: undefined, // Shoonya doesn't use refresh tokens
expiryTime: null, // null indicates infinity
isExpired: false,
canRefresh: false // Shoonya doesn't need token refresh
};
this.isConnectedFlag = true;
console.log('✅ Shoonya authentication successful');
return UnifiedBrokerResponse_1.UnifiedResponseHelper.createSuccessResponse('Shoonya account connected successfully', 'ACTIVE', 'DIRECT_AUTH', this.accountInfo, this.tokenInfo);
}
else {
// Authentication failed
console.error('❌ Shoonya authentication failed:', response.emsg);
return UnifiedBrokerResponse_1.UnifiedResponseHelper.createErrorResponse(response.emsg || 'Shoonya authentication failed', 'AUTH_FAILED', 'INACTIVE', 'REAUTH_REQUIRED');
}
}
catch (error) {
console.error('🚨 Shoonya connection error:', error);
// Determine error type based on error message
let errorType = 'BROKER_ERROR';
if (error.message?.includes('network') || error.message?.includes('timeout')) {
errorType = 'NETWORK_ERROR';
}
else if (error.message?.includes('validation') || error.message?.includes('invalid')) {
errorType = 'VALIDATION_ERROR';
}
return UnifiedBrokerResponse_1.UnifiedResponseHelper.createErrorResponse(error.message || 'Failed to connect to Shoonya', errorType, 'INACTIVE', 'REAUTH_REQUIRED');
}
}
/**
* Shoonya doesn't use OAuth, so this method returns an error
*/
async completeOAuth(_authCode, _credentials) {
return UnifiedBrokerResponse_1.UnifiedResponseHelper.createErrorResponse('Shoonya does not support OAuth authentication', 'VALIDATION_ERROR', 'INACTIVE', 'DIRECT_AUTH');
}
/**
* Shoonya tokens don't expire, so refresh is not needed
*/
async refreshToken(_credentials) {
if (this.isConnectedFlag && this.tokenInfo) {
return UnifiedBrokerResponse_1.UnifiedResponseHelper.createSuccessResponse('Shoonya tokens do not require refresh', 'ACTIVE', 'DIRECT_AUTH', this.accountInfo || undefined, this.tokenInfo || undefined);
}
return UnifiedBrokerResponse_1.UnifiedResponseHelper.createErrorResponse('No active Shoonya session to refresh', 'AUTH_FAILED', 'INACTIVE', 'REAUTH_REQUIRED');
}
/**
* Validate current Shoonya session
*/
async validateSession(credentials) {
if (!this.isConnectedFlag || !this.tokenInfo) {
return {
isValid: false,
accountStatus: 'INACTIVE',
message: 'No active Shoonya session',
errorType: 'AUTH_FAILED'
};
}
try {
// For Shoonya, we can validate by making a simple API call
// Since tokens don't expire, if we have a connection, it should be valid
const shoonyaCredentials = credentials;
const isValid = await this.shoonyaService.validateSession(shoonyaCredentials.userId);
if (isValid) {
return {
isValid: true,
accountStatus: 'ACTIVE',
message: 'Shoonya session is valid',
tokenInfo: this.tokenInfo
};
}
else {
// Session is no longer valid, reset state
this.isConnectedFlag = false;
this.accountInfo = null;
this.tokenInfo = null;
return {
isValid: false,
accountStatus: 'INACTIVE',
message: 'Shoonya session has expired',
errorType: 'TOKEN_EXPIRED'
};
}
}
catch (error) {
console.error('🚨 Shoonya session validation error:', error);
return {
isValid: false,
accountStatus: 'INACTIVE',
message: 'Failed to validate Shoonya session',
errorType: 'NETWORK_ERROR'
};
}
}
/**
* Disconnect from Shoonya
*/
async disconnect() {
try {
if (this.isConnectedFlag) {
await this.shoonyaService.logout();
}
// Reset all state
this.isConnectedFlag = false;
this.accountInfo = null;
this.tokenInfo = null;
this.currentCredentials = null;
console.log('✅ Shoonya disconnected successfully');
return true;
}
catch (error) {
console.error('🚨 Shoonya disconnect error:', error);
// Reset state even if logout fails
this.isConnectedFlag = false;
this.accountInfo = null;
this.tokenInfo = null;
this.currentCredentials = null;
return false;
}
}
/**
* Get current account information
*/
getAccountInfo() {
return this.accountInfo;
}
/**
* Get current token information
*/
getTokenInfo() {
return this.tokenInfo;
}
/**
* Check if currently connected
*/
isConnected() {
return this.isConnectedFlag;
}
/**
* Get current account status
*/
getAccountStatus() {
if (!this.isConnectedFlag) {
return 'INACTIVE';
}
// Shoonya tokens don't expire, so if connected, always active
return 'ACTIVE';
}
// Trading operations - delegate to existing service
async placeOrder(orderRequest) {
if (!this.isConnectedFlag) {
throw new Error('Not connected to Shoonya. Please authenticate first.');
}
// Transform unified order request to Shoonya format
const shoonyaOrderRequest = {
userId: this.accountInfo?.accountId || '',
buyOrSell: orderRequest.action === 'BUY' ? 'B' : 'S',
productType: this.mapProductType(orderRequest.productType),
exchange: orderRequest.exchange || 'NSE',
tradingSymbol: orderRequest.symbol, // Map symbol to tradingSymbol
quantity: orderRequest.quantity,
discloseQty: 0,
priceType: this.mapOrderType(orderRequest.orderType),
price: orderRequest.price || 0,
triggerPrice: orderRequest.triggerPrice || 0,
retention: orderRequest.validity || 'DAY',
amo: 'NO',
remarks: orderRequest.remarks || 'Order via CopyTrade Pro'
};
try {
const shoonyaResponse = await this.shoonyaService.placeOrder(shoonyaOrderRequest);
// Transform Shoonya response to unified format
if (shoonyaResponse.stat === 'Ok') {
return {
success: true,
message: 'Order placed successfully',
data: {
brokerOrderId: shoonyaResponse.norenordno,
orderId: shoonyaResponse.norenordno,
status: 'PLACED'
}
};
}
else {
return {
success: false,
message: shoonyaResponse.emsg || 'Order placement failed',
data: null
};
}
}
catch (error) {
return {
success: false,
message: error.message || 'Order placement failed',
data: null
};
}
}
async cancelOrder(orderId) {
console.log(`🚫 UnifiedShoonyaService.cancelOrder called for order: ${orderId}`);
// Validate authentication state
if (!this.isConnectedFlag || !this.tokenInfo) {
console.error('❌ UnifiedShoonyaService: Not authenticated for order cancellation');
return {
success: false,
message: 'Not connected to Shoonya. Please authenticate first.',
errorType: 'AUTH_FAILED',
data: null
};
}
try {
console.log('🔄 UnifiedShoonyaService: Cancelling Shoonya order via API...');
const shoonyaResponse = await this.shoonyaService.cancelOrder(orderId);
console.log('🔍 UnifiedShoonyaService: Cancel order response:', shoonyaResponse);
// Transform Shoonya response to unified format
if (shoonyaResponse.stat === 'Ok') {
console.log(`✅ UnifiedShoonyaService: Order ${orderId} cancelled successfully`);
return {
success: true,
message: 'Order cancelled successfully',
data: {
orderId: orderId,
brokerOrderId: orderId,
status: 'CANCELLED'
}
};
}
else {
console.error(`❌ UnifiedShoonyaService: Order cancellation failed:`, shoonyaResponse.emsg);
return {
success: false,
message: shoonyaResponse.emsg || 'Order cancellation failed',
data: null
};
}
}
catch (error) {
console.error('🚨 UnifiedShoonyaService: Order cancellation error:', error.message);
return {
success: false,
message: error.message || 'Order cancellation failed',
data: null
};
}
}
async modifyOrder(orderId, modifications) {
console.log(`✏️ UnifiedShoonyaService.modifyOrder called for order: ${orderId}`, modifications);
// Validate authentication state
if (!this.isConnectedFlag || !this.tokenInfo) {
console.error('❌ UnifiedShoonyaService: Not authenticated for order modification');
return {
success: false,
message: 'Not connected to Shoonya. Please authenticate first.',
errorType: 'AUTH_FAILED',
data: null
};
}
try {
console.log('🔄 UnifiedShoonyaService: Modifying Shoonya order via API...');
const shoonyaResponse = await this.shoonyaService.modifyOrder(orderId, modifications);
console.log('🔍 UnifiedShoonyaService: Modify order response:', shoonyaResponse);
// Transform Shoonya response to unified format
if (shoonyaResponse.stat === 'Ok') {
console.log(`✅ UnifiedShoonyaService: Order ${orderId} modified successfully`);
return {
success: true,
message: 'Order modified successfully',
data: {
orderId: orderId,
brokerOrderId: orderId,
status: 'MODIFIED'
}
};
}
else {
console.error(`❌ UnifiedShoonyaService: Order modification failed:`, shoonyaResponse.emsg);
return {
success: false,
message: shoonyaResponse.emsg || 'Order modification failed',
data: null
};
}
}
catch (error) {
console.error('🚨 UnifiedShoonyaService: Order modification error:', error.message);
return {
success: false,
message: error.message || 'Order modification failed',
data: null
};
}
}
mapProductType(productType) {
const mapping = {
'CNC': 'C',
'MIS': 'M',
'NRML': 'M',
'BO': 'B',
'CO': 'H'
};
return mapping[productType] || 'C'; // Default to CNC
}
mapOrderType(orderType) {
const mapping = {
'MARKET': 'MKT',
'LIMIT': 'LMT',
'SL-LIMIT': 'SL-LMT',
'SL-MARKET': 'SL-MKT'
};
return mapping[orderType] || 'MKT'; // Default to MARKET
}
/**
* Transform Shoonya order status response to unified format
*/
transformToUnifiedOrderStatus(shoonyaResponse, orderId) {
console.log('🔄 UnifiedShoonyaService: Transforming Shoonya response to unified format');
// Map Shoonya status to unified status
const unifiedStatus = this.mapShoonyaStatusToUnified(shoonyaResponse.status);
// Parse numeric values safely
const quantity = parseFloat(shoonyaResponse.qty || '0');
const filledQuantity = parseFloat(shoonyaResponse.fillshares || '0');
const price = parseFloat(shoonyaResponse.prc || '0');
const averagePrice = parseFloat(shoonyaResponse.avgprc || '0');
// Create unified order status object
const unifiedOrderStatus = {
orderId: orderId, // Internal order ID
brokerOrderId: shoonyaResponse.norenordno || orderId, // Broker's order ID
status: unifiedStatus, // Standardized status
symbol: shoonyaResponse.tsym || '', // Trading symbol
quantity: quantity, // Order quantity
filledQuantity: filledQuantity, // Executed quantity
price: price, // Order price
averagePrice: averagePrice, // Average execution price
timestamp: this.parseTimestamp(shoonyaResponse.exch_tm || shoonyaResponse.norentm), // Last update time
rejectionReason: shoonyaResponse.rejreason || undefined, // If rejected
exchange: this.extractExchange(shoonyaResponse.tsym), // Exchange name
brokerName: 'shoonya', // Broker identifier
// Additional fields for backward compatibility and debugging
rawResponse: shoonyaResponse, // Original response for debugging
orderTime: shoonyaResponse.norentm || '', // Order placement time
updateTime: shoonyaResponse.exch_tm || '', // Last update time from exchange
};
console.log('✅ UnifiedShoonyaService: Unified order status created:', unifiedOrderStatus);
return unifiedOrderStatus;
}
/**
* Map Shoonya order status to unified status types
*/
mapShoonyaStatusToUnified(shoonyaStatus) {
const SHOONYA_STATUS_MAP = {
'OPEN': 'PLACED',
'COMPLETE': 'EXECUTED',
'CANCELLED': 'CANCELLED',
'REJECTED': 'REJECTED',
'TRIGGER_PENDING': 'PENDING',
'PARTIALLY_FILLED': 'PARTIALLY_FILLED',
// Additional mappings for other possible Shoonya statuses
'NEW': 'PLACED',
'PENDING': 'PENDING',
'MODIFY_PENDING': 'PENDING',
'CANCEL_PENDING': 'PENDING',
'AMO_REQ_RECEIVED': 'PENDING'
};
const unifiedStatus = SHOONYA_STATUS_MAP[shoonyaStatus] || 'PLACED';
console.log(`🔄 UnifiedShoonyaService: Mapped Shoonya status '${shoonyaStatus}' to unified status '${unifiedStatus}'`);
return unifiedStatus;
}
/**
* Parse timestamp from Shoonya response
*/
parseTimestamp(timestamp) {
if (!timestamp) {
return new Date(); // Return current time if no timestamp provided
}
try {
// Shoonya timestamps are typically in format: "DD-MM-YYYY HH:mm:ss"
// Convert to ISO format for proper parsing
const parsedDate = new Date(timestamp);
return isNaN(parsedDate.getTime()) ? new Date() : parsedDate;
}
catch (error) {
console.warn('⚠️ UnifiedShoonyaService: Failed to parse timestamp:', timestamp);
return new Date();
}
}
/**
* Extract exchange from trading symbol
*/
extractExchange(tradingSymbol) {
if (!tradingSymbol)
return 'NSE'; // Default to NSE
// Shoonya trading symbols often include exchange info
if (tradingSymbol.includes('-EQ'))
return 'NSE';
if (tradingSymbol.includes('BSE:'))
return 'BSE';
// Default to NSE if cannot determine
return 'NSE';
}
// Legacy error handling methods removed - now using comprehensive error handler
async getOrderStatus(accountId, orderId) {
const startTime = performance.now();
const operationId = `unified_getOrderStatus_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
// Enhanced logging context
const logContext = {
userId: accountId,
brokerName: 'shoonya',
accountId: accountId,
operation: 'getOrderStatus',
orderId: orderId,
operationId,
component: 'UNIFIED_SHOONYA_SERVICE',
timestamp: new Date().toISOString()
};
console.log(`[${new Date().toISOString()}] [INFO] [UNIFIED_ORDER_STATUS] Order status request initiated`, logContext);
console.log(`📊 UnifiedShoonyaService.getOrderStatus called for order: ${orderId}, account: ${accountId}`);
// Validate authentication state
if (!this.isConnectedFlag || !this.tokenInfo) {
const responseTime = performance.now() - startTime;
console.log(`[${new Date().toISOString()}] [ERROR] [UNIFIED_ORDER_STATUS] Authentication validation failed`, {
...logContext,
responseTime: Math.round(responseTime),
isConnected: this.isConnectedFlag,
hasTokenInfo: !!this.tokenInfo,
errorType: 'AUTH_FAILED'
});
console.error('❌ UnifiedShoonyaService: Not authenticated or missing token info');
return {
success: false,
message: 'Not connected to Shoonya. Please authenticate first.',
errorType: 'AUTH_FAILED',
data: null,
retryable: false,
suggestedActions: [
'Click the "Reconnect Account" button',
'Re-enter your Shoonya credentials',
'Ensure your account is active'
]
};
}
// Note: Comprehensive error handler would be imported here in a real implementation
// const errorHandlerModule = await import('../../../../../backend/src/services/comprehensiveErrorHandler');
// comprehensiveErrorHandler = errorHandlerModule.comprehensiveErrorHandler;
const comprehensiveErrorHandler = null; // Placeholder for now
const context = {
userId: accountId,
brokerName: 'shoonya',
accountId: accountId,
operation: 'getOrderStatus',
timestamp: new Date()
};
// Validate session before making API call
try {
const sessionStartTime = performance.now();
const sessionValidation = await this.validateSession(this.currentCredentials);
const sessionValidationTime = performance.now() - sessionStartTime;
console.log(`[${new Date().toISOString()}] [DEBUG] [UNIFIED_ORDER_STATUS] Session validation completed`, {
...logContext,
sessionValidationTime: Math.round(sessionValidationTime),
isValid: sessionValidation.isValid,
errorType: sessionValidation.errorType
});
if (!sessionValidation.isValid) {
const responseTime = performance.now() - startTime;
console.log(`[${new Date().toISOString()}] [ERROR] [UNIFIED_ORDER_STATUS] Session validation failed`, {
...logContext,
responseTime: Math.round(responseTime),
validationMessage: sessionValidation.message,
errorType: sessionValidation.errorType
});
console.error('❌ UnifiedShoonyaService: Session validation failed');
const errorMessage = sessionValidation.message || 'Shoonya session is invalid';
const userFriendlyMessage = comprehensiveErrorHandler ?
comprehensiveErrorHandler.getUserFriendlyMessage({ message: errorMessage }, context) :
errorMessage;
return {
success: false,
message: userFriendlyMessage,
errorType: sessionValidation.errorType || 'TOKEN_EXPIRED',
data: null,
retryable: false,
suggestedActions: [
'Reconnect your Shoonya account',
'Check your credentials',
'Ensure your account is active'
]
};
}
}
catch (error) {
const responseTime = performance.now() - startTime;
console.log(`[${new Date().toISOString()}] [ERROR] [UNIFIED_ORDER_STATUS] Session validation error`, {
...logContext,
responseTime: Math.round(responseTime),
errorMessage: error.message,
errorType: 'NETWORK_ERROR'
});
console.error('🚨 UnifiedShoonyaService: Session validation error:', error.message);
const userFriendlyMessage = comprehensiveErrorHandler ?
comprehensiveErrorHandler.getUserFriendlyMessage(error, context) :
'Failed to validate Shoonya session';
return {
success: false,
message: userFriendlyMessage,
errorType: 'NETWORK_ERROR',
data: null,
retryable: true,
suggestedActions: [
'Check your internet connection',
'Try again in a few moments',
'Contact support if issue persists'
]
};
}
try {
console.log(`🔄 UnifiedShoonyaService: Fetching order status from Shoonya API...`);
// Use comprehensive error handler if available
const executeOperation = async () => {
// Check rate limiting if comprehensive error handler is available
if (comprehensiveErrorHandler) {
const rateLimitStartTime = performance.now();
const rateLimitCheck = comprehensiveErrorHandler.checkRateLimit(accountId, 'shoonya', 'getOrderStatus');
const rateLimitCheckTime = performance.now() - rateLimitStartTime;
console.log(`[${new Date().toISOString()}] [DEBUG] [UNIFIED_ORDER_STATUS] Rate limit check completed`, {
...logContext,
rateLimitCheckTime: Math.round(rateLimitCheckTime),
allowed: rateLimitCheck.allowed,
remaining: rateLimitCheck.remaining
});
if (!rateLimitCheck.allowed) {
const waitTime = rateLimitCheck.waitTime || 0;
console.log(`[${new Date().toISOString()}] [WARN] [UNIFIED_ORDER_STATUS] Rate limit exceeded`, {
...logContext,
waitTime: Math.ceil(waitTime / 1000),
remaining: rateLimitCheck.remaining,
resetTime: rateLimitCheck.resetTime
});
const error = new Error(`Rate limit exceeded. Please wait ${Math.ceil(waitTime / 1000)} seconds.`);
error.code = 'RATE_LIMITED';
throw error;
}
}
const apiStartTime = performance.now();
const result = await this.shoonyaService.getOrderStatus(accountId, orderId);
const apiDuration = performance.now() - apiStartTime;
console.log(`[${new Date().toISOString()}] [DEBUG] [UNIFIED_ORDER_STATUS] Shoonya API call completed`, {
...logContext,
apiDuration: Math.round(apiDuration),
success: result?.stat === 'Ok'
});
return result;
};
let shoonyaResponse;
if (comprehensiveErrorHandler) {
// Use comprehensive error handler with retry logic
console.log(`[${new Date().toISOString()}] [DEBUG] [UNIFIED_ORDER_STATUS] Using comprehensive error handler with retry logic`, logContext);
shoonyaResponse = await comprehensiveErrorHandler.executeWithRetry(executeOperation, context, {
maxRetries: 3,
baseDelay: 1000,
maxDelay: 10000
});
}
else {
// Fallback to direct execution with basic retry
console.log(`[${new Date().toISOString()}] [DEBUG] [UNIFIED_ORDER_STATUS] Using fallback execution with basic retry`, logContext);
let lastError = null;
const maxRetries = 3;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
console.log(`[${new Date().toISOString()}] [DEBUG] [UNIFIED_ORDER_STATUS] Retry attempt ${attempt}/${maxRetries}`, {
...logContext,
attempt,
maxRetries
});
shoonyaResponse = await executeOperation();
break; // Success, exit retry loop
}
catch (error) {
lastError = error;
console.log(`[${new Date().toISOString()}] [WARN] [UNIFIED_ORDER_STATUS] Retry attempt ${attempt} failed`, {
...logContext,
attempt,
errorMessage: error.message,
errorType: error.errorType || error.code
});
if (attempt === maxRetries) {
throw error; // Last attempt failed, throw error
}
// Wait before retry (exponential backoff)
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 10000);
console.log(`[${new Date().toISOString()}] [DEBUG] [UNIFIED_ORDER_STATUS] Waiting ${delay}ms before retry`, {
...logContext,
delay,
nextAttempt: attempt + 1
});
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
const totalResponseTime = performance.now() - startTime;
console.log('🔍 UnifiedShoonyaService: Raw Shoonya response:', shoonyaResponse);
// Handle successful response
if (shoonyaResponse.stat === 'Ok') {
console.log(`✅ UnifiedShoonyaService: Successfully retrieved order status for ${orderId}`);
// Transform to unified format
const unifiedOrderStatus = this.transformToUnifiedOrderStatus(shoonyaResponse, orderId);
console.log(`[${new Date().toISOString()}] [INFO] [UNIFIED_ORDER_STATUS] Order status retrieved successfully`, {
...logContext,
responseTime: Math.round(totalResponseTime),
orderStatus: unifiedOrderStatus.status,
symbol: unifiedOrderStatus.symbol,
quantity: unifiedOrderStatus.quantity,
filledQuantity: unifiedOrderStatus.filledQuantity
});
console.log('🔄 UnifiedShoonyaService: Transformed to unified format:', unifiedOrderStatus);
return {
success: true,
message: 'Order status retrieved successfully',
data: unifiedOrderStatus
};
}
else {
// Handle API error response
console.error(`❌ UnifiedShoonyaService: Shoonya API error for order ${orderId}:`, shoonyaResponse.emsg);
console.log(`[${new Date().toISOString()}] [ERROR] [UNIFIED_ORDER_STATUS] Shoonya API error response`, {
...logContext,
responseTime: Math.round(totalResponseTime),
errorMessage: shoonyaResponse.emsg,
errorType: shoonyaResponse.errorType,
brokerResponse: shoonyaResponse
});
const userFriendlyMessage = comprehensiveErrorHandler ?
comprehensiveErrorHandler.getUserFriendlyMessage({ message: shoonyaResponse.emsg, code: shoonyaResponse.errorType }, context) :
shoonyaResponse.emsg;
const suggestedActions = comprehensiveErrorHandler ?
comprehensiveErrorHandler.getSuggestedActions({ message: shoonyaResponse.emsg, code: shoonyaResponse.errorType }, context) :
['Try again later', 'Contact support if issue persists'];
return {
success: false,
message: userFriendlyMessage,
data: null,
originalError: shoonyaResponse,
retryable: shoonyaResponse.retryable || false,
suggestedActions: suggestedActions
};
}
}
catch (error) {
const totalResponseTime = performance.now() - startTime;
console.error('🚨 UnifiedShoonyaService: Order status retrieval error:', error.message);
console.log(`[${new Date().toISOString()}] [ERROR] [UNIFIED_ORDER_STATUS] Order status operation failed`, {
...logContext,
responseTime: Math.round(totalResponseTime),
errorMessage: error.message,
errorType: error.errorType || error.code,
stack: error.stack
});
// Handle different error types with comprehensive error handler
const userFriendlyMessage = comprehensiveErrorHandler ?
comprehensiveErrorHandler.getUserFriendlyMessage(error, context) :
error.message;
const suggestedActions = comprehensiveErrorHandler ?
comprehensiveErrorHandler.getSuggestedActions(error, context) :
['Try again later', 'Contact support if issue persists'];
const isRetryable = comprehensiveErrorHandler ?
comprehensiveErrorHandler.isRetryable(error, context) :
false;
return {
success: false,
message: userFriendlyMessage,
data: null,
originalError: error.message,
retryable: isRetryable,
suggestedActions: suggestedActions,
context: context,
responseTime: Math.round(totalResponseTime),
operationId
};
}
}
async getOrderHistory(accountId) {
if (!this.isConnectedFlag) {
throw new Error('Not connected to Shoonya. Please authenticate first.');
}
// Use getOrderBook as Shoonya doesn't have separate getOrderHistory
return this.shoonyaService.getOrderBook(accountId);
}
async getPositions(accountId) {
if (!this.isConnectedFlag) {
throw new Error('Not connected to Shoonya. Please authenticate first.');
}
return this.shoonyaService.getPositions(accountId);
}
async getQuote(symbol, exchange) {
if (!this.isConnectedFlag) {
throw new Error('Not connected to Shoonya. Please authenticate first.');
}
// For Shoonya, we need token instead of symbol, this is a simplified implementation
return this.shoonyaService.getQuotes(exchange, symbol);
}
async searchSymbols(query, exchange) {
if (!this.isConnectedFlag) {
throw new Error('Not connected to Shoonya. Please authenticate first.');
}
// Use searchScrip method from ShoonyaService
return this.shoonyaService.searchScrip(exchange, query);
}
}
exports.UnifiedShoonyaService = UnifiedShoonyaService;
//# sourceMappingURL=UnifiedShoonyaService.js.map