@copytrade/broker-fyers
Version:
Fyers broker plugin for @copytrade/unified-broker
202 lines • 8.38 kB
JavaScript
;
/**
* Fyers Symbol Formatter
* Handles proper symbol formatting for Fyers API
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.FyersSymbolFormatter = void 0;
class FyersSymbolFormatter {
/**
* Format option symbol for Fyers API
* @param underlying - Underlying symbol (e.g., 'NIFTY', 'BANKNIFTY', 'RELIANCE')
* @param expiry - Expiry in YYMMMDD format (e.g., '25JAN30', '25JAN')
* @param strike - Strike price (e.g., 22000, 3000)
* @param optionType - 'CE' for Call, 'PE' for Put
* @param exchange - Exchange (default: 'NSE')
* @returns Formatted symbol (e.g., 'NSE:NIFTY25JAN22000CE')
*/
static formatOption(underlying, expiry, strike, optionType, exchange = 'NSE') {
return `${exchange}:${underlying}${expiry}${strike}${optionType}`;
}
/**
* Format future symbol for Fyers API
* @param underlying - Underlying symbol (e.g., 'NIFTY', 'BANKNIFTY', 'RELIANCE')
* @param expiry - Expiry in YYMMMDD format (e.g., '25JAN30', '25JAN')
* @param exchange - Exchange (default: 'NSE')
* @returns Formatted symbol (e.g., 'NSE:NIFTY25JANFUT')
*/
static formatFuture(underlying, expiry, exchange = 'NSE') {
return `${exchange}:${underlying}${expiry}FUT`;
}
/**
* Format equity symbol for Fyers API
* @param symbol - Stock symbol (e.g., 'RELIANCE', 'TCS')
* @param exchange - Exchange (default: 'NSE')
* @returns Formatted symbol (e.g., 'NSE:RELIANCE-EQ')
*/
static formatEquity(symbol, exchange = 'NSE') {
return `${exchange}:${symbol}-EQ`;
}
/**
* Parse symbol to extract components
* Attempts to detect instrument type and extract components
* @param symbol - Input symbol
* @returns Symbol components or null if parsing fails
*/
static parseSymbol(symbol) {
try {
// Remove exchange prefix if present (e.g., 'NSE:NIFTY25JAN22000CE' -> 'NIFTY25JAN22000CE')
const cleanSymbol = symbol.includes(':') ? symbol.split(':')[1] : symbol;
if (!cleanSymbol) {
return null;
}
// Check for equity format (ends with -EQ)
if (cleanSymbol.endsWith('-EQ')) {
return {
underlying: cleanSymbol.replace('-EQ', ''),
expiry: '',
instrumentType: 'EQUITY'
};
}
// Check for futures format (ends with FUT)
if (cleanSymbol.endsWith('FUT')) {
const withoutFut = cleanSymbol.replace('FUT', '');
const match = withoutFut.match(/^([A-Z]+)(\d{2}[A-Z]{3})$/);
if (match && match[1] && match[2]) {
return {
underlying: match[1],
expiry: match[2],
instrumentType: 'FUTURE'
};
}
}
// Check for options format (ends with CE or PE)
// Pattern: UNDERLYING + EXPIRY + STRIKE + OPTION_TYPE
// Example: NIFTY25JAN22000CE -> NIFTY + 25JAN + 22000 + CE
const optionMatch = cleanSymbol.match(/^([A-Z]+)(\d{2}[A-Z]{3})(\d+)(CE|PE)$/);
if (optionMatch && optionMatch[1] && optionMatch[2] && optionMatch[3] && optionMatch[4]) {
return {
underlying: optionMatch[1],
expiry: optionMatch[2],
strike: parseInt(optionMatch[3]),
optionType: optionMatch[4],
instrumentType: 'OPTION'
};
}
return null;
}
catch (error) {
console.error('Error parsing symbol:', error);
return null;
}
}
/**
* Format symbol based on detected or provided instrument type
* @param symbol - Input symbol
* @param exchange - Exchange (default: 'NSE')
* @param instrumentType - Override instrument type detection
* @returns Formatted symbol for Fyers API
*/
static formatSymbol(symbol, exchange = 'NSE', instrumentType) {
// If symbol already has exchange prefix, return as-is
if (symbol.includes(':')) {
return symbol;
}
// Parse symbol to detect components
const components = this.parseSymbol(symbol);
if (!components) {
// Fallback: assume equity if parsing fails
console.warn(`Failed to parse symbol ${symbol}, assuming equity format`);
return this.formatEquity(symbol, exchange);
}
// Use provided instrument type or detected type
const finalInstrumentType = instrumentType || components.instrumentType;
switch (finalInstrumentType) {
case 'OPTION':
if (!components.strike || !components.optionType) {
throw new Error(`Invalid option symbol: ${symbol} - missing strike or option type`);
}
return this.formatOption(components.underlying, components.expiry, components.strike, components.optionType, exchange);
case 'FUTURE':
return this.formatFuture(components.underlying, components.expiry, exchange);
case 'EQUITY':
default:
return this.formatEquity(components.underlying, exchange);
}
}
/**
* Validate if symbol is properly formatted for Fyers API
* @param symbol - Symbol to validate
* @returns true if valid, false otherwise
*/
static isValidFyersSymbol(symbol) {
// Must have exchange prefix
if (!symbol.includes(':')) {
return false;
}
const [exchange, tradingSymbol] = symbol.split(':');
// Valid exchanges
const validExchanges = ['NSE', 'BSE', 'MCX', 'CDS'];
if (!validExchanges.includes(exchange)) {
return false;
}
if (!tradingSymbol) {
return false;
}
// Check trading symbol format
if (tradingSymbol.endsWith('-EQ')) {
// Equity format
return tradingSymbol.length > 3;
}
else if (tradingSymbol.endsWith('FUT')) {
// Future format
const match = tradingSymbol.match(/^[A-Z]+\d{2}[A-Z]{3}FUT$/);
return !!match;
}
else if (tradingSymbol.endsWith('CE') || tradingSymbol.endsWith('PE')) {
// Option format
const match = tradingSymbol.match(/^[A-Z]+\d{2}[A-Z]{3}\d+(CE|PE)$/);
return !!match;
}
return false;
}
/**
* Get exchange from symbol or default
* @param symbol - Input symbol
* @param defaultExchange - Default exchange if not found in symbol
* @returns Exchange code
*/
static getExchange(symbol, defaultExchange = 'NSE') {
if (symbol.includes(':')) {
const exchange = symbol.split(':')[0];
return exchange || defaultExchange;
}
return defaultExchange;
}
/**
* Convert expiry date to Fyers format
* @param expiryDate - Date in various formats (ISO, DD-MM-YYYY, etc.)
* @returns Expiry in YYMMMDD format
*/
static formatExpiryDate(expiryDate) {
try {
const date = typeof expiryDate === 'string' ? new Date(expiryDate) : expiryDate;
const year = date.getFullYear().toString().slice(-2); // Last 2 digits
const monthNames = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN',
'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC'];
const month = monthNames[date.getMonth()];
const day = date.getDate().toString().padStart(2, '0');
// For monthly expiry (last Thursday), just return YYMM
// For weekly expiry, return YYMMMDD
// This is a simplified approach - in practice, you'd need to determine
// if it's a monthly or weekly expiry based on the date
return `${year}${month}${day}`;
}
catch (error) {
console.error('Error formatting expiry date:', error);
throw new Error(`Invalid expiry date format: ${expiryDate}`);
}
}
}
exports.FyersSymbolFormatter = FyersSymbolFormatter;
//# sourceMappingURL=symbolFormatter.js.map