@discomedia/utils 1.0.18 → 1.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,8 +11,6 @@ import require$$0$1 from 'buffer';
11
11
  import require$$0$4 from 'fs';
12
12
  import require$$1$1 from 'path';
13
13
  import require$$2$1 from 'os';
14
- import { startOfDay, set, endOfDay, add, sub } from 'date-fns';
15
- import { toZonedTime, fromZonedTime, formatInTimeZone } from 'date-fns-tz';
16
14
 
17
15
  var Types = /*#__PURE__*/Object.freeze({
18
16
  __proto__: null
@@ -13479,64 +13477,90 @@ const marketEarlyCloses = {
13479
13477
  },
13480
13478
  };
13481
13479
 
13482
- // market-time.ts - Refactored for better organization and usability
13483
- // ===== CONFIGURATION =====
13484
- /**
13485
- * Market configuration constants
13486
- */
13480
+ // Constants for NY market times (Eastern Time)
13487
13481
  const MARKET_CONFIG = {
13488
- TIMEZONE: 'America/New_York',
13482
+ UTC_OFFSET_STANDARD: -5, // EST
13483
+ UTC_OFFSET_DST: -4, // EDT
13489
13484
  TIMES: {
13490
- EXTENDED_START: { hour: 4, minute: 0 },
13491
13485
  MARKET_OPEN: { hour: 9, minute: 30 },
13492
13486
  MARKET_CLOSE: { hour: 16, minute: 0 },
13493
- EARLY_CLOSE: { hour: 13, minute: 0 },
13494
- EXTENDED_END: { hour: 20, minute: 0 },
13495
- EARLY_EXTENDED_END: { hour: 17, minute: 0 },
13496
- },
13487
+ EARLY_CLOSE: { hour: 13, minute: 0 }},
13497
13488
  };
