UNPKG

smartapi-typescript

Version:

TypeScript library for Angel One SmartAPI broker API

677 lines 29.7 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MarketData = void 0; const apiUrls_1 = require("../../constants/apiUrls"); const types_1 = require("../../types"); const http = __importStar(require("../../utils/http")); /** * Miscellaneous market data module for SmartAPI * Handles data operations like historical candles, LTP, quotes, etc. */ class MarketData { /** * Initialize market data module */ constructor(auth, httpClient, debug = false) { this.auth = auth; this.httpClient = httpClient; this.debug = debug; } /** * Log debug messages if debug mode is enabled */ log(message, data) { if (this.debug) { console.log(`[SmartAPI:MarketData] ${message}`); if (data) { console.log(data); } } } /** * Get market quotes using the new Live Market Data API * Supports three modes: LTP, OHLC, FULL * Supports up to 50 symbols in a single request * * @param request Market quote request with mode and exchangeTokens * @param options Network configuration options * @returns Market quote data */ getMarketQuote(request, options) { return __awaiter(this, void 0, void 0, function* () { if (!this.auth.isAuthenticated()) { return { status: false, message: 'Not authenticated. Please login first.' }; } // Validate request if (!request.mode || !Object.values(types_1.MarketDataMode).includes(request.mode)) { return { status: false, message: 'Invalid mode. Must be one of LTP, OHLC, FULL.' }; } if (!request.exchangeTokens || Object.keys(request.exchangeTokens).length === 0) { return { status: false, message: 'exchangeTokens cannot be empty' }; } // Check if total symbols across all exchanges exceeds the limit (50) const totalSymbols = Object.values(request.exchangeTokens) .reduce((sum, tokens) => sum + tokens.length, 0); if (totalSymbols > 50) { return { status: false, message: 'Maximum 50 symbols allowed in a single request' }; } this.log('Fetching market quote data', request); try { return yield http.post(`${apiUrls_1.API_URLS.BASE_URL}${apiUrls_1.API_URLS.MARKET_QUOTE}`, request, this.auth.getHeaders(options)); } catch (error) { this.log('Get market quote failed', error); const retryOperation = () => this.getMarketQuote(request, options); return this.auth.handleApiError(error, retryOperation); } }); } /** * Get LTP data using the new Live Market Data API * Convenience method that uses the getMarketQuote with LTP mode * * @param exchangeTokenMap Map of exchange to tokens (e.g., {"NSE": ["3045"], "BSE": ["500112"]}) * @param options Network configuration options * @returns LTP data */ getLTPData(exchangeTokenMap, options) { return __awaiter(this, void 0, void 0, function* () { const request = { mode: types_1.MarketDataMode.LTP, exchangeTokens: exchangeTokenMap }; return this.getMarketQuote(request, options); }); } /** * Get OHLC data using the new Live Market Data API * Convenience method that uses the getMarketQuote with OHLC mode * * @param exchangeTokenMap Map of exchange to tokens (e.g., {"NSE": ["3045"], "BSE": ["500112"]}) * @param options Network configuration options * @returns OHLC data */ getOHLCData(exchangeTokenMap, options) { return __awaiter(this, void 0, void 0, function* () { const request = { mode: types_1.MarketDataMode.OHLC, exchangeTokens: exchangeTokenMap }; return this.getMarketQuote(request, options); }); } /** * Get full market data using the new Live Market Data API * Convenience method that uses the getMarketQuote with FULL mode * * @param exchangeTokenMap Map of exchange to tokens (e.g., {"NSE": ["3045"], "BSE": ["500112"]}) * @param options Network configuration options * @returns Full market data including depth */ getFullQuote(exchangeTokenMap, options) { return __awaiter(this, void 0, void 0, function* () { const request = { mode: types_1.MarketDataMode.FULL, exchangeTokens: exchangeTokenMap }; return this.getMarketQuote(request, options); }); } /** * Get last traded price for a symbol (Legacy method) * @deprecated Use getLTPData() instead which supports multiple symbols * @param exchange Exchange name (e.g., NSE, BSE) * @param symbolToken Symbol token * @param tradingSymbol Trading symbol * @param options Network configuration options * @returns LTP data */ getLTP(exchange, symbolToken, tradingSymbol, options) { return __awaiter(this, void 0, void 0, function* () { if (!this.auth.isAuthenticated()) { return { status: false, message: 'Not authenticated. Please login first.' }; } const params = { exchange, symboltoken: symbolToken, tradingsymbol: tradingSymbol }; this.log('Fetching LTP (Legacy)', params); try { return yield http.post(`${apiUrls_1.API_URLS.BASE_URL}${apiUrls_1.API_URLS.LTP_DATA}`, params, this.auth.getHeaders(options)); } catch (error) { this.log('Get LTP failed', error); const retryOperation = () => this.getLTP(exchange, symbolToken, tradingSymbol, options); return this.auth.handleApiError(error, retryOperation); } }); } /** * Get last traded price for multiple symbols (Legacy method) * @deprecated Use getLTPData() instead which has better batching support * @param instruments Array of instruments (exchange, symboltoken, tradingsymbol) * @param options Network configuration options * @returns LTP data for multiple symbols */ getMultiLTP(instruments, options) { return __awaiter(this, void 0, void 0, function* () { if (!this.auth.isAuthenticated()) { return { status: false, message: 'Not authenticated. Please login first.' }; } this.log('Fetching multiple LTPs (Legacy)', instruments); try { return yield http.post(`${apiUrls_1.API_URLS.BASE_URL}/rest/secure/angelbroking/market/v1/getMultiLTP`, { instruments }, this.auth.getHeaders(options)); } catch (error) { this.log('Get multiple LTPs failed', error); const retryOperation = () => this.getMultiLTP(instruments, options); return this.auth.handleApiError(error, retryOperation); } }); } /** * Get comprehensive quote for symbols (Legacy method) * @deprecated Use getFullQuote() instead which supports multiple symbols * @param exchange Exchange name (e.g., NSE, BSE) * @param symbolToken Symbol token * @param tradingSymbol Trading symbol * @param options Network configuration options * @returns Quote data */ getQuote(exchange, symbolToken, tradingSymbol, options) { return __awaiter(this, void 0, void 0, function* () { if (!this.auth.isAuthenticated()) { return { status: false, message: 'Not authenticated. Please login first.' }; } const params = { exchange, symboltoken: symbolToken, tradingsymbol: tradingSymbol }; this.log('Fetching quote (Legacy)', params); try { return yield http.post(`${apiUrls_1.API_URLS.BASE_URL}${apiUrls_1.API_URLS.QUOTE}`, params, this.auth.getHeaders(options)); } catch (error) { this.log('Get quote failed', error); const retryOperation = () => this.getQuote(exchange, symbolToken, tradingSymbol, options); return this.auth.handleApiError(error, retryOperation); } }); } /** * Get market quotes for multiple symbols (Legacy method) * @deprecated Use getFullQuote() instead which has better batching support * @param instruments Array of instruments (exchange, token) * @param options Network configuration options * @returns Market quotes for multiple symbols */ getMultiQuotes(instruments, options) { return __awaiter(this, void 0, void 0, function* () { if (!this.auth.isAuthenticated()) { return { status: false, message: 'Not authenticated. Please login first.' }; } this.log('Fetching multiple quotes (Legacy)', instruments); try { return yield http.post(`${apiUrls_1.API_URLS.BASE_URL}/rest/secure/angelbroking/market/v1/getMultiQuotes`, { instruments }, this.auth.getHeaders(options)); } catch (error) { this.log('Get multiple quotes failed', error); const retryOperation = () => this.getMultiQuotes(instruments, options); return this.auth.handleApiError(error, retryOperation); } }); } /** * Get historical candle data * @param params Historical data parameters * @param options Network configuration options * @returns Historical candle data */ getHistoricalData(params, options) { return __awaiter(this, void 0, void 0, function* () { if (!this.auth.isAuthenticated()) { return { status: false, message: 'Not authenticated. Please login first.' }; } this.log('Fetching historical data', params); try { return yield http.post(`${apiUrls_1.API_URLS.BASE_URL}${apiUrls_1.API_URLS.HISTORICAL_CANDLES}`, params, this.auth.getHeaders(options)); } catch (error) { this.log('Get historical data failed', error); const retryOperation = () => this.getHistoricalData(params, options); return this.auth.handleApiError(error, retryOperation); } }); } /** * Get historical candle data with automatic pagination * Handles large date ranges by splitting into smaller chunks * @param params Historical data parameters * @param maxCandlesPerRequest Maximum number of candles per request (default: 2000) * @param options Network configuration options * @returns Consolidated historical candle data */ getHistoricalDataPaginated(params_1) { return __awaiter(this, arguments, void 0, function* (params, maxCandlesPerRequest = 2000, options) { if (!this.auth.isAuthenticated()) { return { status: false, message: 'Not authenticated. Please login first.' }; } // Parse input dates const fromDate = new Date(params.fromdate); const toDate = new Date(params.todate); // Calculate appropriate chunk size based on interval let chunkSizeMs = 0; switch (params.interval) { case types_1.HistoricalInterval.ONE_MINUTE: chunkSizeMs = maxCandlesPerRequest * 60 * 1000; // 1 minute in ms break; case types_1.HistoricalInterval.FIVE_MINUTE: chunkSizeMs = maxCandlesPerRequest * 5 * 60 * 1000; break; case types_1.HistoricalInterval.FIFTEEN_MINUTE: chunkSizeMs = maxCandlesPerRequest * 15 * 60 * 1000; break; case types_1.HistoricalInterval.THIRTY_MINUTE: chunkSizeMs = maxCandlesPerRequest * 30 * 60 * 1000; break; case types_1.HistoricalInterval.ONE_HOUR: chunkSizeMs = maxCandlesPerRequest * 60 * 60 * 1000; break; case types_1.HistoricalInterval.ONE_DAY: chunkSizeMs = maxCandlesPerRequest * 24 * 60 * 60 * 1000; break; default: chunkSizeMs = 30 * 24 * 60 * 60 * 1000; // 30 days for larger intervals } // Create chunks of date ranges const dateRanges = []; let currentStart = new Date(fromDate); while (currentStart < toDate) { const chunkEnd = new Date(currentStart.getTime() + chunkSizeMs); const actualEnd = chunkEnd < toDate ? chunkEnd : toDate; dateRanges.push({ start: new Date(currentStart), end: new Date(actualEnd) }); currentStart = new Date(actualEnd.getTime() + 1); // Start next chunk 1ms after end of current } // Format date as YYYY-MM-DD HH:mm const formatDate = (date) => { return date.toISOString().replace('T', ' ').substring(0, 16); }; try { // Fetch data for each chunk const allResults = []; for (const range of dateRanges) { const chunkParams = Object.assign(Object.assign({}, params), { fromdate: formatDate(range.start), todate: formatDate(range.end) }); this.log('Fetching historical data chunk', chunkParams); const response = yield this.getHistoricalData(chunkParams, options); if (response.status && response.data) { allResults.push(...response.data); } else { // Return error if any chunk fails return response; } } // Return consolidated results return { status: true, message: 'Historical data retrieved successfully', data: allResults }; } catch (error) { this.log('Paginated historical data request failed', error); return http.handleApiError(error); } }); } /** * Get historical Open Interest data for F&O contracts * Provides historical OI data for live F&O contracts * * @param params Historical data parameters (same as for getHistoricalData) * @param options Network configuration options * @returns Historical OI data */ getHistoricalOIData(params, options) { return __awaiter(this, void 0, void 0, function* () { if (!this.auth.isAuthenticated()) { return { status: false, message: 'Not authenticated. Please login first.' }; } // Validate input parameters if (params.exchange !== 'NFO' && params.exchange !== 'BFO') { return { status: false, message: 'OI data is only available for derivatives segments (NFO, BFO)' }; } this.log('Fetching historical OI data', params); try { return yield http.post(`${apiUrls_1.API_URLS.BASE_URL}${apiUrls_1.API_URLS.HISTORICAL_OI}`, params, this.auth.getHeaders(options)); } catch (error) { this.log('Get historical OI data failed', error); const retryOperation = () => this.getHistoricalOIData(params, options); return this.auth.handleApiError(error, retryOperation); } }); } /** * Get historical Open Interest data with automatic pagination * Handles large date ranges by splitting into smaller chunks * * @param params Historical data parameters * @param maxEntriesPerRequest Maximum number of entries per request based on interval * @param options Network configuration options * @returns Consolidated historical OI data */ getHistoricalOIDataPaginated(params, maxEntriesPerRequest, options) { return __awaiter(this, void 0, void 0, function* () { if (!this.auth.isAuthenticated()) { return { status: false, message: 'Not authenticated. Please login first.' }; } // Determine max entries based on interval if not provided if (maxEntriesPerRequest === undefined) { switch (params.interval) { case types_1.HistoricalInterval.ONE_MINUTE: maxEntriesPerRequest = 30 * 375; // 30 days of market hours break; case types_1.HistoricalInterval.THREE_MINUTE: maxEntriesPerRequest = 60 * 125; // 60 days of market hours break; case types_1.HistoricalInterval.FIVE_MINUTE: maxEntriesPerRequest = 100 * 75; // 100 days of market hours break; case types_1.HistoricalInterval.TEN_MINUTE: maxEntriesPerRequest = 100 * 38; // 100 days of market hours break; case types_1.HistoricalInterval.FIFTEEN_MINUTE: maxEntriesPerRequest = 200 * 25; // 200 days of market hours break; case types_1.HistoricalInterval.THIRTY_MINUTE: maxEntriesPerRequest = 200 * 13; // 200 days of market hours break; case types_1.HistoricalInterval.ONE_HOUR: maxEntriesPerRequest = 400 * 7; // 400 days of market hours break; case types_1.HistoricalInterval.ONE_DAY: maxEntriesPerRequest = 2000; // 2000 days per API doc break; default: maxEntriesPerRequest = 1000; // Default value } } // Parse input dates const fromDate = new Date(params.fromdate); const toDate = new Date(params.todate); // Calculate appropriate chunk size based on interval let chunkSizeMs = 0; switch (params.interval) { case types_1.HistoricalInterval.ONE_MINUTE: chunkSizeMs = maxEntriesPerRequest * 60 * 1000; // 1 minute in ms break; case types_1.HistoricalInterval.THREE_MINUTE: chunkSizeMs = maxEntriesPerRequest * 3 * 60 * 1000; break; case types_1.HistoricalInterval.FIVE_MINUTE: chunkSizeMs = maxEntriesPerRequest * 5 * 60 * 1000; break; case types_1.HistoricalInterval.TEN_MINUTE: chunkSizeMs = maxEntriesPerRequest * 10 * 60 * 1000; break; case types_1.HistoricalInterval.FIFTEEN_MINUTE: chunkSizeMs = maxEntriesPerRequest * 15 * 60 * 1000; break; case types_1.HistoricalInterval.THIRTY_MINUTE: chunkSizeMs = maxEntriesPerRequest * 30 * 60 * 1000; break; case types_1.HistoricalInterval.ONE_HOUR: chunkSizeMs = maxEntriesPerRequest * 60 * 60 * 1000; break; case types_1.HistoricalInterval.ONE_DAY: chunkSizeMs = maxEntriesPerRequest * 24 * 60 * 60 * 1000; break; default: chunkSizeMs = 30 * 24 * 60 * 60 * 1000; // 30 days for larger intervals } // Create chunks of date ranges const dateRanges = []; let currentStart = new Date(fromDate); while (currentStart < toDate) { const chunkEnd = new Date(currentStart.getTime() + chunkSizeMs); const actualEnd = chunkEnd < toDate ? chunkEnd : toDate; dateRanges.push({ start: new Date(currentStart), end: new Date(actualEnd) }); currentStart = new Date(actualEnd.getTime() + 1); // Start next chunk 1ms after end of current } // Format date as YYYY-MM-DD HH:mm const formatDate = (date) => { return date.toISOString().replace('T', ' ').substring(0, 16); }; try { // Fetch data for each chunk const allResults = []; for (const range of dateRanges) { const chunkParams = Object.assign(Object.assign({}, params), { fromdate: formatDate(range.start), todate: formatDate(range.end) }); this.log('Fetching historical OI data chunk', chunkParams); const response = yield this.getHistoricalOIData(chunkParams, options); if (response.status && response.data) { allResults.push(...response.data); } else { // Return error if any chunk fails return response; } } // Return consolidated results return { status: true, message: 'Historical OI data retrieved successfully', data: allResults }; } catch (error) { this.log('Paginated historical OI data request failed', error); return http.handleApiError(error); } }); } /** * Set up webhook configuration for real-time order updates * * Note: The actual webhook URL must be registered when creating your API key in the Angel One dashboard. * This method provides information about webhook behavior and requirements. * * @returns Object with webhook information and requirements */ getWebhookInfo() { // This only provides information about webhook settings // The webhook URL must be configured in the Angel One dashboard return { status: true, message: "Webhook information retrieved", data: { url: null, } }; } /** * Parse webhook data received from Angel One postback * This is meant to be used in your webhook endpoint implementation * * @param data Raw webhook payload as received from Angel One * @returns Parsed PostbackData object */ static parseWebhookData(data) { try { if (typeof data === 'string') { return JSON.parse(data); } return data; } catch (error) { throw new Error('Invalid webhook data format'); } } /** * Get Option Greeks (Delta, Gamma, Theta, Vega) and Implied Volatility * for specified underlying and expiry date * * @param params Parameters containing the underlying name and expiry date * @param options Network configuration options * @returns Option greeks data for multiple strike prices */ getOptionGreeks(params, options) { return __awaiter(this, void 0, void 0, function* () { if (!this.auth.isAuthenticated()) { return { status: false, message: 'Not authenticated. Please login first.' }; } // Validate request parameters if (!params.name || !params.expirydate) { return { status: false, message: 'Both name (underlying) and expirydate are required' }; } this.log('Fetching option greeks data', params); try { return yield http.post(`${apiUrls_1.API_URLS.BASE_URL}${apiUrls_1.API_URLS.OPTION_GREEKS}`, params, this.auth.getHeaders(options)); } catch (error) { this.log('Get option greeks failed', error); const retryOperation = () => this.getOptionGreeks(params, options); return this.auth.handleApiError(error, retryOperation); } }); } /** * Get Top Gainers/Losers in derivatives segment * Provides data about top gainers and losers in derivatives segment based on price change or open interest. * * @param params Parameters containing datatype and expirytype * @param options Network configuration options * @returns Top gainers or losers data based on the specified parameters */ getGainersLosers(params, options) { return __awaiter(this, void 0, void 0, function* () { if (!this.auth.isAuthenticated()) { return { status: false, message: 'Not authenticated. Please login first.' }; } // Validate datatype parameter if (!params.datatype || !Object.values(types_1.GainersLosersDataType).includes(params.datatype)) { return { status: false, message: 'Invalid datatype. Must be one of PercPriceGainers, PercPriceLosers, PercOIGainers, or PercOILosers.' }; } // Validate expirytype parameter if (!params.expirytype || !Object.values(types_1.ExpiryType).includes(params.expirytype)) { return { status: false, message: 'Invalid expirytype. Must be one of NEAR, NEXT, or FAR.' }; } this.log('Fetching gainers/losers data', params); try { return yield http.post(`${apiUrls_1.API_URLS.BASE_URL}${apiUrls_1.API_URLS.GAINERS_LOSERS}`, params, this.auth.getHeaders(options)); } catch (error) { this.log('Get gainers/losers data failed', error); const retryOperation = () => this.getGainersLosers(params, options); return this.auth.handleApiError(error, retryOperation); } }); } } exports.MarketData = MarketData; //# sourceMappingURL=index.js.map