@zero65tech/indian-stock-market
Version:
Indian Stock Market Toolkit
224 lines (182 loc) • 5.71 kB
JavaScript
import HOLIDAYS from "./build/holidays.json";
import SPECIAL_DAYS from "./build/special-days.json";
const IST_OFFSET_MILLIS = 5.5 * 60 * 60 * 1000;
const MAHURAT_DAY = "2026-11-08";
function isHoliday(date = new Date()) {
// Normalize input date
if (typeof date === "string") {
date = new Date(date);
} else if (date instanceof Date) {
// Apply IST offset
date = new Date(date.getTime() + IST_OFFSET_MILLIS);
} else {
throw new TypeError("Invalid date input");
}
let yyyy = date.getUTCFullYear();
let mm = date.getUTCMonth() + 1;
let dd = date.getUTCDate();
// Check special days first
if (SPECIAL_DAYS[yyyy]?.[mm]?.includes(dd)) {
return false;
}
// Check if weekend (Saturday=6, Sunday=0)
const dayOfWeek = date.getUTCDay();
if (dayOfWeek === 0 || dayOfWeek === 6) {
return true;
}
// Check if holiday
return HOLIDAYS[yyyy]?.[mm]?.includes(dd) ?? false;
}
function isOpen(date = new Date()) {
if (isHoliday(date)) {
return false;
}
// Apply IST offset
date = new Date(date.getTime() + IST_OFFSET_MILLIS);
const dateStr = `${date.getUTCFullYear()}-${String(date.getUTCMonth() + 1).padStart(2, "0")}-${String(date.getUTCDate()).padStart(2, "0")}`;
const hours = date.getUTCHours() + date.getUTCMinutes() / 60 + date.getUTCSeconds() / 3600;
if (dateStr === MAHURAT_DAY) {
return hours >= 18 && hours < 19.25;
} else {
return hours >= 9 && hours < 15.5;
}
}
// TODO
const specialday = new Date(MAHURAT_DAY).getTime() / 1000 / 60 / 60 / 24; // UTC
function _istDayAndHr(date) {
let hrs = date.getTime() / 1000 / 60 / 60 + 5.5;
return [Math.floor(hrs / 24), hrs % 24];
}
function eq(name) {
const match = name.match(/^(.*?)-([A-Z]{1,2})$/);
return match
? { symbol: match[1], scrip: match[1], series: match[2] }
: { symbol: name, scrip: name, series: null };
}
function monthlyExpiry(yy, mon, weekday) {
const yyyy = 2000 + parseInt(yy);
const mm = [
"JAN",
"FEB",
"MAR",
"APR",
"MAY",
"JUN",
"JUL",
"AUG",
"SEP",
"OCT",
"NOV",
"DEC",
].indexOf(mon);
let dd = new Date(yyyy, mm + 1, 0).getDate(); // Last day of the month
while (new Date(yyyy, mm, dd).getDay() != weekday) dd--;
for (; dd >= 1; dd--) {
const date = `${yyyy}-${String(mm + 1).padStart(2, "0")}-${String(dd).padStart(2, "0")}`;
if (
HOLIDAYS[yyyy] === undefined ||
HOLIDAYS[yyyy][mm + 1] === undefined ||
!HOLIDAYS[yyyy][mm + 1].includes(dd)
)
return date;
}
}
function weeklyExpiry(yy, m, dd) {
const year = "20" + yy;
const mm = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "O", "N", "D"].indexOf(m);
return `${year}-${String(mm + 1).padStart(2, "0")}-${dd}`;
}
function info(symbol) {
console.warn(".info() is deprecated. Use .fo() instead.");
// FUT - Monthly Expiry (only)
let match = symbol.match(/^(\S+?)(\d{2})([A-Z]{3})FUT$/);
if (match) {
let script = match[1];
let expiry = monthlyExpiry(match[2], match[3], script == "FINNIFTY" ? 2 : 4);
return { script, exp: match[2] + match[3], expiry, type: "FUT" };
}
// OPT - Monthly Expiry
match = symbol.match(/^(\S+?)(\d{2})([A-Z]{3})([\d.]+)(PE|CE)$/);
if (match) {
let script = match[1];
let expiry = monthlyExpiry(match[2], match[3], script == "FINNIFTY" ? 2 : 4);
return {
script,
exp: match[2] + match[3],
expiry,
strike: parseFloat(match[4]),
type: match[5],
};
}
// OPT - Weekly Expiry
match = symbol.match(/^(NIFTY|BANKNIFTY|FINNIFTY)(\d{2})(\w{1})(\d{2})([\d.]+)(PE|CE)$/);
if (match) {
let script = match[1];
let expiry = weeklyExpiry(match[2], match[3], match[4]);
return {
script,
exp: match[2] + match[3] + match[4],
expiry,
strike: parseFloat(match[5]),
type: match[6],
};
}
// MF & EQ
return { script: symbol };
}
function fo(name) {
// FUT - Monthly Expiry (only)
let match = name.match(/^(\S+?)(\d{2})([A-Z]{3})FUT$/);
if (match) {
let scrip = match[1];
let expiry = monthlyExpiry(match[2], match[3], scrip == "FINNIFTY" ? 2 : 4);
return { symbol: scrip, scrip, exp: match[2] + match[3], expiry, type: "FUT" };
}
// OPT - Monthly Expiry
match = name.match(/^(\S+?)(\d{2})([A-Z]{3})([\d.]+)(PE|CE)$/);
if (match) {
let scrip = match[1];
let expiry = monthlyExpiry(match[2], match[3], scrip == "FINNIFTY" ? 2 : 4);
return {
symbol: scrip,
scrip,
exp: match[2] + match[3],
expiry,
strike: parseFloat(match[4]),
type: match[5],
};
}
// OPT - Weekly Expiry
match = name.match(/^(NIFTY|BANKNIFTY|FINNIFTY)(\d{2})(\w{1})(\d{2})([\d.]+)(PE|CE)$/);
if (match) {
let scrip = match[1];
let expiry = weeklyExpiry(match[2], match[3], match[4]);
return {
symbol: scrip,
scrip,
exp: match[2] + match[3] + match[4],
expiry,
strike: parseFloat(match[5]),
type: match[6],
};
}
return null;
}
function hasOpened() {
console.warn(".hasOpened() is deprecated. Shall be removed eventually.");
let date = new Date();
if (isHoliday(date)) return false;
let [day, hrs] = _istDayAndHr(date);
if (day == specialday) return hrs >= 18;
else return hrs >= 9;
}
function hasClosed() {
console.warn(".hasClosed() is deprecated. Shall be removed eventually.");
let date = new Date();
if (isHoliday(date)) return false;
let [day, hrs] = _istDayAndHr(date);
if (day === specialday) return hrs >= 19.25;
else return hrs >= 15.5;
}
export default { eq, info, fo, isOpen, hasOpened, hasClosed, isHoliday };
export { isHoliday as isISMHoliday, isOpen as isISMOpen };