@copytrade/broker-shoonya
Version:
Shoonya (Finvasia) broker plugin for @copytrade/unified-broker
222 lines • 8.76 kB
JavaScript
;
/**
* Shoonya Service Adapter
* Adapts the existing ShoonyaService to implement IBrokerService interface
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ShoonyaServiceAdapter = void 0;
const unified_broker_1 = require("@copytrade/unified-broker");
const shoonyaService_1 = require("./shoonyaService");
class ShoonyaServiceAdapter extends unified_broker_1.IBrokerService {
constructor() {
super('shoonya');
this.shoonyaService = new shoonyaService_1.ShoonyaService();
}
async login(credentials) {
try {
const shoonyaCredentials = credentials;
const response = await this.shoonyaService.login(shoonyaCredentials);
if (response.stat === 'Ok') {
this.setConnected(true, response.actid);
return this.createSuccessResponse('Login successful', {
accountId: response.actid,
userName: response.uname,
email: response.email,
exchanges: response.exarr,
products: response.prarr
});
}
else {
return this.createErrorResponse(response.emsg || 'Login failed', response);
}
}
catch (error) {
return this.createErrorResponse(error.message || 'Login failed', error);
}
}
async logout() {
try {
const result = await this.shoonyaService.logout();
this.setConnected(false);
return result;
}
catch (error) {
console.error('Logout failed:', error);
return false;
}
}
async validateSession(accountId) {
try {
return await this.shoonyaService.validateSession(accountId || this.accountId || '');
}
catch (error) {
return false;
}
}
async placeOrder(orderRequest) {
try {
// Map unified order type to Shoonya-specific format
let shoonyaPriceType;
switch (orderRequest.orderType) {
case 'LIMIT':
shoonyaPriceType = 'LMT';
break;
case 'MARKET':
shoonyaPriceType = 'MKT';
break;
case 'SL-LIMIT':
shoonyaPriceType = 'SL-LMT';
break;
case 'SL-MARKET':
shoonyaPriceType = 'SL-MKT';
break;
default:
shoonyaPriceType = 'MKT';
}
// Map unified product type to Shoonya format
const productTypeMap = {
'CNC': 'C',
'MIS': 'M',
'NRML': 'H',
'BO': 'B',
'C': 'C',
'M': 'M',
'H': 'H',
'B': 'B'
};
const shoonyaProductType = productTypeMap[orderRequest.productType] || orderRequest.productType;
// Transform to Shoonya-specific order format
const shoonyaOrderRequest = {
userId: orderRequest.accountId || this.accountId || '',
buyOrSell: orderRequest.action === 'BUY' ? 'B' : 'S',
productType: shoonyaProductType,
exchange: orderRequest.exchange || 'NSE',
tradingSymbol: orderRequest.symbol,
quantity: orderRequest.quantity,
discloseQty: 0,
priceType: shoonyaPriceType,
price: orderRequest.price || 0,
triggerPrice: orderRequest.triggerPrice || 0,
retention: 'DAY',
remarks: orderRequest.remarks || `Order placed via CopyTrade Pro for account ${orderRequest.accountId || this.accountId}`
};
const response = await this.shoonyaService.placeOrder(shoonyaOrderRequest);
if (response.stat === 'Ok') {
return this.createSuccessResponse('Order placed successfully', {
orderId: response.norenordno,
message: response.result,
brokerOrderId: response.norenordno,
status: 'PLACED'
});
}
else {
return this.createErrorResponse(response.emsg || 'Order placement failed', response);
}
}
catch (error) {
// Handle session expiry with auto-retry logic
if (error.message?.includes('Session Expired') || error.message?.includes('Invalid Session Key')) {
return this.createErrorResponse('Session expired. Please reactivate your account.', {
errorType: 'SESSION_EXPIRED',
originalError: error.message
});
}
return this.createErrorResponse(error.message || 'Order placement failed', error);
}
}
async getOrderStatus(accountId, orderId) {
try {
const response = await this.shoonyaService.getOrderStatus(accountId, orderId);
return {
orderId: response.norenordno || orderId,
status: response.status || 'UNKNOWN',
quantity: parseInt(response.qty) || 0,
filledQuantity: parseInt(response.fillshares) || 0,
price: parseFloat(response.prc) || 0,
averagePrice: parseFloat(response.avgprc) || 0,
timestamp: new Date(response.norentm || Date.now())
};
}
catch (error) {
throw new Error(`Failed to get order status: ${error.message}`);
}
}
async getOrderHistory(accountId) {
try {
// Use getOrderBook for order history in Shoonya
const response = await this.shoonyaService.getOrderBook(accountId);
if (!Array.isArray(response)) {
return [];
}
return response.map((order) => ({
orderId: order.norenordno || '',
status: order.status || 'UNKNOWN',
quantity: parseInt(order.qty) || 0,
filledQuantity: parseInt(order.fillshares) || 0,
price: parseFloat(order.prc) || 0,
averagePrice: parseFloat(order.avgprc) || 0,
timestamp: new Date(order.norentm || Date.now())
}));
}
catch (error) {
throw new Error(`Failed to get order history: ${error.message}`);
}
}
async getPositions(accountId) {
try {
const response = await this.shoonyaService.getPositions(accountId);
if (!Array.isArray(response)) {
return [];
}
return response.map(position => ({
symbol: position.tsym || '',
quantity: parseInt(position.netqty) || 0,
averagePrice: parseFloat(position.netavgprc) || 0,
currentPrice: parseFloat(position.lp) || 0,
pnl: parseFloat(position.urmtom) || 0,
exchange: position.exch || '',
productType: position.prd || ''
}));
}
catch (error) {
throw new Error(`Failed to get positions: ${error.message}`);
}
}
async getQuote(symbol, exchange) {
try {
const response = await this.shoonyaService.getQuotes(exchange, symbol);
return {
symbol: response.tsym || symbol,
price: parseFloat(response.lp) || 0,
change: parseFloat(response.c) || 0,
changePercent: parseFloat(response.pc) || 0,
volume: parseInt(response.v) || 0,
exchange: response.exch || exchange,
timestamp: new Date()
};
}
catch (error) {
throw new Error(`Failed to get quote: ${error.message}`);
}
}
async searchSymbols(query, exchange) {
try {
// Shoonya doesn't have a searchSymbols method, return empty array for now
console.warn('searchSymbols not implemented for Shoonya');
return [];
}
catch (error) {
throw new Error(`Failed to search symbols: ${error.message}`);
}
}
// Shoonya-specific methods that can be accessed if needed
getShoonyaService() {
return this.shoonyaService;
}
getUserId() {
const userId = this.shoonyaService.getUserId();
return userId || undefined;
}
}
exports.ShoonyaServiceAdapter = ShoonyaServiceAdapter;
//# sourceMappingURL=ShoonyaServiceAdapter.js.map