13498
- // ===== MARKET CALENDAR SERVICE =====
13499
- /**
13500
- * Service for handling market calendar operations (holidays, early closes, market days)
13501
- */
13489
+ // Helper: Get NY offset for a given UTC date (DST rules for US)
13490
+ function getNYOffset(date) {
13491
+ // US DST starts 2nd Sunday in March, ends 1st Sunday in November
13492
+ const year = date.getUTCFullYear();
13493
+ const dstStart = getNthWeekdayOfMonth(year, 3, 0, 2); // March, Sunday, 2nd
13494
+ const dstEnd = getNthWeekdayOfMonth(year, 11, 0, 1); // November, Sunday, 1st
13495
+ const utcTime = date.getTime();
13496
+ if (utcTime >= dstStart.getTime() && utcTime < dstEnd.getTime()) {
13497
+ return MARKET_CONFIG.UTC_OFFSET_DST;
13498
+ }
13499
+ return MARKET_CONFIG.UTC_OFFSET_STANDARD;
13500
+ }
13501
+ // Helper: Get nth weekday of month in UTC
13502
+ function getNthWeekdayOfMonth(year, month, weekday, n) {
13503
+ let count = 0;
13504
+ for (let d = 1; d <= 31; d++) {
13505
+ const date = new Date(Date.UTC(year, month - 1, d));
13506
+ if (date.getUTCMonth() !== month - 1)
13507
+ break;
13508
+ if (date.getUTCDay() === weekday) {
13509
+ count++;
13510
+ if (count === n)
13511
+ return date;
13512
+ }
13513
+ }
13514
+ // fallback: last day of month
13515
+ return new Date(Date.UTC(year, month - 1, 28));
13516
+ }
13517
+ // Helper: Convert UTC date to NY time (returns new Date object)
13518
+ function toNYTime(date) {
13519
+ const offset = getNYOffset(date);
13520
+ // NY offset in hours
13521
+ const utcMillis = date.getTime();
13522
+ const nyMillis = utcMillis + offset * 60 * 60 * 1000;
13523
+ return new Date(nyMillis);
13524
+ }
13525
+ // Helper: Convert NY time to UTC (returns new Date object)
13526
+ function fromNYTime(date) {
13527
+ const offset = getNYOffset(date);
13528
+ const nyMillis = date.getTime();
13529
+ const utcMillis = nyMillis - offset * 60 * 60 * 1000;
13530
+ return new Date(utcMillis);
13531
+ }
13532
+ // Market calendar logic
13502
13533
  class MarketCalendar {
13503
- timezone;
13504
- constructor(timezone = MARKET_CONFIG.TIMEZONE) {
13505
- this.timezone = timezone;
13506
- }
13507
- /**
13508
- * Check if a date is a weekend
13509
- */
13510
13534
  isWeekend(date) {
13511
- const day = date.getDay();
13512
- return day === 0 || day === 6; // Sunday or Saturday
13535
+ const day = toNYTime(date).getUTCDay();
13536
+ return day === 0 || day === 6;
13513
13537
  }
13514
- /**
13515
- * Check if a date is a market holiday
13516
- */
13517
13538
  isHoliday(date) {
13518
- const formattedDate = formatInTimeZone(date, this.timezone, 'yyyy-MM-dd');
13519
- const year = toZonedTime(date, this.timezone).getFullYear();
13539
+ const nyDate = toNYTime(date);
13540
+ const year = nyDate.getUTCFullYear();
13541
+ const month = nyDate.getUTCMonth() + 1;
13542
+ const day = nyDate.getUTCDate();
13543
+ const formattedDate = `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
13520
13544
  const yearHolidays = marketHolidays[year];
13521
13545
  if (!yearHolidays)
13522
13546
  return false;
13523
13547
  return Object.values(yearHolidays).some(holiday => holiday.date === formattedDate);
13524
13548
  }
13525
- /**
13526
- * Check if a date is an early close day
13527
- */
13528
13549
  isEarlyCloseDay(date) {
13529
- const formattedDate = formatInTimeZone(date, this.timezone, 'yyyy-MM-dd');
13530
- const year = toZonedTime(date, this.timezone).getFullYear();
13550
+ const nyDate = toNYTime(date);
13551
+ const year = nyDate.getUTCFullYear();
13552
+ const month = nyDate.getUTCMonth() + 1;
13553
+ const day = nyDate.getUTCDate();
13554
+ const formattedDate = `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
13531
13555
  const yearEarlyCloses = marketEarlyCloses[year];
13532
13556
  return yearEarlyCloses && yearEarlyCloses[formattedDate] !== undefined;
13533
13557
  }
13534
- /**
13535
- * Get the early close time for a date (in minutes from midnight)
13536
- */
13537
13558
  getEarlyCloseTime(date) {
13538
- const formattedDate = formatInTimeZone(date, this.timezone, 'yyyy-MM-dd');
13539
- const year = toZonedTime(date, this.timezone).getFullYear();
13559
+ const nyDate = toNYTime(date);
13560
+ const year = nyDate.getUTCFullYear();
13561
+ const month = nyDate.getUTCMonth() + 1;
13562
+ const day = nyDate.getUTCDate();
13563
+ const formattedDate = `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
13540
13564
  const yearEarlyCloses = marketEarlyCloses[year];
13541
13565
  if (yearEarlyCloses && yearEarlyCloses[formattedDate]) {
13542
13566
  const [hours, minutes] = yearEarlyCloses[formattedDate].time.split(':').map(Number);
@@ -13544,285 +13568,61 @@ class MarketCalendar {
13544
13568
  }
13545
13569
  return null;
13546
13570
  }
13547
- /**
13548
- * Check if a date is a market day (not weekend or holiday)
13549
- */
13550
13571
  isMarketDay(date) {
13551
13572
  return !this.isWeekend(date) && !this.isHoliday(date);
13552
13573
  }
13553
- /**
13554
- * Get the next market day from a given date
13555
- */
13556
13574
  getNextMarketDay(date) {
13557
- let nextDay = add(date, { days: 1 });
13575
+ let nextDay = new Date(date.getTime() + 24 * 60 * 60 * 1000);
13558
13576
  while (!this.isMarketDay(nextDay)) {
13559
- nextDay = add(nextDay, { days: 1 });
13577
+ nextDay = new Date(nextDay.getTime() + 24 * 60 * 60 * 1000);
13560
13578
  }
13561
13579
  return nextDay;
13562
13580
  }
13563
- /**
13564
- * Get the previous market day from a given date
13565
- */
13566
13581
  getPreviousMarketDay(date) {
13567
- let prevDay = sub(date, { days: 1 });
13582
+ let prevDay = new Date(date.getTime() - 24 * 60 * 60 * 1000);
13568
13583
  while (!this.isMarketDay(prevDay)) {
13569
- prevDay = sub(prevDay, { days: 1 });
13584
+ prevDay = new Date(prevDay.getTime() - 24 * 60 * 60 * 1000);
13570
13585
  }
13571
13586
  return prevDay;
13572
13587
  }
13573
13588
  }
13574
- // ===== TIME FORMATTER SERVICE =====
13575
- /**
13576
- * Service for formatting time outputs
13577
- */
13578
- class TimeFormatter {
13579
- timezone;
13580
- constructor(timezone = MARKET_CONFIG.TIMEZONE) {
13581
- this.timezone = timezone;
13582
- }
13583
- /**
13584
- * Format a date based on the output format
13585
- */
13586
- formatDate(date, outputFormat = 'iso') {
13587
- switch (outputFormat) {
13588
- case 'unix-seconds':
13589
- return Math.floor(date.getTime() / 1000);
13590
- case 'unix-ms':
13591
- return date.getTime();
13592
- case 'iso':
13593
- default:
13594
- return formatInTimeZone(date, this.timezone, "yyyy-MM-dd'T'HH:mm:ssXXX");
13595
- }
13596
- }
13597
- /**
13598
- * Get New York timezone offset
13599
- */
13600
- getNYTimeZone(date = new Date()) {
13601
- const dtf = new Intl.DateTimeFormat('en-US', {
13602
- timeZone: this.timezone,
13603
- timeZoneName: 'shortOffset',
13604
- });
13605
- const parts = dtf.formatToParts(date);
13606
- const tz = parts.find(p => p.type === 'timeZoneName')?.value;
13607
- if (!tz) {
13608
- throw new Error('Could not determine New York offset');
13609
- }
13610
- const shortOffset = tz.replace('GMT', '');
13611
- if (shortOffset === '-4') {
13612
- return '-04:00';
13613
- }
13614
- else if (shortOffset === '-5') {
13615
- return '-05:00';
13616
- }
13617
- else {
13618
- throw new Error(`Unexpected timezone offset: ${shortOffset}`);
13619
- }
13620
- }
13621
- /**
13622
- * Get trading date in YYYY-MM-DD format
13623
- */
13624
- getTradingDate(time) {
13625
- let date;
13626
- if (typeof time === 'number') {
13627
- date = new Date(time);
13628
- }
13629
- else if (typeof time === 'string') {
13630
- date = new Date(time);
13631
- }
13632
- else {
13633
- date = time;
13634
- }
13635
- return formatInTimeZone(date, this.timezone, 'yyyy-MM-dd');
13636
- }
13589
+ // Get last full trading date
13590
+ function getLastFullTradingDateImpl(currentDate = new Date()) {
13591
+ const calendar = new MarketCalendar();
13592
+ const nyDate = toNYTime(currentDate);
13593
+ const minutes = nyDate.getUTCHours() * 60 + nyDate.getUTCMinutes();
13594
+ const marketOpenMinutes = MARKET_CONFIG.TIMES.MARKET_OPEN.hour * 60 + MARKET_CONFIG.TIMES.MARKET_OPEN.minute;
13595
+ let marketCloseMinutes = MARKET_CONFIG.TIMES.MARKET_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.MARKET_CLOSE.minute;
13596
+ if (calendar.isEarlyCloseDay(currentDate)) {
13597
+ marketCloseMinutes = MARKET_CONFIG.TIMES.EARLY_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.EARLY_CLOSE.minute;
13598
+ }
13599
+ // If not a market day, or before open, or during market hours, return previous market day's close
13600
+ if (!calendar.isMarketDay(currentDate) || minutes < marketOpenMinutes || (minutes >= marketOpenMinutes && minutes < marketCloseMinutes)) {
13601
+ const prevMarketDay = calendar.getPreviousMarketDay(currentDate);
13602
+ let prevCloseMinutes = MARKET_CONFIG.TIMES.MARKET_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.MARKET_CLOSE.minute;
13603
+ if (calendar.isEarlyCloseDay(prevMarketDay)) {
13604
+ prevCloseMinutes = MARKET_CONFIG.TIMES.EARLY_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.EARLY_CLOSE.minute;
13605
+ }
13606
+ const year = prevMarketDay.getUTCFullYear();
13607
+ const month = prevMarketDay.getUTCMonth();
13608
+ const day = prevMarketDay.getUTCDate();
13609
+ const closeHour = Math.floor(prevCloseMinutes / 60);
13610
+ const closeMinute = prevCloseMinutes % 60;
13611
+ return fromNYTime(new Date(Date.UTC(year, month, day, closeHour, closeMinute, 0, 0)));
13612
+ }
13613
+ // After market close or after extended hours, return today's close
13614
+ const year = nyDate.getUTCFullYear();
13615
+ const month = nyDate.getUTCMonth();
13616
+ const day = nyDate.getUTCDate();
13617
+ const closeHour = Math.floor(marketCloseMinutes / 60);
13618
+ const closeMinute = marketCloseMinutes % 60;
13619
+ return fromNYTime(new Date(Date.UTC(year, month, day, closeHour, closeMinute, 0, 0)));
13637
13620
  }
13638
- // ===== MARKET TIME CALCULATOR =====
13639
13621
  /**
13640
- * Service for core market time calculations
13641
- */
13642
- class MarketTimeCalculator {
13643
- calendar;
13644
- timezone;
13645
- constructor(timezone = MARKET_CONFIG.TIMEZONE) {
13646
- this.timezone = timezone;
13647
- this.calendar = new MarketCalendar(timezone);
13648
- }
13649
- /**
13650
- * Get market open/close times for a date
13651
- */
13652
- getMarketTimes(date) {
13653
- const zonedDate = toZonedTime(date, this.timezone);
13654
- // Market closed on weekends and holidays
13655
- if (!this.calendar.isMarketDay(zonedDate)) {
13656
- return {
13657
- marketOpen: false,
13658
- open: null,
13659
- close: null,
13660
- openExt: null,
13661
- closeExt: null,
13662
- };
13663
- }
13664
- const dayStart = startOfDay(zonedDate);
13665
- // Regular market times
13666
- const open = fromZonedTime(set(dayStart, { hours: MARKET_CONFIG.TIMES.MARKET_OPEN.hour, minutes: MARKET_CONFIG.TIMES.MARKET_OPEN.minute }), this.timezone);
13667
- let close = fromZonedTime(set(dayStart, { hours: MARKET_CONFIG.TIMES.MARKET_CLOSE.hour, minutes: MARKET_CONFIG.TIMES.MARKET_CLOSE.minute }), this.timezone);
13668
- // Extended hours
13669
- const openExt = fromZonedTime(set(dayStart, { hours: MARKET_CONFIG.TIMES.EXTENDED_START.hour, minutes: MARKET_CONFIG.TIMES.EXTENDED_START.minute }), this.timezone);
13670
- let closeExt = fromZonedTime(set(dayStart, { hours: MARKET_CONFIG.TIMES.EXTENDED_END.hour, minutes: MARKET_CONFIG.TIMES.EXTENDED_END.minute }), this.timezone);
13671
- // Handle early close days
13672
- if (this.calendar.isEarlyCloseDay(zonedDate)) {
13673
- close = fromZonedTime(set(dayStart, { hours: MARKET_CONFIG.TIMES.EARLY_CLOSE.hour, minutes: MARKET_CONFIG.TIMES.EARLY_CLOSE.minute }), this.timezone);
13674
- closeExt = fromZonedTime(set(dayStart, { hours: MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.hour, minutes: MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.minute }), this.timezone);
13675
- }
13676
- return {
13677
- marketOpen: true,
13678
- open,
13679
- close,
13680
- openExt,
13681
- closeExt,
13682
- };
13683
- }
13684
- /**
13685
- * Check if a time is within market hours based on intraday reporting mode
13686
- */
13687
- isWithinMarketHours(date, intradayReporting = 'market_hours') {
13688
- const zonedDate = toZonedTime(date, this.timezone);
13689
- // Not a market day
13690
- if (!this.calendar.isMarketDay(zonedDate)) {
13691
- return false;
13692
- }
13693
- const timeInMinutes = zonedDate.getHours() * 60 + zonedDate.getMinutes();
13694
- switch (intradayReporting) {
13695
- case 'extended_hours': {
13696
- const startMinutes = MARKET_CONFIG.TIMES.EXTENDED_START.hour * 60 + MARKET_CONFIG.TIMES.EXTENDED_START.minute;
13697
- let endMinutes = MARKET_CONFIG.TIMES.EXTENDED_END.hour * 60 + MARKET_CONFIG.TIMES.EXTENDED_END.minute;
13698
- // Handle early close
13699
- if (this.calendar.isEarlyCloseDay(zonedDate)) {
13700
- endMinutes = MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.hour * 60 + MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.minute;
13701
- }
13702
- return timeInMinutes >= startMinutes && timeInMinutes <= endMinutes;
13703
- }
13704
- case 'continuous':
13705
- return true;
13706
- default: {
13707
- // 'market_hours'
13708
- const startMinutes = MARKET_CONFIG.TIMES.MARKET_OPEN.hour * 60 + MARKET_CONFIG.TIMES.MARKET_OPEN.minute;
13709
- let endMinutes = MARKET_CONFIG.TIMES.MARKET_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.MARKET_CLOSE.minute;
13710
- // Handle early close
13711
- if (this.calendar.isEarlyCloseDay(zonedDate)) {
13712
- endMinutes = MARKET_CONFIG.TIMES.EARLY_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.EARLY_CLOSE.minute;
13713
- }
13714
- return timeInMinutes >= startMinutes && timeInMinutes <= endMinutes;
13715
- }
13716
- }
13717
- }
13718
- /**
13719
- * Get the last full trading date
13720
- */
13721
- getLastFullTradingDate(currentDate = new Date(), extendedHours = true) {
13722
- const nowET = toZonedTime(currentDate, this.timezone);
13723
- if (this.calendar.isMarketDay(nowET)) {
13724
- const timeInMinutes = nowET.getHours() * 60 + nowET.getMinutes();
13725
- let endMinutes;
13726
- if (extendedHours) {
13727
- endMinutes = MARKET_CONFIG.TIMES.EXTENDED_END.hour * 60 + MARKET_CONFIG.TIMES.EXTENDED_END.minute;
13728
- if (this.calendar.isEarlyCloseDay(nowET)) {
13729
- endMinutes = MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.hour * 60 + MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.minute;
13730
- }
13731
- }
13732
- else {
13733
- endMinutes = MARKET_CONFIG.TIMES.MARKET_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.MARKET_CLOSE.minute;
13734
- if (this.calendar.isEarlyCloseDay(nowET)) {
13735
- endMinutes = MARKET_CONFIG.TIMES.EARLY_CLOSE.hour * 60 + MARKET_CONFIG.TIMES.EARLY_CLOSE.minute;
13736
- }
13737
- }
13738
- if (timeInMinutes >= endMinutes) {
13739
- return fromZonedTime(set(nowET, { hours: 0, minutes: 0, seconds: 0, milliseconds: 0 }), this.timezone);
13740
- }
13741
- }
13742
- // Return the last completed trading day
13743
- const lastMarketDay = this.calendar.getPreviousMarketDay(nowET);
13744
- return fromZonedTime(set(lastMarketDay, { hours: 0, minutes: 0, seconds: 0, milliseconds: 0 }), this.timezone);
13745
- }
13746
- /**
13747
- * Get day boundaries based on intraday reporting mode
13748
- */
13749
- getDayBoundaries(date, intradayReporting = 'market_hours') {
13750
- const zonedDate = toZonedTime(date, this.timezone);
13751
- let start;
13752
- let end;
13753
- switch (intradayReporting) {
13754
- case 'extended_hours': {
13755
- start = set(zonedDate, {
13756
- hours: MARKET_CONFIG.TIMES.EXTENDED_START.hour,
13757
- minutes: MARKET_CONFIG.TIMES.EXTENDED_START.minute,
13758
- seconds: 0,
13759
- milliseconds: 0,
13760
- });
13761
- end = set(zonedDate, {
13762
- hours: MARKET_CONFIG.TIMES.EXTENDED_END.hour,
13763
- minutes: MARKET_CONFIG.TIMES.EXTENDED_END.minute,
13764
- seconds: 59,
13765
- milliseconds: 999,
13766
- });
13767
- // Handle early close
13768
- if (this.calendar.isEarlyCloseDay(zonedDate)) {
13769
- end = set(zonedDate, {
13770
- hours: MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.hour,
13771
- minutes: MARKET_CONFIG.TIMES.EARLY_EXTENDED_END.minute,
13772
- seconds: 59,
13773
- milliseconds: 999,
13774
- });
13775
- }
13776
- break;
13777
- }
13778
- case 'continuous': {
13779
- start = startOfDay(zonedDate);
13780
- end = endOfDay(zonedDate);
13781
- break;
13782
- }
13783
- default: {
13784
- // 'market_hours'
13785
- start = set(zonedDate, {
13786
- hours: MARKET_CONFIG.TIMES.MARKET_OPEN.hour,
13787
- minutes: MARKET_CONFIG.TIMES.MARKET_OPEN.minute,
13788
- seconds: 0,
13789
- milliseconds: 0,
13790
- });
13791
- end = set(zonedDate, {
13792
- hours: MARKET_CONFIG.TIMES.MARKET_CLOSE.hour,
13793
- minutes: MARKET_CONFIG.TIMES.MARKET_CLOSE.minute,
13794
- seconds: 59,
13795
- milliseconds: 999,
13796
- });
13797
- // Handle early close
13798
- if (this.calendar.isEarlyCloseDay(zonedDate)) {
13799
- end = set(zonedDate, {
13800
- hours: MARKET_CONFIG.TIMES.EARLY_CLOSE.hour,
13801
- minutes: MARKET_CONFIG.TIMES.EARLY_CLOSE.minute,
13802
- seconds: 59,
13803
- milliseconds: 999,
13804
- });
13805
- }
13806
- break;
13807
- }
13808
- }
13809
- return {
13810
- start: fromZonedTime(start, this.timezone),
13811
- end: fromZonedTime(end, this.timezone),
13812
- };
13813
- }
13814
- }
13815
- const marketTimeCalculator = new MarketTimeCalculator();
13816
- const timeFormatter = new TimeFormatter();
13817
- /**
13818
- * Get the last full trading date
13622
+ * Returns the last full trading date as a Date object.
13819
13623
  */
13820
13624
  function getLastFullTradingDate(currentDate = new Date()) {
13821
- const date = marketTimeCalculator.getLastFullTradingDate(currentDate);
13822
- return {
13823
- date,
13824
- YYYYMMDD: timeFormatter.getTradingDate(date),
13825
- };
13625
+ return getLastFullTradingDateImpl(currentDate);
13826
13626
  }
13827
13627
 
13828
13628
  const log = (message, options = { type: 'info' }) => {
@@ -14222,8 +14022,8 @@ class AlpacaMarketDataAPI extends EventEmitter {
14222
14022
  const response = await this.getHistoricalBars({
14223
14023
  symbols: [symbol],
14224
14024
  timeframe: '1Day',
14225
- start: prevMarketDate.date.toISOString(),
14226
- end: prevMarketDate.date.toISOString(),
14025
+ start: prevMarketDate.toISOString(),
14026
+ end: prevMarketDate.toISOString(),
14227
14027
  limit: 1,
14228
14028
  });
14229
14029
  if (!response.bars[symbol] || response.bars[symbol].length === 0) {