mdas-jsview-sdk 1.0.10-uat.0 → 1.0.12-uat.0

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.
@@ -1199,13 +1199,12 @@ const NightSessionTemplate = `
1199
1199
  <div class="company-market-info">
1200
1200
  <span class="night-company-name"></span>
1201
1201
  <span class="market-name"></span>
1202
- <span class="market-mic"></span>
1203
1202
  </div>
1204
1203
  <h1 class="symbol editable-symbol"
1205
1204
  title="Double-click to edit symbol"
1206
1205
  data-original-symbol="">
1207
1206
  </h1>
1208
-
1207
+
1209
1208
  </div>
1210
1209
  <div class="price-section">
1211
1210
  <div class="current-price"></div>
@@ -4415,13 +4414,6 @@ class NightSessionWidget extends BaseWidget {
4415
4414
  marketNameElement.textContent = this.exchangeName || data.exchangeName || '';
4416
4415
  }
4417
4416
 
4418
- // Update MIC (Market Identifier Code)
4419
- const marketMicElement = this.container.querySelector('.market-mic');
4420
- if (marketMicElement) {
4421
- const micValue = this.mic || data.mic || '';
4422
- marketMicElement.textContent = micValue;
4423
- }
4424
-
4425
4417
  // Update price
4426
4418
  const currentPriceElement = this.container.querySelector('.current-price');
4427
4419
  if (currentPriceElement) {
@@ -37500,6 +37492,10 @@ class IntradayChartWidget extends BaseWidget {
37500
37492
 
37501
37493
  // Symbol editor
37502
37494
  this.symbolEditor = null;
37495
+
37496
+ // Lazy loading cache for 5D data
37497
+ this.cached5DData = null;
37498
+ this.is5DDataLoading = false;
37503
37499
  this.createWidgetStructure();
37504
37500
  this.setupSymbolEditor();
37505
37501
  this.initialize();
@@ -37601,6 +37597,10 @@ class IntradayChartWidget extends BaseWidget {
37601
37597
  this.chartData = null;
37602
37598
  this.livePrice = null;
37603
37599
 
37600
+ // Clear cached 5D data for old symbol
37601
+ this.cached5DData = null;
37602
+ this.is5DDataLoading = false;
37603
+
37604
37604
  // Update internal symbol
37605
37605
  this.symbol = upperSymbol;
37606
37606
 
@@ -37751,7 +37751,13 @@ class IntradayChartWidget extends BaseWidget {
37751
37751
  throw new Error('API service not available');
37752
37752
  }
37753
37753
 
37754
- // FALLBACK LOGIC: Try to find most recent available data
37754
+ // For 5D chart: Load data from multiple days (rangeback 0-5)
37755
+ if (this.rangeBack === 5) {
37756
+ await this.load5DayChartData(apiService);
37757
+ return;
37758
+ }
37759
+
37760
+ // FALLBACK LOGIC: Try to find most recent available data (for 1D)
37755
37761
  const MAX_LOOKBACK_DAYS = 7;
37756
37762
  let attemptRangeBack = this.rangeBack;
37757
37763
  let dataFound = false;
@@ -37840,6 +37846,9 @@ class IntradayChartWidget extends BaseWidget {
37840
37846
  await this.startAutoRefresh();
37841
37847
  // Subscribe to live price updates
37842
37848
  this.subscribeToLivePrice();
37849
+
37850
+ // LAZY LOADING: Preload 5D data in the background after 1D loads
37851
+ this.preload5DDataInBackground();
37843
37852
  } else {
37844
37853
  // Historical data or fallback - don't refresh
37845
37854
  this.stopAutoRefresh();
@@ -37867,6 +37876,159 @@ class IntradayChartWidget extends BaseWidget {
37867
37876
  this.showError(errorMessage);
37868
37877
  }
37869
37878
  }
37879
+ async load5DayChartData(apiService) {
37880
+ try {
37881
+ // Check if we have cached 5D data
37882
+ if (this.cached5DData) {
37883
+ if (this.debug) {
37884
+ console.log('[IntradayChartWidget] Using cached 5D data');
37885
+ }
37886
+
37887
+ // Use cached data
37888
+ this.chartData = this.cached5DData;
37889
+ this.renderChart();
37890
+ this.updateStats();
37891
+ this.hideLoading();
37892
+
37893
+ // Don't auto-refresh for 5D chart
37894
+ this.stopAutoRefresh();
37895
+ return;
37896
+ }
37897
+
37898
+ // No cached data, load it now
37899
+ const combinedData = await this.fetch5DayData(apiService);
37900
+
37901
+ // Process the combined data
37902
+ this.chartData = new IntradayChartModel(combinedData);
37903
+ if (this.chartData.dataPoints.length === 0) {
37904
+ throw new Error(`No valid data points for ${this.symbol}`);
37905
+ }
37906
+
37907
+ // Cache the processed data
37908
+ this.cached5DData = this.chartData;
37909
+ this.renderChart();
37910
+ this.updateStats();
37911
+ this.hideLoading();
37912
+ if (this.debug) {
37913
+ console.log(`[IntradayChartWidget] 5D chart loaded with ${this.chartData.dataPoints.length} data points`);
37914
+ }
37915
+
37916
+ // Don't auto-refresh for 5D chart
37917
+ this.stopAutoRefresh();
37918
+ } catch (error) {
37919
+ console.error('[IntradayChartWidget] Error loading 5D chart data:', error);
37920
+ throw error; // Re-throw to be caught by parent loadChartData
37921
+ }
37922
+ }
37923
+ async fetch5DayData(apiService) {
37924
+ const DAYS_TO_COLLECT = 5;
37925
+ const MAX_RANGEBACK_ATTEMPTS = 7; // API limit: maximum 7 rangeback
37926
+
37927
+ if (this.debug) {
37928
+ console.log('[IntradayChartWidget] Fetching 5D chart data with parallel requests (API limit: rangeback 0-6)');
37929
+ }
37930
+
37931
+ // OPTIMIZATION: Query all rangeback values in parallel (0-6)
37932
+ const rangebackPromises = [];
37933
+ for (let rb = 0; rb < MAX_RANGEBACK_ATTEMPTS; rb++) {
37934
+ rangebackPromises.push(apiService.getIntradayChart(this.source, this.symbol, rb).then(response => {
37935
+ // Extract data array from response
37936
+ let dataArray = response;
37937
+ if (response && response.data && Array.isArray(response.data)) {
37938
+ dataArray = response.data;
37939
+ }
37940
+ return {
37941
+ rangeBack: rb,
37942
+ data: dataArray
37943
+ };
37944
+ }).catch(error => {
37945
+ // If a request fails, return null so we can filter it out
37946
+ if (this.debug) {
37947
+ console.warn(`[IntradayChartWidget] Request failed for rangeback ${rb}:`, error);
37948
+ }
37949
+ return {
37950
+ rangeBack: rb,
37951
+ data: null
37952
+ };
37953
+ }));
37954
+ }
37955
+
37956
+ // Wait for all parallel requests to complete
37957
+ const results = await Promise.all(rangebackPromises);
37958
+ if (this.debug) {
37959
+ console.log('[IntradayChartWidget] All parallel requests completed');
37960
+ }
37961
+
37962
+ // Filter out empty/null responses and collect valid days
37963
+ const collectedDays = results.filter(result => {
37964
+ const isValid = result.data && Array.isArray(result.data) && result.data.length > 0;
37965
+ if (this.debug) {
37966
+ if (isValid) {
37967
+ console.log(`[IntradayChartWidget] Rangeback ${result.rangeBack}: ${result.data.length} data points`);
37968
+ } else {
37969
+ console.log(`[IntradayChartWidget] Rangeback ${result.rangeBack}: No data (skipped)`);
37970
+ }
37971
+ }
37972
+ return isValid;
37973
+ }).slice(0, DAYS_TO_COLLECT); // Take only the first 5 days with data
37974
+
37975
+ if (collectedDays.length === 0) {
37976
+ throw new Error(`No intraday data available for ${this.symbol} in the past ${MAX_RANGEBACK_ATTEMPTS} days`);
37977
+ }
37978
+ if (this.debug) {
37979
+ console.log(`[IntradayChartWidget] Collected ${collectedDays.length}/${DAYS_TO_COLLECT} days of data`);
37980
+ if (collectedDays.length < DAYS_TO_COLLECT) {
37981
+ console.log(`[IntradayChartWidget] Note: Only ${collectedDays.length} days available within API limit (rangeback 0-6)`);
37982
+ }
37983
+ }
37984
+
37985
+ // Combine all collected days into a single array (oldest to newest)
37986
+ const combinedData = [];
37987
+ for (let i = collectedDays.length - 1; i >= 0; i--) {
37988
+ combinedData.push(...collectedDays[i].data);
37989
+ }
37990
+ if (this.debug) {
37991
+ console.log(`[IntradayChartWidget] Combined ${combinedData.length} total data points from ${collectedDays.length} days`);
37992
+ }
37993
+ return combinedData;
37994
+ }
37995
+ preload5DDataInBackground() {
37996
+ // Prevent multiple simultaneous preloads
37997
+ if (this.is5DDataLoading || this.cached5DData) {
37998
+ return;
37999
+ }
38000
+ this.is5DDataLoading = true;
38001
+ if (this.debug) {
38002
+ console.log('[IntradayChartWidget] Starting background preload of 5D data...');
38003
+ }
38004
+
38005
+ // Run in background without blocking
38006
+ setTimeout(async () => {
38007
+ try {
38008
+ const apiService = this.wsManager.getApiService();
38009
+ if (!apiService) {
38010
+ if (this.debug) {
38011
+ console.log('[IntradayChartWidget] API service not available for preload');
38012
+ }
38013
+ return;
38014
+ }
38015
+ const combinedData = await this.fetch5DayData(apiService);
38016
+
38017
+ // Create and cache the model
38018
+ const chartData = new IntradayChartModel(combinedData);
38019
+ this.cached5DData = chartData;
38020
+ if (this.debug) {
38021
+ console.log(`[IntradayChartWidget] ✓ Background preload complete: ${chartData.dataPoints.length} data points cached`);
38022
+ }
38023
+ } catch (error) {
38024
+ if (this.debug) {
38025
+ console.warn('[IntradayChartWidget] Background preload failed:', error);
38026
+ }
38027
+ } finally {
38028
+ this.is5DDataLoading = false;
38029
+ }
38030
+ }, 1000); // Wait 1 second after 1D loads before starting preload
38031
+ }
37870
38032
  subscribeToLivePrice() {
37871
38033
  // Unsubscribe from previous subscription if any
37872
38034
  if (this.unsubscribe) {
@@ -38189,16 +38351,28 @@ class IntradayChartWidget extends BaseWidget {
38189
38351
 
38190
38352
  if (this.chartType === 'candlestick') {
38191
38353
  // Prepare OHLC data for candlestick chart
38192
- chartDataPoints = validDataPoints.map(point => ({
38193
- x: this.parseTimestamp(point.time).getTime(),
38194
- o: point.open,
38195
- h: point.high,
38196
- l: point.low,
38197
- c: point.close
38198
- })).filter(point => {
38199
- // Filter out invalid timestamps
38200
- return !isNaN(point.x) && point.x > 0;
38201
- });
38354
+ if (this.rangeBack > 0) {
38355
+ // For multi-day charts, use index as x-value (linear scale)
38356
+ chartDataPoints = validDataPoints.map((point, index) => ({
38357
+ x: index,
38358
+ o: point.open,
38359
+ h: point.high,
38360
+ l: point.low,
38361
+ c: point.close
38362
+ }));
38363
+ } else {
38364
+ // For single-day charts, use timestamps (time scale)
38365
+ chartDataPoints = validDataPoints.map(point => ({
38366
+ x: this.parseTimestamp(point.time).getTime(),
38367
+ o: point.open,
38368
+ h: point.high,
38369
+ l: point.low,
38370
+ c: point.close
38371
+ })).filter(point => {
38372
+ // Filter out invalid timestamps
38373
+ return !isNaN(point.x) && point.x > 0;
38374
+ });
38375
+ }
38202
38376
  if (chartDataPoints.length === 0) {
38203
38377
  console.error('[IntradayChartWidget] No valid candlestick data points after date parsing');
38204
38378
  this.showError('Unable to parse chart data timestamps');
@@ -38220,14 +38394,23 @@ class IntradayChartWidget extends BaseWidget {
38220
38394
  };
38221
38395
  } else {
38222
38396
  // Prepare data with timestamps for line/area chart
38223
- chartDataPoints = validDataPoints.map(point => ({
38224
- x: this.parseTimestamp(point.time),
38225
- y: point.close
38226
- })).filter(point => {
38227
- // Filter out invalid dates
38228
- const timestamp = point.x.getTime();
38229
- return !isNaN(timestamp) && timestamp > 0;
38230
- });
38397
+ if (this.rangeBack > 0) {
38398
+ // For multi-day charts, use index as x-value (linear scale)
38399
+ chartDataPoints = validDataPoints.map((point, index) => ({
38400
+ x: index,
38401
+ y: point.close
38402
+ }));
38403
+ } else {
38404
+ // For single-day charts, use timestamps (time scale)
38405
+ chartDataPoints = validDataPoints.map(point => ({
38406
+ x: this.parseTimestamp(point.time),
38407
+ y: point.close
38408
+ })).filter(point => {
38409
+ // Filter out invalid dates
38410
+ const timestamp = point.x.getTime();
38411
+ return !isNaN(timestamp) && timestamp > 0;
38412
+ });
38413
+ }
38231
38414
  if (chartDataPoints.length === 0) {
38232
38415
  console.error('[IntradayChartWidget] No valid chart data points after date parsing');
38233
38416
  this.showError('Unable to parse chart data timestamps');
@@ -38262,16 +38445,23 @@ class IntradayChartWidget extends BaseWidget {
38262
38445
  console.log('[IntradayChartWidget] Last data point - Source time:', validDataPoints[validDataPoints.length - 1].time);
38263
38446
  console.log('[IntradayChartWidget] Last chart point:', chartDataPoints[chartDataPoints.length - 1]);
38264
38447
 
38265
- // Calculate explicit min/max for x-axis to prevent Chart.js from auto-calculating incorrectly
38266
- const timestamps = chartDataPoints.map(p => this.chartType === 'candlestick' ? p.x : p.x.getTime());
38267
- const minTimestamp = Math.min(...timestamps);
38268
- const maxTimestamp = Math.max(...timestamps);
38269
- console.log('[IntradayChartWidget] X-axis bounds:', {
38270
- min: minTimestamp,
38271
- max: maxTimestamp,
38272
- minDate: new Date(minTimestamp),
38273
- maxDate: new Date(maxTimestamp)
38274
- });
38448
+ // Calculate explicit min/max for x-axis (only for time scale, not linear)
38449
+ let minTimestamp, maxTimestamp;
38450
+ if (this.rangeBack === 0) {
38451
+ // For time scale, calculate timestamp bounds
38452
+ const timestamps = chartDataPoints.map(p => this.chartType === 'candlestick' ? p.x : p.x.getTime());
38453
+ minTimestamp = Math.min(...timestamps);
38454
+ maxTimestamp = Math.max(...timestamps);
38455
+ console.log('[IntradayChartWidget] X-axis bounds:', {
38456
+ min: minTimestamp,
38457
+ max: maxTimestamp,
38458
+ minDate: new Date(minTimestamp),
38459
+ maxDate: new Date(maxTimestamp)
38460
+ });
38461
+ } else {
38462
+ // For linear scale, bounds are just indices
38463
+ console.log('[IntradayChartWidget] Using linear scale with', chartDataPoints.length, 'data points');
38464
+ }
38275
38465
  const config = {
38276
38466
  type: this.chartType === 'candlestick' ? 'candlestick' : 'line',
38277
38467
  data: {
@@ -38309,10 +38499,9 @@ class IntradayChartWidget extends BaseWidget {
38309
38499
  const index = context[0].dataIndex;
38310
38500
  const point = validDataPoints[index];
38311
38501
  if (point) {
38312
- // Format timestamp for tooltip
38502
+ // Display timestamp as-is from the data, without timezone conversion
38313
38503
  const date = new Date(point.time);
38314
38504
  return date.toLocaleString('en-US', {
38315
- timeZone: 'America/New_York',
38316
38505
  month: '2-digit',
38317
38506
  day: '2-digit',
38318
38507
  year: 'numeric',
@@ -38363,62 +38552,62 @@ class IntradayChartWidget extends BaseWidget {
38363
38552
  }
38364
38553
  },
38365
38554
  annotation: {
38366
- annotations: {
38367
- livePriceLine: {
38368
- type: 'line',
38369
- scaleID: 'y',
38370
- value: this.livePrice || stats.close,
38371
- borderColor: '#667eea',
38372
- borderWidth: 2,
38373
- borderDash: [5, 5],
38374
- label: {
38375
- display: true,
38376
- content: this.livePrice ? `$${this.livePrice.toFixed(2)}` : `$${stats.close.toFixed(2)}`,
38377
- enabled: true,
38378
- position: 'end',
38379
- backgroundColor: 'rgb(102, 126, 234)',
38380
- color: '#ffffff',
38381
- font: {
38382
- size: 12,
38383
- weight: 'bold',
38384
- family: 'system-ui, -apple-system, sans-serif'
38385
- },
38386
- padding: {
38387
- top: 4,
38388
- bottom: 4,
38389
- left: 8,
38390
- right: 8
38391
- },
38392
- borderRadius: 4,
38393
- xAdjust: -10,
38394
- yAdjust: 0
38395
- }
38396
- }
38397
- }
38555
+ annotations: this.createAnnotations(validDataPoints, stats)
38398
38556
  }
38399
38557
  },
38400
38558
  scales: {
38401
38559
  x: {
38402
- type: 'time',
38403
- min: minTimestamp,
38404
- max: maxTimestamp,
38405
- time: {
38406
- unit: this.rangeBack === 0 ? 'minute' : 'hour',
38560
+ type: this.rangeBack === 0 ? 'time' : 'linear',
38561
+ min: this.rangeBack === 0 ? minTimestamp : undefined,
38562
+ max: this.rangeBack === 0 ? maxTimestamp : undefined,
38563
+ time: this.rangeBack === 0 ? {
38564
+ unit: 'hour',
38565
+ stepSize: 1,
38407
38566
  displayFormats: {
38408
- minute: 'h:mm a',
38409
- hour: 'MMM d, ha'
38567
+ hour: 'h:mm a'
38410
38568
  },
38411
38569
  tooltipFormat: 'MMM d, h:mm a'
38412
- },
38570
+ } : undefined,
38413
38571
  grid: {
38414
38572
  display: false
38415
38573
  },
38416
38574
  ticks: {
38417
- maxTicksLimit: 10,
38575
+ maxTicksLimit: this.rangeBack === 0 ? 10 : 8,
38418
38576
  color: '#6b7280',
38419
38577
  autoSkip: true,
38420
38578
  maxRotation: 0,
38421
- minRotation: 0
38579
+ minRotation: 0,
38580
+ callback: (value, index, ticks) => {
38581
+ if (this.rangeBack > 0) {
38582
+ // For multi-day charts with linear scale
38583
+ // value is the x-value (index in data array), not the tick index
38584
+ const dataIndex = Math.round(value);
38585
+ const point = validDataPoints[dataIndex];
38586
+ if (point) {
38587
+ const date = new Date(point.time);
38588
+ return date.toLocaleString('en-US', {
38589
+ month: 'short',
38590
+ day: 'numeric',
38591
+ hour: 'numeric',
38592
+ minute: '2-digit',
38593
+ hour12: true
38594
+ });
38595
+ }
38596
+ return '';
38597
+ }
38598
+ // For single-day time scale, provide fallback formatting
38599
+ // If value is a number (timestamp), format it
38600
+ if (typeof value === 'number') {
38601
+ const date = new Date(value);
38602
+ return date.toLocaleString('en-US', {
38603
+ hour: 'numeric',
38604
+ minute: '2-digit',
38605
+ hour12: true
38606
+ });
38607
+ }
38608
+ // Otherwise let Chart.js handle it (if date adapter is working)
38609
+ return value;
38610
+ }
38422
38611
  }
38423
38612
  },
38424
38613
  y: {
@@ -38464,6 +38653,63 @@ class IntradayChartWidget extends BaseWidget {
38464
38653
  // Add reset zoom button listener
38465
38654
  this.setupZoomReset();
38466
38655
  }
38656
+ createAnnotations(dataPoints, stats) {
38657
+ const annotations = {
38658
+ livePriceLine: {
38659
+ type: 'line',
38660
+ scaleID: 'y',
38661
+ value: this.livePrice || stats.close,
38662
+ borderColor: '#667eea',
38663
+ borderWidth: 2,
38664
+ borderDash: [5, 5],
38665
+ label: {
38666
+ display: true,
38667
+ content: this.livePrice ? `$${this.livePrice.toFixed(2)}` : `$${stats.close.toFixed(2)}`,
38668
+ enabled: true,
38669
+ position: 'end',
38670
+ backgroundColor: 'rgb(102, 126, 234)',
38671
+ color: '#ffffff',
38672
+ font: {
38673
+ size: 12,
38674
+ weight: 'bold',
38675
+ family: 'system-ui, -apple-system, sans-serif'
38676
+ },
38677
+ padding: {
38678
+ top: 4,
38679
+ bottom: 4,
38680
+ left: 8,
38681
+ right: 8
38682
+ },
38683
+ borderRadius: 4,
38684
+ xAdjust: -10,
38685
+ yAdjust: 0
38686
+ }
38687
+ }
38688
+ };
38689
+
38690
+ // Add day separators for multi-day charts (5D, etc.)
38691
+ if (this.rangeBack > 0 && dataPoints.length > 0) {
38692
+ let currentDay = null;
38693
+ dataPoints.forEach((point, index) => {
38694
+ const pointDate = new Date(point.time);
38695
+ const day = pointDate.toDateString();
38696
+
38697
+ // Add vertical line at the start of each new day
38698
+ if (currentDay !== day && currentDay !== null) {
38699
+ annotations[`daySeparator${index}`] = {
38700
+ type: 'line',
38701
+ scaleID: 'x',
38702
+ value: index,
38703
+ borderColor: 'rgba(0, 0, 0, 0.1)',
38704
+ borderWidth: 1,
38705
+ borderDash: [3, 3]
38706
+ };
38707
+ }
38708
+ currentDay = day;
38709
+ });
38710
+ }
38711
+ return annotations;
38712
+ }
38467
38713
  setupZoomReset() {
38468
38714
  const resetBtn = this.container.querySelector('.zoom-reset-btn');
38469
38715
  if (resetBtn) {