@copytrade/broker-fyers
Version:
Fyers broker plugin for @copytrade/unified-broker
324 lines • 12.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FyersService = void 0;
const { fyersModel } = require('fyers-api-v3');
class FyersService {
constructor() {
this.accessToken = null;
this.refreshToken = null;
this.appId = '';
this.secretKey = '';
// Initialize the official Fyers API client
this.fyers = new fyersModel({
path: process.cwd() + '/logs',
enableLogging: true
});
}
// Generate auth URL for user to visit - Fixed to use actual login page
generateAuthUrl(credentials) {
this.appId = credentials.clientId;
this.fyers.setAppId(credentials.clientId);
this.fyers.setRedirectUrl(credentials.redirectUri);
// Use the correct Fyers login URL instead of API endpoint
const authUrl = `https://api-t1.fyers.in/api/v3/generate-authcode?client_id=${credentials.clientId}&redirect_uri=${encodeURIComponent(credentials.redirectUri)}&response_type=code&state=sample_state`;
console.log('🔗 Fixed Auth URL generated:', authUrl);
return authUrl;
}
// Generate access token from auth code
async generateAccessToken(authCode, credentials) {
try {
// Set App ID and secret key before generating access token
this.fyers.setAppId(credentials.clientId);
this.appId = credentials.clientId;
this.secretKey = credentials.secretKey;
const response = await this.fyers.generate_access_token({
client_id: credentials.clientId,
secret_key: credentials.secretKey,
auth_code: authCode
});
if (response.s === 'ok') {
this.accessToken = response.access_token;
this.refreshToken = response.refresh_token; // Store refresh token
this.fyers.setAccessToken(response.access_token);
console.log('✅ Fyers access token and refresh token generated successfully');
// Get user profile to extract actual account ID
try {
const profileResponse = await this.fyers.get_profile();
if (profileResponse.s === 'ok' && profileResponse.data) {
const accountId = profileResponse.data.fy_id || profileResponse.data.id || profileResponse.data.user_id;
console.log(`✅ Fyers profile fetched, account ID: ${accountId}`);
return {
success: true,
accessToken: response.access_token,
refreshToken: response.refresh_token,
accountId: accountId, // Return actual Fyers account ID
message: 'Access token generated successfully',
};
}
else {
console.log('⚠️ Profile fetch failed, using client ID as fallback');
}
}
catch (profileError) {
console.log('⚠️ Profile fetch error:', profileError.message);
}
// Fallback: return without account ID
return {
success: true,
accessToken: response.access_token,
refreshToken: response.refresh_token,
message: 'Access token generated successfully',
};
}
else {
throw new Error(response.message || 'Failed to generate access token');
}
}
catch (error) {
console.error('🚨 Failed to generate access token:', error);
return {
success: false,
message: error.message || 'Access token generation failed',
};
}
}
// Complete login flow - returns auth URL for user to visit
async login(credentials) {
try {
const authUrl = this.generateAuthUrl(credentials);
// For OAuth flows, return success: false with authUrl
// This indicates that authentication is required
return {
success: false,
authUrl,
message: 'OAuth authentication required',
};
}
catch (error) {
console.error('🚨 Fyers login failed:', error);
return {
success: false,
message: error.message || 'Login failed',
};
}
}
// Place order using official API
async placeOrder(orderData) {
if (!this.accessToken) {
throw new Error('Not authenticated. Please login first.');
}
try {
const payload = {
symbol: orderData.symbol,
qty: orderData.qty,
type: orderData.type === 'MARKET' ? 2 : 1, // 1=LIMIT, 2=MARKET
side: orderData.side === 'BUY' ? 1 : -1, // 1=BUY, -1=SELL
productType: this.getProductTypeCode(orderData.productType), // Fyers API expects camelCase
limitPrice: orderData.limitPrice || 0, // Fyers API expects camelCase
stopPrice: orderData.stopPrice || 0, // Fyers API expects camelCase
disclosedQty: orderData.disclosedQty || 0, // Fyers API expects camelCase
validity: orderData.validity === 'DAY' ? 'DAY' : 'IOC',
offlineOrder: orderData.offlineOrder || false, // Fyers API expects camelCase
stopLoss: orderData.stopLoss || 0, // Fyers API expects camelCase
takeProfit: orderData.takeProfit || 0, // Fyers API expects camelCase
};
const response = await this.fyers.place_order(payload);
console.log('✅ Order placed successfully:', response);
return response;
}
catch (error) {
console.error('🚨 Failed to place order:', error);
throw new Error(error.message || 'Order placement failed');
}
}
// Get order book using official API
async getOrderBook() {
if (!this.accessToken) {
throw new Error('Not authenticated. Please login first.');
}
try {
const response = await this.fyers.orderbook();
return response.orderBook || [];
}
catch (error) {
console.error('🚨 Failed to get order book:', error);
throw new Error(error.message || 'Failed to get order book');
}
}
// Get positions using official API
async getPositions() {
if (!this.accessToken) {
throw new Error('Not authenticated. Please login first.');
}
try {
const response = await this.fyers.get_positions();
return response.netPositions || [];
}
catch (error) {
console.error('🚨 Failed to get positions:', error);
throw new Error(error.message || 'Failed to get positions');
}
}
// Search symbols using official API
async searchScrip(exchange, symbol) {
try {
const response = await this.fyers.search_scrips({
symbol: `${exchange}:${symbol}`
});
return response.symbols || [];
}
catch (error) {
console.error('🚨 Failed to search symbols:', error);
throw new Error(error.message || 'Symbol search failed');
}
}
// Get quotes using official API
async getQuotes(symbols) {
if (!this.accessToken) {
throw new Error('Not authenticated. Please login first.');
}
try {
const response = await this.fyers.getQuotes(symbols);
return response.d || [];
}
catch (error) {
console.error('🚨 Failed to get quotes:', error);
throw new Error(error.message || 'Failed to get quotes');
}
}
// Get profile using official API
async getProfile() {
if (!this.accessToken) {
throw new Error('Not authenticated. Please login first.');
}
try {
const response = await this.fyers.get_profile();
return response;
}
catch (error) {
console.error('🚨 Failed to get profile:', error);
throw new Error(error.message || 'Failed to get profile');
}
}
// Helper method to convert product type to code
getProductTypeCode(productType) {
const productMap = {
'CNC': 'CNC',
'INTRADAY': 'INTRADAY',
'MARGIN': 'MARGIN',
'CO': 'CO',
'BO': 'BO',
};
return productMap[productType] || 'CNC';
}
// Check if authenticated
isAuthenticated() {
return !!this.accessToken;
}
// Get access token
getAccessToken() {
return this.accessToken;
}
// Set access token (for existing sessions)
setAccessToken(token) {
this.accessToken = token;
this.fyers.setAccessToken(token);
}
// Set refresh token
setRefreshToken(token) {
this.refreshToken = token;
}
// Get refresh token
getRefreshToken() {
return this.refreshToken;
}
// Refresh access token using refresh token
async refreshAccessToken() {
if (!this.refreshToken) {
return {
success: false,
message: 'No refresh token available',
};
}
if (!this.appId || !this.secretKey) {
return {
success: false,
message: 'App ID or Secret Key not set',
};
}
try {
console.log('🔄 Refreshing Fyers access token using refresh token...');
const response = await this.fyers.refresh_access_token({
client_id: this.appId,
secret_key: this.secretKey,
refresh_token: this.refreshToken
});
if (response.s === 'ok') {
this.accessToken = response.access_token;
this.refreshToken = response.refresh_token; // Update refresh token if provided
this.fyers.setAccessToken(response.access_token);
console.log('✅ Fyers access token refreshed successfully');
return {
success: true,
accessToken: response.access_token,
refreshToken: response.refresh_token,
message: 'Access token refreshed successfully',
};
}
else {
console.error('❌ Failed to refresh access token:', response.message);
return {
success: false,
message: response.message || 'Failed to refresh access token',
};
}
}
catch (error) {
console.error('🚨 Failed to refresh access token:', error);
return {
success: false,
message: error.message || 'Access token refresh failed',
};
}
}
// Validate if the current session is still active
async validateSession() {
if (!this.accessToken) {
return false;
}
try {
// Use a lightweight API call to check if session is still valid
// getProfile is a simple endpoint that requires authentication
const response = await this.fyers.get_profile();
// If the call succeeds, session is valid
return response.s === 'ok';
}
catch (error) {
console.log('⚠️ Session validation failed for Fyers:', error.message);
// If API call fails, session is likely expired
this.accessToken = null;
return false;
}
}
// Logout
async logout() {
try {
this.accessToken = null;
this.appId = '';
console.log('✅ Fyers logout successful');
return {
success: true,
message: 'Logout successful',
};
}
catch (error) {
console.error('🚨 Fyers logout failed:', error);
return {
success: false,
message: error.message || 'Logout failed',
};
}
}
}
exports.FyersService = FyersService;
//# sourceMappingURL=fyersService.js.map