SPB Git forge

spb/market-atlas

Public
12commits 1branches 0releases
1.1 MBsize
maindefault branch
10 days agolast push
TypeScript 96.7% SQL 1.6% CSS 0.8% JavaScript 0.5%
3.6 KB · 55 lines typescript
Raw Blame History
1import { describe, expect, it } from "vitest";2import type { Exchange } from "@market-atlas/market-model";3import { MarketCalendar } from "./calendar.js";45const xnys: Exchange = { id: "xnys", mic: "XNYS", name: "NYSE", operator: null, country: "US", city: null, timezone: "America/New_York", currency: "USD", website: null, lat: null, lon: null, assetClasses: ["EQUITY"], sessions: { regular: [{ open: "09:30", close: "16:00" }], pre: { open: "04:00", close: "09:30" }, post: { open: "16:00", close: "20:00" } } };6const xtks: Exchange = { ...xnys, id: "xtks", timezone: "Asia/Tokyo", sessions: { regular: [{ open: "09:00", close: "11:30" }, { open: "12:30", close: "15:30" }] } };7const xsau: Exchange = { ...xnys, id: "xsau", timezone: "Asia/Riyadh", sessions: { regular: [{ open: "10:00", close: "15:00" }], weekdays: [0, 1, 2, 3, 4] } };8const coinbase: Exchange = { ...xnys, id: "coinbase", timezone: "UTC", sessions: { regular: [], continuous: true } };910const cal = new MarketCalendar();11cal.load([xnys, xtks, xsau, coinbase], [12  { exchangeId: "xnys", date: "2026-09-07", name: "Labor Day", kind: "CLOSED", sourceId: "t" },13  { exchangeId: "xnys", date: "2026-11-27", name: "Early close", kind: "EARLY_CLOSE", closeTime: "13:00", sourceId: "t" },14]);1516const at = (iso: string) => Date.parse(iso);1718describe("market calendar", () => {19  it("NYSE regular, pre, post, closed and weekend", () => {20    expect(cal.status("xnys", at("2026-09-11T14:00:00Z")).state).toBe("OPEN"); // 10:00 EDT Friday21    expect(cal.status("xnys", at("2026-09-11T12:00:00Z")).state).toBe("PRE"); // 08:00 EDT22    expect(cal.status("xnys", at("2026-09-11T21:00:00Z")).state).toBe("POST"); // 17:00 EDT23    expect(cal.status("xnys", at("2026-09-12T14:00:00Z")).state).toBe("CLOSED"); // Saturday24    expect(cal.status("xnys", at("2026-09-11T02:00:00Z")).state).toBe("CLOSED"); // 22:00 EDT Thursday25  });26  it("holidays and early closes", () => {27    const labor = cal.status("xnys", at("2026-09-07T14:00:00Z"));28    expect(labor.state).toBe("CLOSED");29    expect(labor.isHoliday).toBe(true);30    expect(labor.holidayName).toBe("Labor Day");31    expect(cal.status("xnys", at("2026-11-27T17:00:00Z")).state).toBe("OPEN"); // 12:00 EST32    expect(cal.status("xnys", at("2026-11-27T18:30:00Z")).state).toBe("CLOSED"); // 13:30 EST after early close33  });34  it("DST: winter session in UTC shifts by one hour", () => {35    expect(cal.status("xnys", at("2026-01-15T14:00:00Z")).state).toBe("PRE"); // 09:00 EST36    expect(cal.status("xnys", at("2026-01-15T14:30:00Z")).state).toBe("OPEN");37  });38  it("Tokyo lunch break and Riyadh Sunday trading", () => {39    expect(cal.status("xtks", at("2026-09-11T01:00:00Z")).state).toBe("OPEN"); // 10:00 JST40    expect(cal.status("xtks", at("2026-09-11T03:00:00Z")).state).toBe("CLOSED"); // 12:00 JST lunch41    expect(cal.status("xtks", at("2026-09-11T04:00:00Z")).state).toBe("OPEN"); // 13:00 JST42    expect(cal.status("xsau", at("2026-09-13T08:00:00Z")).state).toBe("OPEN"); // Sunday 11:00 AST43    expect(cal.status("xsau", at("2026-09-11T08:00:00Z")).state).toBe("CLOSED"); // Friday44  });45  it("continuous venues are always open; next transition computed for NYSE", () => {46    expect(cal.status("coinbase", at("2026-09-12T03:00:00Z")).state).toBe("OPEN");47    const s = cal.status("xnys", at("2026-09-11T14:00:00Z"));48    expect(s.nextTransition?.state).toBe("POST");49    expect(new Date(s.nextTransition!.at).toISOString()).toBe("2026-09-11T20:00:00.000Z");50  });51  it("session start for the trading day", () => {52    expect(new Date(cal.sessionStart("xnys", at("2026-09-11T14:00:00Z"))!).toISOString()).toBe("2026-09-11T08:00:00.000Z"); // pre-market 04:00 EDT53  });54});55